void SprayBrush::paintDistanceMap(KisPaintDeviceSP dev, const KisPaintInformation &info, const KoColor &painterColor){ KisRandomAccessor accessor = dev->createRandomAccessor(0, 0); KoColor color = painterColor; qreal posX = info.pos().x(); qreal posY = info.pos().y(); qreal opacity = 255; for (int y = -m_radius; y <= m_radius; y++){ for (int x = -m_radius; x <= m_radius; x++){ //opacity = sqrt(y*y + x*x) / m_radius; opacity = (y*y + x*x) / (m_radius * m_radius); opacity = 1.0 - opacity; opacity *= m_scale; if (opacity < 0) continue; if (opacity > 1.0) opacity = 1.0; if ( (y*y + x*x) <= (m_radius * m_radius) ) { color.setOpacity( opacity * 255); accessor.moveTo(x + posX, y + posY); memcpy( accessor.rawData(), color.data(), dev->colorSpace()->pixelSize() ); } } } }
void ChalkBrush::paint(KisPaintDeviceSP dev, qreal x, qreal y, const KoColor &color) { m_inkColor = color; m_counter++; qreal decr = (m_counter * m_counter * m_counter) / 1000000.0f; Bristle *bristle; qint32 pixelSize = dev->colorSpace()->pixelSize(); KisRandomAccessor accessor = dev->createRandomAccessor((int)x, (int)y); qreal dirt, result; //count decrementing of saturation and alpha result = log( ( qreal )m_counter); result = -(result * 10) / 100.0; QHash<QString, QVariant> params; params["h"] = 0.0; params["s"] = result; params["v"] = 0.0; KoColorTransformation* transfo = dev->colorSpace()->createColorTransformation("hsv_adjustment", params); transfo->transform(m_inkColor.data(), m_inkColor.data(), 1); int opacity = static_cast<int>((1.0f + result) * 255.0); m_inkColor.setOpacity(opacity); for (int i = 0;i < m_bristles.size();i++) { bristle = &m_bristles[i]; // let's call that noise from ground to chalk :) dirt = drand48(); if (bristle->distanceCenter() > m_radius || dirt < 0.5) { continue; } int dx, dy; dx = (int)(x + bristle->x()); dy = (int)(y + bristle->y()); accessor.moveTo(dx, dy); memcpy(accessor.rawData(), m_inkColor.data(), pixelSize); bristle->setInkAmount(1.0f - decr); } }
void SprayBrush::paint(KisPaintDeviceSP dev, const KisPaintInformation& info, const KoColor &color) { qreal x = info.pos().x(); qreal y = info.pos().y(); // initializing painter KisPainter drawer(dev); drawer.setPaintColor(color); // jitter radius int tmpRadius = m_radius; if (m_jitterSize){ m_radius = m_radius * drand48(); } // jitter movement if (m_jitterMovement){ x = x + (( 2 * m_radius * drand48() ) - m_radius) * m_amount; y = y + (( 2 * m_radius * drand48() ) - m_radius) * m_amount; } KisRandomAccessor accessor = dev->createRandomAccessor( qRound(x), qRound(y) ); m_pixelSize = dev->colorSpace()->pixelSize(); m_inkColor = color; m_counter++; // coverage: adaptively select how many objects are sprayed per paint if (m_useDensity){ m_particlesCount = (m_coverage * (M_PI * m_radius * m_radius) ); } // Metaballs are rendered little differently if (m_shape == 2 && m_object == 0){ paintMetaballs(dev, info, color); } qreal nx, ny; int ix, iy; qreal angle; qreal lengthX; qreal lengthY; for (int i = 0; i < m_particlesCount; i++){ // generate random angle angle = drand48() * M_PI * 2; // different X and Y length?? lengthY = lengthX = drand48(); // I hope we live the era where sin and cos is not slow for spray nx = (sin(angle) * m_radius * lengthX); ny = (cos(angle) * m_radius * lengthY); // transform nx *= m_scale; ny *= m_scale; // it is some shape (circle, ellipse, rectangle) if (m_object == 0) { // steps for single step in circle and ellipse int steps = 36; qreal random = drand48(); drawer.setFillColor(m_inkColor); drawer.setBackgroundColor(m_inkColor); drawer.setPaintColor(m_inkColor); // it is ellipse if (m_shape == 0){ // qreal ellipseA = m_width / 2.0; qreal ellipseB = m_height / 2.0; if (m_width == m_height) { if (m_jitterShapeSize){ paintCircle(drawer, nx + x, ny + y, int((random * ellipseA) + 1.5) , steps); } else{ paintCircle(drawer, nx + x, ny + y, qRound(ellipseA) , steps); } } else { if (m_jitterShapeSize){ paintEllipse(drawer, nx + x, ny + y,int((random * ellipseA) + 1.5) ,int((random * ellipseB) + 1.5), angle , steps); } else{ paintEllipse(drawer, nx + x, ny + y, qRound(ellipseA), qRound(ellipseB), angle , steps); } } } else if (m_shape == 1) { if (m_jitterShapeSize){ paintRectangle(drawer, nx + x, ny + y,int((random * m_width) + 1.5) ,int((random * m_height) + 1.5), angle , steps); } else{ paintRectangle(drawer, nx + x, ny + y, qRound(m_width), qRound(m_height), angle , steps); } } // it is pixel particle }else if (m_object == 1){ paintParticle(accessor,m_inkColor,nx + x, ny + y); } // it is pixel else if (m_object == 2) { ix = qRound(nx + x); iy = qRound(ny + y); accessor.moveTo(ix, iy); memcpy(accessor.rawData(), m_inkColor.data(), m_pixelSize); } } // hidden code for outline detection //m_inkColor.setOpacity(128); //paintOutline(dev,m_inkColor,x, y, m_radius * 2); // recover from jittering of color m_radius = tmpRadius; }
void SprayBrush::paintOutline(KisPaintDeviceSP dev ,const KoColor &outlineColor,qreal posX, qreal posY, qreal radius) { QList<QPointF> antiPixels; KisRandomAccessor accessor = dev->createRandomAccessor( 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()->alpha(accessor.rawData()); if (alpha != 0){ // top left accessor.moveTo(x - 1,y - 1); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x - 1,y - 1) ); //continue; } // top accessor.moveTo(x,y - 1); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x,y - 1) ); //continue; } // top right accessor.moveTo(x + 1,y - 1); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x + 1,y - 1) ); //continue; } //left accessor.moveTo(x - 1,y); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x - 1,y) ); //continue; } //right accessor.moveTo(x + 1,y); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x + 1,y) ); //continue; } // bottom left accessor.moveTo(x - 1,y + 1); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x - 1,y + 1) ); //continue; } // bottom accessor.moveTo(x,y + 1); if ( dev->colorSpace()->alpha(accessor.rawData()) == 0){ antiPixels.append( QPointF(x,y + 1) ); //continue; } // bottom right accessor.moveTo(x + 1,y + 1); if ( dev->colorSpace()->alpha(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 SprayBrush::paintMetaballs(KisPaintDeviceSP dev, const KisPaintInformation &info, const KoColor &painterColor) { // TODO: make adjustable? qreal MIN_TRESHOLD = m_mintresh; qreal MAX_TRESHOLD = m_maxtresh; // dbgPlugins << "MAX " << MAX_TRESHOLD; // dbgPlugins << "MIN " << MIN_TRESHOLD; KoColor color = painterColor; qreal posX = info.pos().x(); qreal posY = info.pos().y(); //int points = m_coverage * (m_radius * m_radius * M_PI); qreal ballRadius = m_width * 0.5; // generate metaballs QList<Metaball> list; for (int i = 0; i < m_particlesCount ; i++){ qreal x = (2 * drand48() * m_radius) - m_radius; qreal y = (2 * drand48() * m_radius) - m_radius; list.append( Metaball( x, y , drand48() * ballRadius) ); } // paint it KisRandomAccessor accessor = dev->createRandomAccessor(0, 0); qreal sum = 0.0; m_computeArea.translate( -qRound(posX), -qRound(posY) ); for (int y = m_computeArea.y(); y <= m_computeArea.height(); y++){ for (int x = m_computeArea.x() ; x <= m_computeArea.width(); x++){ sum = 0.0; for (int i = 0; i < m_particlesCount; i++){ sum += list[i].equation(x, y ); } if (sum >= MIN_TRESHOLD && sum <= MAX_TRESHOLD){ if (sum < 0.0) sum = 0.0; if (sum > 1.0) sum = 1.0; color.setOpacity(OPACITY_OPAQUE * sum); accessor.moveTo( x + posX ,y + posY ); memcpy(accessor.rawData(), color.data(), dev->colorSpace()->pixelSize() ); } } } m_computeArea.translate( qRound(posX), qRound(posY) ); #if 0 KisPainter dabPainter(dev); dabPainter.setFillColor(color); dabPainter.setPaintColor(color); dabPainter.setFillStyle(KisPainter::FillStyleForegroundColor); for (int i = 0; i < m_particlesCount; i++){ qreal x = list[i].x() + posX; qreal y = list[i].y() + posY; dabPainter.paintEllipse(x, y, list[i].radius() * 2,list[i].radius() * 2); } #endif }
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; }