void KisPerspectiveTransformWorker::runPartialDst(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev, const QRect &dstRect) { if (m_isIdentity) { KisPainter::copyAreaOptimizedOldData(dstRect.topLeft(), srcDev, dstDev, dstRect); return; } QRectF srcClipRect = srcDev->exactBounds(); if (srcClipRect.isEmpty()) return; KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height()); KisRandomSubAccessorSP srcAcc = srcDev->createRandomSubAccessor(); KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG(dstRect.x(), dstRect.y()); for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); ++y) { for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); ++x) { QPointF dstPoint(x, y); QPointF srcPoint = m_backwardTransform.map(dstPoint); if (srcClipRect.contains(srcPoint)) { accessor->moveTo(dstPoint.x(), dstPoint.y()); srcAcc->moveTo(srcPoint.x(), srcPoint.y()); srcAcc->sampledOldRawData(accessor->rawData()); } } progressHelper.step(); } }
KisScanlineFill::KisScanlineFill(KisPaintDeviceSP device, const QPoint &startPoint, const QRect &boundingRect) : m_d(new Private) { m_d->device = device; m_d->it = device->createRandomAccessorNG(startPoint.x(), startPoint.y()); m_d->startPoint = startPoint; m_d->boundingRect = boundingRect; m_d->rowIncrement = 1; m_d->threshold = 0; }
void ChalkBrush::paint(KisPaintDeviceSP dev, qreal x, qreal y, const KoColor &color, qreal additionalScale) { m_inkColor = color; m_counter++; qint32 pixelSize = dev->colorSpace()->pixelSize(); KisRandomAccessorSP accessor = dev->createRandomAccessorNG((int)x, (int)y); qreal result; if (m_properties->inkDepletion) { //count decrementing of saturation and opacity result = log((qreal)m_counter); result = -(result * 10) / 100.0; if (m_properties->useSaturation) { if (m_transfo) { m_transfo->setParameter(m_saturationId, 1.0f + result); m_transfo->transform(m_inkColor.data(), m_inkColor.data(), 1); } } if (m_properties->useOpacity) { qreal opacity = (1.0f + result); m_inkColor.setOpacity(opacity); } } int pixelX, pixelY; const int radius = m_properties->radius * additionalScale; const int radiusSquared = pow2(radius); double dirtThreshold = 0.5; for (int by = -radius; by <= radius; by++) { int bySquared = by * by; for (int bx = -radius; bx <= radius; bx++) { // let's call that noise from ground to chalk :) if (((bx * bx + bySquared) > radiusSquared) || m_randomSource.generateNormalized() < dirtThreshold) { continue; } pixelX = qRound(x + bx); pixelY = qRound(y + by); accessor->moveTo(pixelX, pixelY); memcpy(accessor->rawData(), m_inkColor.data(), pixelSize); } } }
void testWrappedLineIteratorReadMoreThanBounds(QString testName) { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KisPaintDeviceSP dst = new KisPaintDevice(cs); // fill device with a gradient QRect bounds = dev->defaultBounds()->bounds(); for (int y = bounds.y(); y < bounds.y() + bounds.height(); y++) { for (int x = bounds.x(); x < bounds.x() + bounds.width(); x++) { QColor c((10 * x) % 255, (10 * y) % 255, 0, 255); dev->setPixel(x, y, c); } } // test rect doesn't fit the wrap rect in both dimentions const QRect &rect(bounds.adjusted(-6,-6,8,8)); KisRandomAccessorSP dstIt = dst->createRandomAccessorNG(rect.x(), rect.y()); IteratorSP it = createIterator<IteratorSP>(dev, rect); for (int y = rect.y(); y < rect.y() + rect.height(); y++) { for (int x = rect.x(); x < rect.x() + rect.width(); x++) { quint8 *data = it->rawData(); QVERIFY(checkConseqPixels<IteratorSP>(it->nConseqPixels(), QPoint(x, y), KisWrappedRect(rect, bounds))); dstIt->moveTo(x, y); memcpy(dstIt->rawData(), data, cs->pixelSize()); QVERIFY(checkXY<IteratorSP>(QPoint(it->x(), it->y()), QPoint(x,y))); bool stepDone = it->nextPixel(); QCOMPARE(stepDone, x < rect.x() + rect.width() - 1); } if (!nextRowGeneral(it, y, rect)) break; } testName = QString("%1_%2_%3_%4_%5") .arg(testName) .arg(rect.x()) .arg(rect.y()) .arg(rect.width()) .arg(rect.height()); QRect rc = rect; QImage result = dst->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QImage ref = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()); QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators_huge", testName)); }
/* 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()); }
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 SprayBrush::paintOutline(KisPaintDeviceSP dev , const KoColor &outlineColor, qreal posX, qreal posY, qreal radius) { QList<QPointF> antiPixels; KisRandomAccessorSP accessor = dev->createRandomAccessorNG(qRound(posX), qRound(posY)); for (int y = -radius + posY; y <= radius + posY; y++) { for (int x = -radius + posX; x <= radius + posX; x++) { accessor->moveTo(x, y); qreal alpha = dev->colorSpace()->opacityU8(accessor->rawData()); if (alpha != 0) { // top left accessor->moveTo(x - 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y - 1)); //continue; } // top accessor->moveTo(x, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y - 1)); //continue; } // top right accessor->moveTo(x + 1, y - 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y - 1)); //continue; } //left accessor->moveTo(x - 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y)); //continue; } //right accessor->moveTo(x + 1, y); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y)); //continue; } // bottom left accessor->moveTo(x - 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x - 1, y + 1)); //continue; } // bottom accessor->moveTo(x, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x, y + 1)); //continue; } // bottom right accessor->moveTo(x + 1, y + 1); if (dev->colorSpace()->opacityU8(accessor->rawData()) == 0) { antiPixels.append(QPointF(x + 1, y + 1)); //continue; } } } } // anti-alias it int size = antiPixels.size(); for (int i = 0; i < size; i++) { accessor->moveTo(antiPixels[i].x(), antiPixels[i].y()); memcpy(accessor->rawData(), outlineColor.data(), dev->colorSpace()->pixelSize()); } }
void KisGmicSimpleConvertor::convertFromGmicImage(gmic_image<float>& gmicImage, KisPaintDeviceSP dst, float gmicMaxChannelValue) { Q_ASSERT(!dst.isNull()); const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->rgb8()->profile()); const KoColorSpace *dstColorSpace = dst->colorSpace(); if (dstColorSpace == 0) { dstColorSpace = rgbaFloat32bitcolorSpace; } KisPaintDeviceSP dev = dst; int greenOffset = gmicImage._width * gmicImage._height; int blueOffset = greenOffset * 2; int alphaOffset = greenOffset * 3; QRect rc(0,0,gmicImage._width, gmicImage._height); KisRandomAccessorSP it = dev->createRandomAccessorNG(0,0); int pos; float r,g,b,a; int optimalBufferSize = 64; // most common numContiguousColumns, tile size? quint8 * floatRGBApixel = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize]; quint32 pixelSize = rgbaFloat32bitcolorSpace->pixelSize(); KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent(); KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags(); // Krita needs rgba in 0.0...1.0 float multiplied = KoColorSpaceMathsTraits<float>::unitValue / gmicMaxChannelValue; switch (gmicImage._spectrum) { case 1: { // convert grayscale to rgba 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); pos = y * gmicImage._width + x; for (qint32 bx = 0; bx < numContiguousColumns; bx++) { r = g = b = gmicImage._data[pos] * multiplied; a = KoColorSpaceMathsTraits<float>::unitValue; memcpy(floatRGBApixel + bx * pixelSize, &r,4); memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4); memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4); memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4); pos++; } rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags); x += numContiguousColumns; } } break; } case 2: { // convert grayscale alpha to rgba 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); pos = y * gmicImage._width + x; for (qint32 bx = 0; bx < numContiguousColumns; bx++) { r = g = b = gmicImage._data[pos] * multiplied; a = gmicImage._data[pos + greenOffset] * multiplied; memcpy(floatRGBApixel + bx * pixelSize, &r,4); memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4); memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4); memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4); pos++; } rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags); x += numContiguousColumns; } } break; } case 3: { // convert rgb -> rgba 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); pos = y * gmicImage._width + x; for (qint32 bx = 0; bx < numContiguousColumns; bx++) { r = gmicImage._data[pos] * multiplied; g = gmicImage._data[pos + greenOffset] * multiplied; b = gmicImage._data[pos + blueOffset ] * multiplied; a = gmicMaxChannelValue * multiplied; memcpy(floatRGBApixel + bx * pixelSize, &r,4); memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4); memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4); memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4); pos++; } rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags); x += numContiguousColumns; } } break; } case 4: { 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); pos = y * gmicImage._width + x; for (qint32 bx = 0; bx < numContiguousColumns; bx++) { r = gmicImage._data[pos] * multiplied; g = gmicImage._data[pos + greenOffset] * multiplied; b = gmicImage._data[pos + blueOffset ] * multiplied; a = gmicImage._data[pos + alphaOffset] * multiplied; memcpy(floatRGBApixel + bx * pixelSize, &r,4); memcpy(floatRGBApixel + bx * pixelSize + 4, &g,4); memcpy(floatRGBApixel + bx * pixelSize + 8, &b,4); memcpy(floatRGBApixel + bx * pixelSize + 12, &a,4); pos++; } rgbaFloat32bitcolorSpace->convertPixelsTo(floatRGBApixel, it->rawData(), dstColorSpace, numContiguousColumns,renderingIntent, conversionFlags); x += numContiguousColumns; } } break; } default: { dbgPlugins << "Unsupported gmic output format : " << gmicImage._width << gmicImage._height << gmicImage._depth << gmicImage._spectrum; } } }
void KisGmicSimpleConvertor::convertFromGmicFast(gmic_image<float>& gmicImage, KisPaintDeviceSP dst, float gmicUnitValue) { const KoColorSpace * dstColorSpace = dst->colorSpace(); KoColorTransformation * gmicToDstPixelFormat = createTransformationFromGmic(dstColorSpace,gmicImage._spectrum,gmicUnitValue); if (gmicToDstPixelFormat == 0) { dbgPlugins << "Fall-back to slow color conversion"; convertFromGmicImage(gmicImage, dst, gmicUnitValue); return; } qint32 x = 0; qint32 y = 0; qint32 width = gmicImage._width; qint32 height = gmicImage._height; width = width < 0 ? 0 : width; height = height < 0 ? 0 : height; const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Float32BitsColorDepthID.id(), KoColorSpaceRegistry::instance()->rgb8()->profile()); // this function always convert to rgba or rgb with various color depth quint32 dstNumChannels = rgbaFloat32bitcolorSpace->channelCount(); // number of channels that we will copy quint32 numChannels = gmicImage._spectrum; // gmic image has 4, 3, 2, 1 channel QVector<float *> planes(dstNumChannels); int channelOffset = gmicImage._width * gmicImage._height; for (unsigned int channelIndex = 0; channelIndex < gmicImage._spectrum; channelIndex++) { planes[channelIndex] = gmicImage._data + channelOffset * channelIndex; } for (unsigned int channelIndex = gmicImage._spectrum; channelIndex < dstNumChannels; channelIndex++) { planes[channelIndex] = 0; //turn off } qint32 dataY = 0; qint32 imageY = y; qint32 rowsRemaining = height; const qint32 floatPixelSize = rgbaFloat32bitcolorSpace->pixelSize(); KisRandomAccessorSP it = dst->createRandomAccessorNG(dst->x(), dst->y()); // 0,0 int tileWidth = it->numContiguousColumns(dst->x()); int tileHeight = it->numContiguousRows(dst->y()); Q_ASSERT(tileWidth == 64); Q_ASSERT(tileHeight == 64); quint8 * convertedTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight]; // grayscale and rgb case does not have alpha, so let's fill 4th channel of rgba tile with opacity opaque if (gmicImage._spectrum == 1 || gmicImage._spectrum == 3) { quint32 nPixels = tileWidth * tileHeight; quint32 pixelIndex = 0; KoRgbF32Traits::Pixel* srcPixel = reinterpret_cast<KoRgbF32Traits::Pixel*>(convertedTile); while (pixelIndex < nPixels) { srcPixel->alpha = gmicUnitValue; ++srcPixel; ++pixelIndex; } } while (rowsRemaining > 0) { qint32 dataX = 0; qint32 imageX = x; qint32 columnsRemaining = width; qint32 numContiguousImageRows = it->numContiguousRows(imageY); qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousImageColumns = it->numContiguousColumns(imageX); qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining); const qint32 dataIdx = dataX + dataY * width; const qint32 tileRowStride = (tileWidth - columnsToWork) * floatPixelSize; quint8 *tileItStart = convertedTile; // copy gmic channels to float tile qint32 channelSize = sizeof(float); for(quint32 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(tileIt, planeIt, channelSize); tileIt += floatPixelSize; planeIt += 1; } tileIt += tileRowStride; planeIt += dataStride; } tileItStart += channelSize; } it->moveTo(imageX, imageY); quint8 *dstTileItStart = it->rawData(); tileItStart = convertedTile; // back to the start of the converted tile // copy float tile to dst colorspace based on input colorspace (rgb or grayscale) for (qint32 row = 0; row < rowsToWork; row++) { gmicToDstPixelFormat->transform(tileItStart, dstTileItStart, columnsToWork); dstTileItStart += dstColorSpace->pixelSize() * tileWidth; tileItStart += floatPixelSize * tileWidth; } imageX += columnsToWork; dataX += columnsToWork; columnsRemaining -= columnsToWork; } imageY += rowsToWork; dataY += rowsToWork; rowsRemaining -= rowsToWork; } delete [] convertedTile; delete gmicToDstPixelFormat; }
SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) { return device->createRandomAccessorNG(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); }
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); }
void HairyBrush::paintLine(KisPaintDeviceSP dab, KisPaintDeviceSP layer, const KisPaintInformation &pi1, const KisPaintInformation &pi2, qreal scale, qreal rotation) { m_counter++; qreal x1 = pi1.pos().x(); qreal y1 = pi1.pos().y(); qreal x2 = pi2.pos().x(); qreal y2 = pi2.pos().y(); qreal dx = x2 - x1; qreal dy = y2 - y1; // TODO:this angle is different from the drawing angle in sensor (info.angle()). The bug is caused probably due to // not computing the drag vector properly in paintBezierLine when smoothing is used //qreal angle = atan2(dy, dx); qreal angle = rotation; qreal mousePressure = 1.0; if (m_properties->useMousePressure) { // want pressure from mouse movement qreal distance = sqrt(dx * dx + dy * dy); mousePressure = (1.0 - computeMousePressure(distance)); scale *= mousePressure; } // this pressure controls shear and ink depletion qreal pressure = mousePressure * (pi2.pressure() * 2); Bristle *bristle = 0; KoColor bristleColor(dab->colorSpace()); m_dabAccessor = dab->createRandomAccessorNG((int)x1, (int)y1); m_dab = dab; // initialization block if (firstStroke()) { initAndCache(); } // if this is first time the brush touches the canvas and we use soak the ink from canvas if (firstStroke() && m_properties->useSoakInk) { if (layer) { colorifyBristles(layer, pi1.pos()); } else { dbgKrita << "Can't soak the ink from the layer"; } } KisRandomSourceSP randomSource = pi2.randomSource(); qreal fx1, fy1, fx2, fy2; qreal randomX, randomY; qreal shear; float inkDeplation = 0.0; int inkDepletionSize = m_properties->inkDepletionCurve.size(); int bristleCount = m_bristles.size(); int bristlePathSize; qreal treshold = 1.0 - pi2.pressure(); for (int i = 0; i < bristleCount; i++) { if (!m_bristles.at(i)->enabled()) continue; bristle = m_bristles[i]; randomX = (randomSource->generateNormalized() * 2 - 1.0) * m_properties->randomFactor; randomY = (randomSource->generateNormalized() * 2 - 1.0) * m_properties->randomFactor; shear = pressure * m_properties->shearFactor; m_transform.reset(); m_transform.rotateRadians(-angle); m_transform.scale(scale, scale); m_transform.translate(randomX, randomY); m_transform.shear(shear, shear); if (firstStroke() || (!m_properties->connectedPath)) { // transform start dab m_transform.map(bristle->x(), bristle->y(), &fx1, &fy1); // transform end dab m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2); } else { // continue the path of the bristle from the previous position fx1 = bristle->prevX(); fy1 = bristle->prevY(); m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2); } // remember the end point bristle->setPrevX(fx2); bristle->setPrevY(fy2); // all coords relative to device position fx1 += x1; fy1 += y1; fx2 += x2; fy2 += y2; if (m_properties->threshold && (bristle->length() < treshold)) continue; // paint between first and last dab const QVector<QPointF> bristlePath = m_trajectory.getLinearTrajectory(QPointF(fx1, fy1), QPointF(fx2, fy2), 1.0); bristlePathSize = m_trajectory.size(); memcpy(bristleColor.data(), bristle->color().data() , m_pixelSize); for (int i = 0; i < bristlePathSize ; i++) { if (m_properties->inkDepletionEnabled) { inkDeplation = fetchInkDepletion(bristle, inkDepletionSize); if (m_properties->useSaturation && m_transfo != 0) { saturationDepletion(bristle, bristleColor, pressure, inkDeplation); } if (m_properties->useOpacity) { opacityDepletion(bristle, bristleColor, pressure, inkDeplation); } } else { if (bristleColor.opacityU8() != 0) { bristleColor.setOpacity(bristle->length()); } } addBristleInk(bristle, bristlePath.at(i), bristleColor); bristle->setInkAmount(1.0 - inkDeplation); bristle->upIncrement(); } } m_dab = 0; m_dabAccessor = 0; }