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 KisFixedPaintDeviceTest::testFill() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8* red = new quint8[cs->pixelSize()]; memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize()); cs->setOpacity(red, quint8(128), 1); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->fill(0, 0, 100, 100, red); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data()) == 128); QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0); //Compare fill will normal paint device dev = new KisFixedPaintDevice(cs); dev->setRect(QRect(0, 0, 150, 150)); dev->initialize(); dev->fill(50, 50, 50, 50, red); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->fill(50, 50, 50, 50, red); QImage image = dev->convertToQImage(0); QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, checkImage)) { image.save("kis_fixed_paint_device_filled_result.png"); checkImage.save("kis_fixed_paint_device_filled_result_expected.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete[] red; }
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; } }
void KisFixedPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->clear(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8); }
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); } }
//#define SAVE_OUTPUT_IMAGES void KisAutoBrushTest::testCopyMasking() { int w = 64; int h = 64; int x = 0; int y = 0; QRect rc(x, y, w, h); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor black(Qt::black, cs); KoColor red(Qt::red, cs); KisPaintDeviceSP tempDev = new KisPaintDevice(cs); tempDev->fill(0, 0, w, h, red.data()); #ifdef SAVE_OUTPUT_IMAGES tempDev->convertToQImage(0).save("tempDev.png"); #endif KisCircleMaskGenerator * mask = new KisCircleMaskGenerator(w, 1.0, 0.5, 0.5, 2, true); KisAutoBrush brush(mask, 0, 0); KisFixedPaintDeviceSP maskDab = new KisFixedPaintDevice(cs); brush.mask(maskDab, black, KisDabShape(), KisPaintInformation()); maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); #ifdef SAVE_OUTPUT_IMAGES maskDab->convertToQImage(0, 0, 0, 64, 64).save("maskDab.png"); #endif QCOMPARE(tempDev->exactBounds(), rc); QCOMPARE(maskDab->bounds(), rc); KisFixedPaintDeviceSP dev2fixed = new KisFixedPaintDevice(cs); dev2fixed->setRect(rc); dev2fixed->initialize(); tempDev->readBytes(dev2fixed->data(), rc); dev2fixed->convertToQImage(0).save("converted-tempDev-to-fixed.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter painter(dev); painter.setCompositeOp(COMPOSITE_COPY); painter.bltFixedWithFixedSelection(x, y, dev2fixed, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); //painter.bitBltWithFixedSelection(x, y, tempDev, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); #ifdef SAVE_OUTPUT_IMAGES dev->convertToQImage(0).save("final.png"); #endif }
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); }
void benchmarkSIMD(qreal fade) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->setRect(QRect(0, 0, 1000, 1000)); dev->initialize(); MaskProcessingData data(dev, cs, 0.0, 1.0, 500, 500, 0); KisCircleMaskGenerator gen(1000, 1.0, fade, fade, 2, false); KisBrushMaskApplicatorBase *applicator = gen.applicator(); applicator->initializeData(&data); QVector<QRect> rects = KritaUtils::splitRectIntoPatches(dev->bounds(), QSize(63, 63)); QBENCHMARK{ Q_FOREACH (const QRect &rc, rects) { applicator->process(rc); } }
void KisFilterOp::paintAt(const KisPaintInformation& info) { if (!painter()) { return; } KisFilterSP filter = settings->filter(); if (!filter) { return; } if (!source()) { return; } KisBrushSP brush = m_brush;; if (!brush) return; KisPaintInformation adjustedInfo = settings->m_optionsWidget->m_sizeOption->apply(info); if (! brush->canPaintFor(adjustedInfo)) return; 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); double scale = KisPaintOp::scaleForPressure(adjustedInfo.pressure()); qint32 maskWidth = brush->maskWidth(scale, 0.0); qint32 maskHeight = brush->maskHeight(scale, 0.0); // Filter the paint device filter->process(KisConstProcessingInformation(source(), QPoint(x, y)), KisProcessingInformation(m_tmpDevice, QPoint(0, 0)), QSize(maskWidth, maskHeight), settings->filterConfig(), 0); // Apply the mask on the paint device (filter before mask because edge pixels may be important) KisFixedPaintDeviceSP fixedDab = new KisFixedPaintDevice(m_tmpDevice->colorSpace()); fixedDab->setRect(m_tmpDevice->extent()); fixedDab->initialize(); m_tmpDevice->readBytes(fixedDab->data(), fixedDab->bounds()); brush->mask(fixedDab, scale, scale, 0.0, info, xFraction, yFraction); m_tmpDevice->writeBytes(fixedDab->data(), fixedDab->bounds()); if (!settings->ignoreAlpha()) { KisHLineIteratorPixel itTmpDev = m_tmpDevice->createHLineIterator(0, 0, maskWidth); KisHLineIteratorPixel itSrc = source()->createHLineIterator(x, y, maskWidth); const KoColorSpace* cs = m_tmpDevice->colorSpace(); for (int y = 0; y < maskHeight; ++y) { while (!itTmpDev.isDone()) { quint8 alphaTmpDev = cs->alpha(itTmpDev.rawData()); quint8 alphaSrc = cs->alpha(itSrc.rawData()); cs->setAlpha(itTmpDev.rawData(), qMin(alphaTmpDev, alphaSrc), 1); ++itTmpDev; ++itSrc; } itTmpDev.nextRow(); itSrc.nextRow(); } } // Blit the paint device onto the layer QRect dabRect = QRect(0, 0, maskWidth, maskHeight); QRect dstRect = QRect(x, y, dabRect.width(), dabRect.height()); if (painter()->bounds().isValid()) { dstRect &= painter()->bounds(); } if (dstRect.isNull() || dstRect.isEmpty() || !dstRect.isValid()) return; qint32 sx = dstRect.x() - x; qint32 sy = dstRect.y() - y; qint32 sw = dstRect.width(); qint32 sh = dstRect.height(); painter()->bitBlt(dstRect.x(), dstRect.y(), m_tmpDevice, sx, sy, sw, sh); }
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 KisDuplicateOp::paintAt(const KisPaintInformation& info) { if (!painter()) return; if (!m_duplicateStartIsSet) { m_duplicateStartIsSet = true; m_duplicateStart = info.pos(); } bool heal = settings->healing(); if (!source()) return; KisBrushSP brush = m_brush; if (!brush) return; if (! brush->canPaintFor(info)) return; double scale = KisPaintOp::scaleForPressure(info.pressure()); QPointF hotSpot = brush->hotSpot(scale, scale); 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); xFraction = yFraction = 0.0; QPointF srcPointF = pt - settings->offset(); QPoint srcPoint = QPoint(x - static_cast<qint32>(settings->offset().x()), y - static_cast<qint32>(settings->offset().y())); qint32 sw = brush->maskWidth(scale, 0.0); qint32 sh = brush->maskHeight(scale, 0.0); if (srcPoint.x() < 0) srcPoint.setX(0); if (srcPoint.y() < 0) srcPoint.setY(0); if (!(m_srcdev && !(*m_srcdev->colorSpace() == *source()->colorSpace()))) { m_srcdev = new KisPaintDevice(source()->colorSpace()); } Q_CHECK_PTR(m_srcdev); // Perspective correction ? KisPainter copyPainter(m_srcdev); KisImageSP image = settings->m_image; if (settings->perspectiveCorrection() && image && image->perspectiveGrid()->countSubGrids() == 1) { Matrix3qreal startM = Matrix3qreal::Identity(); Matrix3qreal endM = Matrix3qreal::Identity(); // First look for the grid corresponding to the start point KisSubPerspectiveGrid* subGridStart = *image->perspectiveGrid()->begin(); QRect r = QRect(0, 0, image->width(), image->height()); #if 1 if (subGridStart) { startM = KisPerspectiveMath::computeMatrixTransfoFromPerspective(r, *subGridStart->topLeft(), *subGridStart->topRight(), *subGridStart->bottomLeft(), *subGridStart->bottomRight()); } #endif #if 1 // Second look for the grid corresponding to the end point KisSubPerspectiveGrid* subGridEnd = *image->perspectiveGrid()->begin(); if (subGridEnd) { endM = KisPerspectiveMath::computeMatrixTransfoToPerspective(*subGridEnd->topLeft(), *subGridEnd->topRight(), *subGridEnd->bottomLeft(), *subGridEnd->bottomRight(), r); } #endif // Compute the translation in the perspective transformation space: QPointF positionStartPaintingT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart)); QPointF duplicateStartPositionT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart) - QPointF(settings->offset())); QPointF translat = duplicateStartPositionT - positionStartPaintingT; KisRectIteratorPixel dstIt = m_srcdev->createRectIterator(0, 0, sw, sh); KisRandomSubAccessorPixel srcAcc = source()->createRandomSubAccessor(); //Action while (!dstIt.isDone()) { if (dstIt.isSelected()) { QPointF p = KisPerspectiveMath::matProd(startM, KisPerspectiveMath::matProd(endM, QPointF(dstIt.x() + x, dstIt.y() + y)) + translat); srcAcc.moveTo(p); srcAcc.sampledOldRawData(dstIt.rawData()); } ++dstIt; } } else { // Or, copy the source data on the temporary device: copyPainter.setCompositeOp(COMPOSITE_COPY); copyPainter.bitBlt(0, 0, source(), srcPoint.x(), srcPoint.y(), sw, sh); copyPainter.end(); } // heal ? if (heal) { quint16 dataDevice[4]; quint16 dataSrcDev[4]; double* matrix = new double[ 3 * sw * sh ]; // First divide const KoColorSpace* deviceCs = source()->colorSpace(); KisHLineConstIteratorPixel deviceIt = source()->createHLineConstIterator(x, y, sw); KisHLineIteratorPixel srcDevIt = m_srcdev->createHLineIterator(0, 0, sw); double* matrixIt = &matrix[0]; for (int j = 0; j < sh; j++) { for (int i = 0; !srcDevIt.isDone(); i++) { deviceCs->toLabA16(deviceIt.rawData(), (quint8*)dataDevice, 1); deviceCs->toLabA16(srcDevIt.rawData(), (quint8*)dataSrcDev, 1); // Division for (int k = 0; k < 3; k++) { matrixIt[k] = dataDevice[k] / (double)qMax((int)dataSrcDev [k], 1); } ++deviceIt; ++srcDevIt; matrixIt += 3; } deviceIt.nextRow(); srcDevIt.nextRow(); } // Minimize energy { int iter = 0; double err; double* solution = new double [ 3 * sw * sh ]; do { err = minimizeEnergy(&matrix[0], &solution[0], sw, sh); memcpy(&matrix[0], &solution[0], sw * sh * 3 * sizeof(double)); iter++; } while (err < 0.00001 && iter < 100); delete [] solution; } // Finaly multiply deviceIt = source()->createHLineIterator(x, y, sw); srcDevIt = m_srcdev->createHLineIterator(0, 0, sw); matrixIt = &matrix[0]; for (int j = 0; j < sh; j++) { for (int i = 0; !srcDevIt.isDone(); i++) { deviceCs->toLabA16(deviceIt.rawData(), (quint8*)dataDevice, 1); deviceCs->toLabA16(srcDevIt.rawData(), (quint8*)dataSrcDev, 1); // Multiplication for (int k = 0; k < 3; k++) { dataSrcDev[k] = (int)CLAMP(matrixIt[k] * qMax((int) dataSrcDev[k], 1), 0, 65535); } deviceCs->fromLabA16((quint8*)dataSrcDev, srcDevIt.rawData(), 1); ++deviceIt; ++srcDevIt; matrixIt += 3; } deviceIt.nextRow(); srcDevIt.nextRow(); } delete [] matrix; } KisFixedPaintDeviceSP fixedDab = new KisFixedPaintDevice(m_srcdev->colorSpace()); fixedDab->setRect(QRect(0, 0, sw, sh)); fixedDab->initialize(); m_srcdev->readBytes(fixedDab->data(), fixedDab->bounds()); brush->mask(fixedDab, scale, scale, 0.0, info, xFraction, yFraction); m_srcdev->writeBytes(fixedDab->data(), fixedDab->bounds()); QRect dabRect = QRect(0, 0, brush->maskWidth(scale, 0.0), brush->maskHeight(scale, 0.0)); QRect dstRect = QRect(x, y, dabRect.width(), dabRect.height()); if (painter()->bounds().isValid()) { dstRect &= painter()->bounds(); } if (dstRect.isNull() || dstRect.isEmpty() || !dstRect.isValid()) return; qint32 sx = dstRect.x() - x; qint32 sy = dstRect.y() - y; sw = dstRect.width(); sh = dstRect.height(); painter()->bitBlt(dstRect.x(), dstRect.y(), m_srcdev, sx, sy, sw, sh); }
KisSpacingInformation KisFilterOp::paintAt(const KisPaintInformation& info) { if (!painter()) { return KisSpacingInformation(1.0); } if (!m_filter) { return KisSpacingInformation(1.0); } if (!source()) { return KisSpacingInformation(1.0); } KisBrushSP brush = m_brush; if (!brush) return KisSpacingInformation(1.0); if (! brush->canPaintFor(info)) return KisSpacingInformation(1.0); qreal scale = m_sizeOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); setCurrentScale(scale); qreal rotation = m_rotationOption.apply(info); static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); QRect dstRect; KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(cs, color, info.pos(), scale, scale, rotation, info, 1.0, &dstRect); if (dstRect.isEmpty()) return KisSpacingInformation(1.0); QRect dabRect = dab->bounds(); // sanity check Q_ASSERT(dstRect.size() == dabRect.size()); // Filter the paint device QRect neededRect = m_filter->neededRect(dstRect, m_filterConfiguration); KisPainter p(m_tmpDevice); if (!m_smudgeMode) { p.setCompositeOp(COMPOSITE_COPY); } p.bitBltOldData(neededRect.topLeft() - dstRect.topLeft(), source(), neededRect); KisTransaction transaction(m_tmpDevice); m_filter->process(m_tmpDevice, dabRect, m_filterConfiguration, 0); transaction.end(); painter()-> bitBltWithFixedSelection(dstRect.x(), dstRect.y(), m_tmpDevice, dab, 0, 0, dabRect.x(), dabRect.y(), dabRect.width(), dabRect.height()); painter()->renderMirrorMaskSafe(dstRect, m_tmpDevice, 0, 0, dab, !m_dabCache->needSeparateOriginal()); return effectiveSpacing(scale, rotation); }
void KisSmudgeOp::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()); 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); KisFixedPaintDeviceSP dab = 0; double scale = KisPaintOp::scaleForPressure(adjustedInfo.pressure()); QRect dabRect = QRect(0, 0, brush->maskWidth(scale, 0.0), brush->maskHeight(scale, 0.0)); QRect dstRect = QRect(x, y, dabRect.width(), dabRect.height()); if (dstRect.isNull() || dstRect.isEmpty() || !dstRect.isValid()) return; if (brush->brushType() == IMAGE || brush->brushType() == PIPE_IMAGE) { dab = brush->image(device->colorSpace(), pScale, 0.0, adjustedInfo, xFraction, yFraction); dab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); } else { dab = cachedDab(); KoColor color = painter()->paintColor(); dab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); brush->mask(dab, color, scale, pScale, 0.0, info, xFraction, yFraction); } qint32 sw = dab->bounds().width(); qint32 sh = dab->bounds().height(); /* To smudge, one does the following: * at first, initialize a temporary paint device with a copy of the original (dab-sized piece, really). * all other times: reduce the transparency of the temporary paint device so as to let it mix gradually * combine the temp device with the piece the brush currently is 'painting', according to a mix (opacity) note that in the first step, this does the actual copying of the data * this combination is then composited upon the actual image TODO: what happened exactly in 1.6 (and should happen now) when the dab resizes halfway due to pressure? */ int opacity = OPACITY_OPAQUE; if (!m_firstRun) { opacity = settings->m_optionsWidget->m_rateOption->apply( opacity, sw, sh, m_srcdev, info.pressure() ); KisRectIterator it = m_srcdev->createRectIterator(0, 0, sw, sh); KoColorSpace* cs = m_srcdev->colorSpace(); while(!it.isDone()) { cs->setAlpha(it.rawData(), (cs->alpha(it.rawData()) * opacity) / OPACITY_OPAQUE, 1); ++it; } opacity = OPACITY_OPAQUE - opacity; } else { m_firstRun = false; } KisPainter copyPainter(m_srcdev); copyPainter.setOpacity(opacity); copyPainter.bitBlt(0, 0, device, pt.x(), pt.y(), sw, sh); copyPainter.end(); m_target = new KisPaintDevice(device->colorSpace()); // Looks hacky, but we lost bltMask, or the ability to easily convert alpha8 paintdev to selection? KisSelectionSP dabAsSelection = new KisSelection(); copyPainter.begin(dabAsSelection); copyPainter.setOpacity(OPACITY_OPAQUE); copyPainter.setCompositeOp(COMPOSITE_COPY); copyPainter.bltFixed(0, 0, dab, 0, 0, sw, sh); copyPainter.end(); copyPainter.begin(m_target); copyPainter.setCompositeOp(COMPOSITE_OVER); copyPainter.setSelection(dabAsSelection); copyPainter.bitBlt(0, 0, m_srcdev, 0, 0, sw, sh); copyPainter.end(); qint32 sx = dstRect.x() - x; qint32 sy = dstRect.y() - y; sw = dstRect.width(); sh = dstRect.height(); painter()->bitBlt(dstRect.x(), dstRect.y(), m_target, sx, sy, sw, sh); }
KisSpacingInformation KisDuplicateOp::paintAt(const KisPaintInformation& info) { if (!painter()->device()) return 1.0; KisBrushSP brush = m_brush; if (!brush) return 1.0; if (!brush->canPaintFor(info)) return 1.0; if (!m_duplicateStartIsSet) { m_duplicateStartIsSet = true; m_duplicateStart = info.pos(); } KisPaintDeviceSP realSourceDevice = settings->node()->paintDevice(); qreal scale = m_sizeOption.apply(info); if (checkSizeTooSmall(scale)) return KisSpacingInformation(); QPointF hotSpot = brush->hotSpot(scale, scale, 0, info); QPointF pt = info.pos() - hotSpot; setCurrentScale(scale); // 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, y; qreal xFraction, yFraction; // will not be used splitCoordinate(pt.x(), &x, &xFraction); splitCoordinate(pt.y(), &y, &yFraction); QPoint srcPoint; if(m_moveSourcePoint) { srcPoint = QPoint(x - static_cast<qint32>(settings->offset().x()), y - static_cast<qint32>(settings->offset().y())); } else { srcPoint = QPoint(static_cast<qint32>(settings->position().x() - hotSpot.x()), static_cast<qint32>(settings->position().y() - hotSpot.y())); } qint32 sw = brush->maskWidth(scale, 0.0, xFraction, yFraction, info); qint32 sh = brush->maskHeight(scale, 0.0, xFraction, yFraction, info); if (srcPoint.x() < 0) srcPoint.setX(0); if (srcPoint.y() < 0) srcPoint.setY(0); // Perspective correction ? KisImageWSP image = settings->m_image; if (m_perspectiveCorrection && image && image->perspectiveGrid()->countSubGrids() == 1) { Matrix3qreal startM = Matrix3qreal::Identity(); Matrix3qreal endM = Matrix3qreal::Identity(); // First look for the grid corresponding to the start point KisSubPerspectiveGrid* subGridStart = *image->perspectiveGrid()->begin(); QRect r = QRect(0, 0, image->width(), image->height()); #if 1 if (subGridStart) { startM = KisPerspectiveMath::computeMatrixTransfoFromPerspective(r, *subGridStart->topLeft(), *subGridStart->topRight(), *subGridStart->bottomLeft(), *subGridStart->bottomRight()); } #endif #if 1 // Second look for the grid corresponding to the end point KisSubPerspectiveGrid* subGridEnd = *image->perspectiveGrid()->begin(); if (subGridEnd) { endM = KisPerspectiveMath::computeMatrixTransfoToPerspective(*subGridEnd->topLeft(), *subGridEnd->topRight(), *subGridEnd->bottomLeft(), *subGridEnd->bottomRight(), r); } #endif // Compute the translation in the perspective transformation space: QPointF positionStartPaintingT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart)); QPointF duplicateStartPositionT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart) - QPointF(settings->offset())); QPointF translat = duplicateStartPositionT - positionStartPaintingT; KisRectIteratorSP dstIt = m_srcdev->createRectIteratorNG(0, 0, sw, sh); KisRandomSubAccessorSP srcAcc = realSourceDevice->createRandomSubAccessor(); //Action do { QPointF p = KisPerspectiveMath::matProd(startM, KisPerspectiveMath::matProd(endM, QPointF(dstIt->x() + x, dstIt->y() + y)) + translat); srcAcc->moveTo(p); srcAcc->sampledOldRawData(dstIt->rawData()); } while (dstIt->nextPixel()); } else { KisPainter copyPainter(m_srcdev); copyPainter.setCompositeOp(COMPOSITE_COPY); copyPainter.bitBltOldData(0, 0, realSourceDevice, srcPoint.x(), srcPoint.y(), sw, sh); copyPainter.end(); } // heal ? if (m_healing) { quint16 srcData[4]; quint16 tmpData[4]; qreal* matrix = new qreal[ 3 * sw * sh ]; // First divide const KoColorSpace* srcCs = realSourceDevice->colorSpace(); const KoColorSpace* tmpCs = m_srcdev->colorSpace(); KisHLineConstIteratorSP srcIt = realSourceDevice->createHLineConstIteratorNG(x, y, sw); KisHLineIteratorSP tmpIt = m_srcdev->createHLineIteratorNG(0, 0, sw); qreal* matrixIt = &matrix[0]; for (int j = 0; j < sh; j++) { for (int i = 0; i < sw; i++) { srcCs->toLabA16(srcIt->oldRawData(), (quint8*)srcData, 1); tmpCs->toLabA16(tmpIt->rawData(), (quint8*)tmpData, 1); // Division for (int k = 0; k < 3; k++) { matrixIt[k] = srcData[k] / (qreal)qMax((int)tmpData [k], 1); } srcIt->nextPixel(); tmpIt->nextPixel(); matrixIt += 3; } srcIt->nextRow(); tmpIt->nextRow(); } // Minimize energy { int iter = 0; qreal err; qreal* solution = new qreal [ 3 * sw * sh ]; do { err = minimizeEnergy(&matrix[0], &solution[0], sw, sh); // swap pointers qreal *tmp = matrix; matrix = solution; solution = tmp; iter++; } while (err > 0.00001 && iter < 100); delete [] solution; } // Finaly multiply KisHLineIteratorSP srcIt2 = realSourceDevice->createHLineIteratorNG(x, y, sw); KisHLineIteratorSP tmpIt2 = m_srcdev->createHLineIteratorNG(0, 0, sw); matrixIt = &matrix[0]; for (int j = 0; j < sh; j++) { for (int i = 0; i < sw; i++) { srcCs->toLabA16(srcIt2->rawData(), (quint8*)srcData, 1); tmpCs->toLabA16(tmpIt2->rawData(), (quint8*)tmpData, 1); // Multiplication for (int k = 0; k < 3; k++) { tmpData[k] = (int)CLAMP(matrixIt[k] * qMax((int) tmpData[k], 1), 0, 65535); } tmpCs->fromLabA16((quint8*)tmpData, tmpIt2->rawData(), 1); srcIt2->nextPixel(); tmpIt2->nextPixel(); matrixIt += 3; } srcIt2->nextRow(); tmpIt2->nextRow(); } delete [] matrix; } static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); static KoColor color(Qt::black, cs); KisFixedPaintDeviceSP dab = m_dabCache->fetchDab(cs, color, scale, scale, 0.0, info); QRect dstRect = QRect(x, y, dab->bounds().width(), dab->bounds().height()); if (dstRect.isEmpty()) return 1.0; painter()->bitBltWithFixedSelection(dstRect.x(), dstRect.y(), m_srcdev, dab, dstRect.width(), dstRect.height()); painter()->renderMirrorMaskSafe(dstRect, m_srcdev, 0, 0, dab, !m_dabCache->needSeparateOriginal()); return effectiveSpacing(dstRect.width(), dstRect.height()); }
qreal KisFilterOp::paintAt(const KisPaintInformation& info) { if (!painter()) { return 1.0; } if (!m_filter) { return 1.0; } if (!source()) { return 1.0; } KisBrushSP brush = m_brush;; if (!brush) return 1.0; if (! brush->canPaintFor(info)) return 1.0; qreal scale = KisPaintOp::scaleForPressure(m_sizeOption.apply(info)); if ((scale * brush->width()) <= 0.01 || (scale * brush->height()) <= 0.01) return spacing(scale); setCurrentScale(scale); QPointF hotSpot = brush->hotSpot(scale, scale); 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; qreal xFraction; qint32 y; qreal yFraction; splitCoordinate(pt.x(), &x, &xFraction); splitCoordinate(pt.y(), &y, &yFraction); qint32 maskWidth = brush->maskWidth(scale, 0.0); qint32 maskHeight = brush->maskHeight(scale, 0.0); // Filter the paint device m_filter->process(KisConstProcessingInformation(source(), QPoint(x, y)), KisProcessingInformation(m_tmpDevice, QPoint(0, 0)), QSize(maskWidth, maskHeight), m_filterConfiguration, 0); // Apply the mask on the paint device (filter before mask because edge pixels may be important) KisFixedPaintDeviceSP fixedDab = new KisFixedPaintDevice(m_tmpDevice->colorSpace()); fixedDab->setRect(m_tmpDevice->extent()); fixedDab->initialize(); m_tmpDevice->readBytes(fixedDab->data(), fixedDab->bounds()); brush->mask(fixedDab, scale, scale, 0.0, info, xFraction, yFraction); m_tmpDevice->writeBytes(fixedDab->data(), fixedDab->bounds()); if (!m_ignoreAlpha) { KisHLineIteratorPixel itTmpDev = m_tmpDevice->createHLineIterator(0, 0, maskWidth); KisHLineIteratorPixel itSrc = source()->createHLineIterator(x, y, maskWidth); const KoColorSpace* cs = m_tmpDevice->colorSpace(); for (int y = 0; y < maskHeight; ++y) { while (!itTmpDev.isDone()) { quint8 alphaTmpDev = cs->opacityU8(itTmpDev.rawData()); quint8 alphaSrc = cs->opacityU8(itSrc.rawData()); cs->setOpacity(itTmpDev.rawData(), qMin(alphaTmpDev, alphaSrc), 1); ++itTmpDev; ++itSrc; } itTmpDev.nextRow(); itSrc.nextRow(); } } // Blit the paint device onto the layer QRect dabRect = QRect(0, 0, maskWidth, maskHeight); QRect dstRect = QRect(x, y, dabRect.width(), dabRect.height()); if (dstRect.isNull() || dstRect.isEmpty() || !dstRect.isValid()) return 1.0; qint32 sx = dstRect.x() - x; qint32 sy = dstRect.y() - y; qint32 sw = dstRect.width(); qint32 sh = dstRect.height(); painter()->bitBlt(dstRect.x(), dstRect.y(), m_tmpDevice, sx, sy, sw, sh); return spacing(scale); }