inline void setPixel(KisFixedPaintDeviceSP dev, int x, int y, quint8 alpha) { KoColor c(Qt::black, dev->colorSpace()); c.setOpacity(alpha); dev->fill(x, y, 1, 1, c.data()); }
void HairyBrush::fromDabWithDensity(KisFixedPaintDeviceSP dab, qreal density) { int width = dab->bounds().width(); int height = dab->bounds().height(); int centerX = width * 0.5; int centerY = height * 0.5; // make mask Bristle * bristle = 0; qreal alpha; quint8 * dabPointer = dab->data(); quint8 pixelSize = dab->pixelSize(); const KoColorSpace * cs = dab->colorSpace(); KoColor bristleColor(cs); srand48(12345678); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { alpha = cs->opacityF(dabPointer); if (alpha != 0.0){ if (density == 1.0 || drand48() <= density){ memcpy(bristleColor.data(), dabPointer, pixelSize); bristle = new Bristle(x - centerX, y - centerY, alpha); // using value from image as length of bristle bristle->setColor(bristleColor); m_bristles.append(bristle); } } dabPointer += pixelSize; } } }
void KisPressureSharpnessOption::applyThreshold(KisFixedPaintDeviceSP dab) { if (!isChecked()) return; const KoColorSpace * cs = dab->colorSpace(); // Set all alpha > opaque/2 to opaque, the rest to transparent. // XXX: Using 4/10 as the 1x1 circle brush paints nothing with 0.5. quint8* dabPointer = dab->data(); QRect rc = dab->bounds(); int pixelSize = dab->pixelSize(); int pixelCount = rc.width() * rc.height(); for (int i = 0; i < pixelCount; i++) { quint8 alpha = cs->opacityU8(dabPointer); if (alpha < (m_threshold * OPACITY_OPAQUE_U8) / 100) { cs->setOpacity(dabPointer, OPACITY_TRANSPARENT_U8, 1); } else { cs->setOpacity(dabPointer, OPACITY_OPAQUE_U8, 1); } dabPointer += pixelSize; } }
inline quint8 pixel(KisFixedPaintDeviceSP dev, int x, int y) { KoColor c(Qt::black, dev->colorSpace()); dev->readBytes(c.data(), x, y, 1, 1); return c.opacityU8(); }
void KisFixedPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->convertTo(dstCs); QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height())); QVERIFY(dev->pixelSize() == dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); }
void KisBrushOp::paintAt(const KisPaintInformation& info) { if (!painter()->device()) return; KisBrushSP brush = m_brush; Q_ASSERT(brush); if (!brush) return; KisPaintInformation adjustedInfo = settings->m_optionsWidget->m_sizeOption->apply(info); if (!brush->canPaintFor(adjustedInfo)) return; KisPaintDeviceSP device = painter()->device(); double pScale = KisPaintOp::scaleForPressure(adjustedInfo.pressure()); // TODO: why is there scale and pScale that seems to contains the same things ? QPointF hotSpot = brush->hotSpot(pScale, pScale); QPointF pt = info.pos() - hotSpot; // Split the coordinates into integer plus fractional parts. The integer // is where the dab will be positioned and the fractional part determines // the sub-pixel positioning. qint32 x; double xFraction; qint32 y; double yFraction; splitCoordinate(pt.x(), &x, &xFraction); splitCoordinate(pt.y(), &y, &yFraction); quint8 origOpacity = settings->m_optionsWidget->m_opacityOption->apply(painter(), info.pressure()); KoColor origColor = settings->m_optionsWidget->m_darkenOption->apply(painter(), info.pressure()); double scale = KisPaintOp::scaleForPressure(adjustedInfo.pressure()); KisFixedPaintDeviceSP dab = cachedDab(device->colorSpace()); if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->image(device->colorSpace(), scale, 0.0, adjustedInfo, xFraction, yFraction); } else { KoColor color = painter()->paintColor(); color.convertTo(dab->colorSpace()); brush->mask(dab, color, scale, scale, 0.0, info, xFraction, yFraction); } painter()->bltFixed(QPoint(x, y), dab, dab->bounds()); painter()->setOpacity(origOpacity); painter()->setPaintColor(origColor); }
QImage KisAutoBrush::createBrushPreview() { srand(0); srand48(0); int width = maskWidth(1.0, 0.0, 0.0, 0.0, KisPaintInformation()); int height = maskHeight(1.0, 0.0, 0.0, 0.0, KisPaintInformation()); KisPaintInformation info(QPointF(width * 0.5, height * 0.5), 0.5, 0, 0, 0, 0); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice( KoColorSpaceRegistry::instance()->rgb8() ); fdev->setRect(QRect(0, 0, width, height)); fdev->initialize(); mask(fdev,KoColor(Qt::black, fdev->colorSpace()),1.0, 1.0, 0.0, info); return fdev->convertToQImage(0); }
void KisFixedPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev = new KisFixedPaintDevice(cs); QVERIFY(dev->bounds() == QRect()); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->pixelSize() == cs->pixelSize()); dev->setRect(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); dev->initialize(); QVERIFY(dev->data() != 0); quint8* data = dev->data(); for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) { QVERIFY(data[i] == 0); } }
void KisBidirectionalMixingOption::applyFixed(KisFixedPaintDeviceSP dab, KisPaintDeviceSP device, KisPainter* painter, qint32 sx, qint32 sy, qint32 sw, qint32 sh, quint8 pressure, const QRect& dstRect) { if (!isChecked()) return; KisFixedPaintDevice canvas(device->colorSpace()); canvas.setRect(QRect(dstRect.x(), dstRect.y(), sw, sh)); canvas.initialize(); device->readBytes(canvas.data(), canvas.bounds()); const KoColorSpace* cs = dab->colorSpace(); int channelCount = cs->channelCount(); quint8* dabPointer = dab->data(); quint8* canvasPointer = canvas.data(); QVector<float> cc(channelCount ), dc(channelCount ); for (int y = 0; y < sh; y++) { for (int x = 0; x < sw; x++) { if (cs->alpha(dabPointer) > 10 && cs->alpha(canvasPointer) > 10) { cs->normalisedChannelsValue(canvasPointer, cc); cs->normalisedChannelsValue(dabPointer, dc); for (int i = 0; i < channelCount ; i++) { dc[i] = (1.0 - 0.4 * pressure) * cc[i] + 0.4 * pressure * dc[i]; } cs->fromNormalisedChannelsValue(dabPointer, dc); if (x == (int)(sw / 2) && y == (int)(sh / 2)) painter->setPaintColor(KoColor(dabPointer, cs)); } } dabPointer += dab->pixelSize(); canvasPointer += canvas.pixelSize(); } }
void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX , double subPixelY, qreal softnessFactor) const { Q_UNUSED(info); // Generate the paint device from the mask const KoColorSpace* cs = dst->colorSpace(); quint32 pixelSize = cs->pixelSize(); // mask dimension methods already includes KisBrush::angle() int dstWidth = maskWidth(scaleX, angle, subPixelX, subPixelY, info); int dstHeight = maskHeight(scaleY, angle, subPixelX, subPixelY, info); QPointF hotSpot = this->hotSpot(scaleX, scaleY, angle, info); // mask size and hotSpot function take the KisBrush rotation into account angle += KisBrush::angle(); // if there's coloring information, we merely change the alpha: in that case, // the dab should be big enough! if (coloringInformation) { // old bounds QRect oldBounds = dst->bounds(); // new bounds. we don't care if there is some extra memory occcupied. dst->setRect(QRect(0, 0, dstWidth, dstHeight)); if (dstWidth * dstHeight <= oldBounds.width() * oldBounds.height()) { // just clear the data in dst, memset(dst->data(), OPACITY_TRANSPARENT_U8, dstWidth * dstHeight * dst->pixelSize()); } else { // enlarge the data dst->initialize(); } } else { if (dst->data() == 0 || dst->bounds().isEmpty()) { qWarning() << "Creating a default black dab: no coloring info and no initialized paint device to mask"; dst->clear(QRect(0, 0, dstWidth, dstHeight)); } Q_ASSERT(dst->bounds().width() >= dstWidth && dst->bounds().height() >= dstHeight); } quint8* dabPointer = dst->data(); quint8* color = 0; if (coloringInformation) { if (dynamic_cast<PlainColoringInformation*>(coloringInformation)) { color = const_cast<quint8*>(coloringInformation->color()); } } double invScaleX = 1.0 / scaleX; double invScaleY = 1.0 / scaleY; double centerX = hotSpot.x() - 0.5 + subPixelX; double centerY = hotSpot.y() - 0.5 + subPixelY; d->shape->setSoftness( softnessFactor ); if (coloringInformation) { if (color && pixelSize == 4) { fillPixelOptimized_4bytes(color, dabPointer, dstWidth * dstHeight); } else if (color) { fillPixelOptimized_general(color, dabPointer, dstWidth * dstHeight, pixelSize); } else { for (int y = 0; y < dstHeight; y++) { for (int x = 0; x < dstWidth; x++) { memcpy(dabPointer, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); dabPointer += pixelSize; } coloringInformation->nextRow(); } } } MaskProcessingData data(dst, cs, d->randomness, d->density, centerX, centerY, invScaleX, invScaleY, angle); KisBrushMaskApplicatorBase *applicator = d->shape->applicator(); applicator->initializeData(&data); int jobs = d->idealThreadCountCached; if(dstHeight > 100 && jobs >= 4) { int splitter = dstHeight/jobs; QVector<QRect> rects; for(int i = 0; i < jobs - 1; i++) { rects << QRect(0, i*splitter, dstWidth, splitter); } rects << QRect(0, (jobs - 1)*splitter, dstWidth, dstHeight - (jobs - 1)*splitter); OperatorWrapper wrapper(applicator); QtConcurrent::blockingMap(rects, wrapper); } else { QRect rect(0, 0, dstWidth, dstHeight); applicator->process(rect); } }
void KisBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info_, double subPixelX, double subPixelY, qreal softnessFactor) const { Q_ASSERT(valid()); Q_UNUSED(info_); Q_UNUSED(softnessFactor); angle += d->angle; // Make sure the angle stay in [0;2*M_PI] if (angle < 0) angle += 2 * M_PI; if (angle > 2 * M_PI) angle -= 2 * M_PI; scaleX *= d->scale; scaleY *= d->scale; double scale = 0.5 * (scaleX + scaleY); prepareBrushPyramid(); QImage outputImage = d->brushPyramid->createImage(scale, -angle, subPixelX, subPixelY); qint32 maskWidth = outputImage.width(); qint32 maskHeight = outputImage.height(); dst->setRect(QRect(0, 0, maskWidth, maskHeight)); dst->initialize(); quint8* color = 0; if (coloringInformation) { if (dynamic_cast<PlainColoringInformation*>(coloringInformation)) { color = const_cast<quint8*>(coloringInformation->color()); } } const KoColorSpace *cs = dst->colorSpace(); qint32 pixelSize = cs->pixelSize(); quint8 *dabPointer = dst->data(); quint8 *rowPointer = dabPointer; quint8 *alphaArray = new quint8[maskWidth]; bool hasColor = this->hasColor(); for (int y = 0; y < maskHeight; y++) { #if QT_VERSION >= 0x040700 const quint8* maskPointer = outputImage.constScanLine(y); #else const quint8* maskPointer = outputImage.scanLine(y); #endif if (coloringInformation) { for (int x = 0; x < maskWidth; x++) { if (color) { memcpy(dabPointer, color, pixelSize); } else { memcpy(dabPointer, coloringInformation->color(), pixelSize); coloringInformation->nextColumn(); } dabPointer += pixelSize; } } if (hasColor) { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast<const QRgb*>(src); *dst = KoColorSpaceMaths<quint8>::multiply(255 - qGray(*c), qAlpha(*c)); src += 4; dst++; } } else { const quint8 *src = maskPointer; quint8 *dst = alphaArray; for (int x = 0; x < maskWidth; x++) { const QRgb *c = reinterpret_cast<const QRgb*>(src); *dst = KoColorSpaceMaths<quint8>::multiply(255 - *src, qAlpha(*c)); src += 4; dst++; } } cs->applyAlphaU8Mask(rowPointer, alphaArray, maskWidth); rowPointer += maskWidth * pixelSize; dabPointer = rowPointer; if (!color && coloringInformation) { coloringInformation->nextRow(); } } delete alphaArray; }