Paste P113

Masterwork From Distant Lands
ActivePublic

Authored by dkazakov on Oct 10 2017, 1:50 PM.
diff --git a/benchmarks/kis_mask_generator_benchmark.cpp b/benchmarks/kis_mask_generator_benchmark.cpp
index da3709a..058657b 100644
--- a/benchmarks/kis_mask_generator_benchmark.cpp
+++ b/benchmarks/kis_mask_generator_benchmark.cpp
@@ -66,7 +66,7 @@ void benchmarkSIMD(qreal fade) {
dev->setRect(QRect(0, 0, 1000, 1000));
dev->initialize();
- MaskProcessingData data(dev, cs,
+ MaskProcessingData data(dev, cs, 0,
0.0, 1.0,
500, 500, 0);
diff --git a/libs/brush/kis_auto_brush.cpp b/libs/brush/kis_auto_brush.cpp
index 7feeb3e..2cdec70 100644
--- a/libs/brush/kis_auto_brush.cpp
+++ b/libs/brush/kis_auto_brush.cpp
@@ -251,28 +251,13 @@ void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst
// 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();
- }
+ dst->lazyGrowDataWithoutInitialization();
}
else {
- if (dst->data() == 0 || dst->bounds().isEmpty()) {
- warnKrita << "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);
+ KIS_SAFE_ASSERT_RECOVER_RETURN(dst->bounds().width() >= dstWidth &&
+ dst->bounds().height() >= dstHeight);
}
quint8* dabPointer = dst->data();
@@ -290,14 +275,18 @@ void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst
d->shape->setScale(shape.scaleX(), shape.scaleY());
d->shape->setSoftness(softnessFactor);
- if (coloringInformation) {
+ if (coloringInformation && !color) {
+#if 0
+
if (color && pixelSize == 4) {
fillPixelOptimized_4bytes(color, dabPointer, dstWidth * dstHeight);
}
else if (color) {
fillPixelOptimized_general(color, dabPointer, dstWidth * dstHeight, pixelSize);
}
- else {
+ else
+#endif
+ {
for (int y = 0; y < dstHeight; y++) {
for (int x = 0; x < dstWidth; x++) {
memcpy(dabPointer, coloringInformation->color(), pixelSize);
@@ -309,7 +298,9 @@ void KisAutoBrush::generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst
}
}
- MaskProcessingData data(dst, cs, d->randomness, d->density,
+ MaskProcessingData data(dst, cs,
+ color,
+ d->randomness, d->density,
centerX, centerY,
angle);
diff --git a/libs/brush/kis_brush.h b/libs/brush/kis_brush.h
index b1ea820..8a5ff3d 100644
--- a/libs/brush/kis_brush.h
+++ b/libs/brush/kis_brush.h
@@ -259,6 +259,8 @@ public:
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0) const;
+private:
+
/**
* Apply the brush mask to the pixels in dst. Dst should be big enough!
*/
@@ -266,6 +268,7 @@ public:
KisDabShape const& shape,
const KisPaintInformation& info,
double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const;
+public:
/**
* clear dst fill it with a mask colored with KoColor
diff --git a/libs/brush/tests/kis_gbr_brush_test.cpp b/libs/brush/tests/kis_gbr_brush_test.cpp
index c027e9d..38f8f43 100644
--- a/libs/brush/tests/kis_gbr_brush_test.cpp
+++ b/libs/brush/tests/kis_gbr_brush_test.cpp
@@ -56,7 +56,7 @@ void KisGbrBrushTest::testMaskGenerationNoColor()
QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
}
- brush->mask(fdev, KisDabShape(), info);
+ //brush->mask(fdev, KisDabShape(), info);
result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_2.png");
image = fdev->convertToQImage(0);
@@ -144,7 +144,7 @@ void KisGbrBrushTest::testMaskGenerationDefaultColor()
// check creating a mask dab with a default color
fdev = new KisFixedPaintDevice(cs);
- brush->mask(fdev, KisDabShape(), info);
+ //brush->mask(fdev, KisDabShape(), info);
QPoint errpoint;
QImage result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_brush_3.png");
diff --git a/libs/brush/tests/kis_imagepipe_brush_test.cpp b/libs/brush/tests/kis_imagepipe_brush_test.cpp
index ea47af1..c7216c6 100644
--- a/libs/brush/tests/kis_imagepipe_brush_test.cpp
+++ b/libs/brush/tests/kis_imagepipe_brush_test.cpp
@@ -145,7 +145,7 @@ void checkIncrementalPainting(KisBrush *brush, const QString &prefix)
fixedDab->initialize();
fixedDab->fill(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), fillColor.data());
- brush->mask(fixedDab, KisDabShape(realScale, 1.0, realAngle), info);
+ //brush->mask(fixedDab, KisDabShape(realScale, 1.0, realAngle), info);
QCOMPARE(fixedDab->bounds(), fillRect);
QImage result = fixedDab->convertToQImage(0);
diff --git a/libs/image/kis_brush_mask_applicator_base.h b/libs/image/kis_brush_mask_applicator_base.h
index 9376326..811bd6c 100644
--- a/libs/image/kis_brush_mask_applicator_base.h
+++ b/libs/image/kis_brush_mask_applicator_base.h
@@ -27,6 +27,7 @@
struct MaskProcessingData {
MaskProcessingData(KisFixedPaintDeviceSP _device,
const KoColorSpace* _colorSpace,
+ const quint8 *_fillColor,
qreal _randomness,
qreal _density,
double _centerX,
@@ -35,6 +36,7 @@ struct MaskProcessingData {
{
device = _device;
colorSpace = _colorSpace;
+ fillColor = _fillColor,
randomness = _randomness;
density = _density;
centerX = _centerX;
@@ -56,6 +58,8 @@ struct MaskProcessingData {
double cosa;
double sina;
+ const quint8 *fillColor = 0;
+
qint32 pixelSize;
};
diff --git a/libs/image/kis_brush_mask_applicators.h b/libs/image/kis_brush_mask_applicators.h
index b61c6bd..8903282 100644
--- a/libs/image/kis_brush_mask_applicators.h
+++ b/libs/image/kis_brush_mask_applicators.h
@@ -94,6 +94,88 @@ private:
}
};
+inline void fillPixelOptimized_4bytes(const quint8 *color, quint8 *buf, int size)
+{
+ /**
+ * This version of filling uses low granularity of data transfers
+ * (32-bit chunks) and internal processor's parallelism. It reaches
+ * 25% better performance in KisStrokeBenchmark in comparison to
+ * per-pixel memcpy version (tested on Sandy Bridge).
+ */
+
+ int block1 = size / 8;
+ int block2 = size % 8;
+
+ const quint32 *src = reinterpret_cast<const quint32*>(color);
+ quint32 *dst = reinterpret_cast<quint32*>(buf);
+
+ // check whether all buffers are 4 bytes aligned
+ // (uncomment if experience some problems)
+ // Q_ASSERT(((qint64)src & 3) == 0);
+ // Q_ASSERT(((qint64)dst & 3) == 0);
+
+ for (int i = 0; i < block1; i++) {
+ *dst = *src;
+ *(dst + 1) = *src;
+ *(dst + 2) = *src;
+ *(dst + 3) = *src;
+ *(dst + 4) = *src;
+ *(dst + 5) = *src;
+ *(dst + 6) = *src;
+ *(dst + 7) = *src;
+
+ dst += 8;
+ }
+
+ for (int i = 0; i < block2; i++) {
+ *dst = *src;
+ dst++;
+ }
+}
+
+inline void fillPixelOptimized_general(const quint8 *color, quint8 *buf, int size, int pixelSize)
+{
+ /**
+ * This version uses internal processor's parallelism and gives
+ * 20% better performance in KisStrokeBenchmark in comparison to
+ * per-pixel memcpy version (tested on Sandy Bridge (+20%) and
+ * on Merom (+10%)).
+ */
+
+ int block1 = size / 8;
+ int block2 = size % 8;
+
+ for (int i = 0; i < block1; i++) {
+ quint8 *d1 = buf;
+ quint8 *d2 = buf + pixelSize;
+ quint8 *d3 = buf + 2 * pixelSize;
+ quint8 *d4 = buf + 3 * pixelSize;
+ quint8 *d5 = buf + 4 * pixelSize;
+ quint8 *d6 = buf + 5 * pixelSize;
+ quint8 *d7 = buf + 6 * pixelSize;
+ quint8 *d8 = buf + 7 * pixelSize;
+
+ for (int j = 0; j < pixelSize; j++) {
+ *(d1 + j) = color[j];
+ *(d2 + j) = color[j];
+ *(d3 + j) = color[j];
+ *(d4 + j) = color[j];
+ *(d5 + j) = color[j];
+ *(d6 + j) = color[j];
+ *(d7 + j) = color[j];
+ *(d8 + j) = color[j];
+ }
+
+ buf += 8 * pixelSize;
+ }
+
+ for (int i = 0; i < block2; i++) {
+ memcpy(buf, color, pixelSize);
+ buf += pixelSize;
+ }
+}
+
+
template<class MaskGenerator, Vc::Implementation _impl>
void KisBrushMaskVectorApplicator<MaskGenerator, _impl>::processVector(const QRect &rect)
{
@@ -121,6 +203,15 @@ void KisBrushMaskVectorApplicator<MaskGenerator, _impl>::processVector(const QRe
for (int y = rect.y(); y < rect.y() + rect.height(); y++) {
+ // TODO: port to Vc
+ if (m_d->fillColor) {
+ if (m_d->pixelSize == 4) {
+ fillPixelOptimized_4bytes(m_d->fillColor, dabPointer, width);
+ } else {
+ fillPixelOptimized_general(m_d->fillColor, dabPointer, width, m_d->pixelSize);
+ }
+ }
+
processor.template process<_impl>(buffer, simdWidth, y, m_d->cosa, m_d->sina, m_d->centerX, m_d->centerY);
if (m_d->randomness != 0.0 || m_d->density != 1.0) {
@@ -204,6 +295,10 @@ void KisBrushMaskScalarApplicator<MaskGenerator, _impl>::processScalar(const QRe
}
}
+ if (m_d->fillColor) {
+ memcpy(dabPointer, m_d->fillColor, m_d->pixelSize);
+ }
+
m_d->colorSpace->applyAlphaU8Mask(dabPointer, &alphaValue, 1);
dabPointer += m_d->pixelSize;
}//endfor x
diff --git a/libs/image/kis_fixed_paint_device.cpp b/libs/image/kis_fixed_paint_device.cpp
index 2f8b446..cb02c3f 100644
--- a/libs/image/kis_fixed_paint_device.cpp
+++ b/libs/image/kis_fixed_paint_device.cpp
@@ -45,7 +45,16 @@ KisFixedPaintDevice& KisFixedPaintDevice::operator=(const KisFixedPaintDevice& r
{
m_bounds = rhs.m_bounds;
m_colorSpace = rhs.m_colorSpace;
- m_data = rhs.m_data;
+
+
+ const int referenceSize = m_bounds.height() * m_bounds.width() * pixelSize();
+
+ if (m_data.size() >= referenceSize) {
+ memcpy(m_data.data(), rhs.m_data.data(), referenceSize);
+ } else {
+ m_data = rhs.m_data;
+ }
+
return *this;
}
@@ -79,14 +88,33 @@ bool KisFixedPaintDevice::initialize(quint8 defaultValue)
return true;
}
+void KisFixedPaintDevice::reallocateDataWithoutInitialization()
+{
+ m_data.resize(m_bounds.height() * m_bounds.width() * pixelSize());
+}
+
+void KisFixedPaintDevice::lazyGrowDataWithoutInitialization()
+{
+ const int referenceSize = m_bounds.height() * m_bounds.width() * pixelSize();
+
+ if (m_data.size() < referenceSize) {
+ m_data.resize(referenceSize);
+ }
+}
+
quint8* KisFixedPaintDevice::data()
{
- return m_data.data();
+ return (quint8*) m_data.data();
+}
+
+const quint8 *KisFixedPaintDevice::constData() const
+{
+ return (const quint8*) m_data.constData();
}
quint8* KisFixedPaintDevice::data() const
{
- return const_cast<quint8*>(m_data.data());
+ return const_cast<quint8*>((quint8*)m_data.data());
}
void KisFixedPaintDevice::convertTo(const KoColorSpace* dstColorSpace,
@@ -97,9 +125,12 @@ void KisFixedPaintDevice::convertTo(const KoColorSpace* dstColorSpace,
return;
}
quint32 size = m_bounds.width() * m_bounds.height();
- QVector<quint8> dstData(size * dstColorSpace->pixelSize());
+ QByteArray dstData;
- m_colorSpace->convertPixelsTo(data(), dstData.data(),
+ // make sure that we are not initializing the destination pixels!
+ dstData.resize(size * dstColorSpace->pixelSize());
+
+ m_colorSpace->convertPixelsTo(constData(), (quint8*)dstData.data(),
dstColorSpace,
size,
renderingIntent,
@@ -107,7 +138,6 @@ void KisFixedPaintDevice::convertTo(const KoColorSpace* dstColorSpace,
m_colorSpace = dstColorSpace;
m_data = dstData;
-
}
void KisFixedPaintDevice::convertFromQImage(const QImage& _image, const QString &srcProfileName)
@@ -118,7 +148,7 @@ void KisFixedPaintDevice::convertFromQImage(const QImage& _image, const QString
image = image.convertToFormat(QImage::Format_ARGB32);
}
setRect(image.rect());
- initialize();
+ reallocateDataWithoutInitialization();
// Don't convert if not no profile is given and both paint dev and qimage are rgba.
if (srcProfileName.isEmpty() && colorSpace()->id() == "RGBA") {
@@ -158,15 +188,15 @@ QImage KisFixedPaintDevice::convertToQImage(const KoColorProfile * dstProfile,
return QImage();
if (QRect(x1, y1, w, h) == m_bounds) {
- return colorSpace()->convertToQImage(data(), w, h, dstProfile,
+ return colorSpace()->convertToQImage(constData(), w, h, dstProfile,
intent, conversionFlags);
} else {
try {
// XXX: fill the image row by row!
- int pSize = pixelSize();
- int deviceWidth = m_bounds.width();
+ const int pSize = pixelSize();
+ const int deviceWidth = m_bounds.width();
quint8* newData = new quint8[w * h * pSize];
- quint8* srcPtr = data() + x1 * pSize + y1 * deviceWidth * pSize;
+ const quint8* srcPtr = constData() + x1 * pSize + y1 * deviceWidth * pSize;
quint8* dstPtr = newData;
// copy the right area out of the paint device into data
for (int row = 0; row < h; row++) {
@@ -204,7 +234,7 @@ void KisFixedPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const qui
{
if (m_data.isEmpty() || m_bounds.isEmpty()) {
setRect(QRect(x, y, w, h));
- initialize();
+ reallocateDataWithoutInitialization();
}
QRect rc(x, y, w, h);
@@ -245,18 +275,16 @@ void KisFixedPaintDevice::readBytes(quint8* dstData, qint32 x, qint32 y, qint32
return;
}
- quint8 pixelSize = m_colorSpace->pixelSize();
- quint8* dabPointer = data();
+ const int pixelSize = m_colorSpace->pixelSize();
+ const quint8* dabPointer = constData();
if (rc == m_bounds) {
- memcpy(dstData, dabPointer, pixelSize * w * h);
- }
- else
- {
- int deviceWidth = bounds().width();
- quint8* rowPointer = dabPointer + ((y - bounds().y()) * deviceWidth + (x - bounds().x())) * pixelSize;
+ memcpy(dstData, dabPointer, pixelSize * w * h);
+ } else {
+ int deviceWidth = m_bounds.width();
+ const quint8* rowPointer = dabPointer + ((y - bounds().y()) * deviceWidth + (x - bounds().x())) * pixelSize;
for (int row = 0; row < h; row++) {
- memcpy(dstData,rowPointer, w * pixelSize);
+ memcpy(dstData, rowPointer, w * pixelSize);
rowPointer += deviceWidth * pixelSize;
dstData += w * pixelSize;
}
@@ -281,6 +309,8 @@ void KisFixedPaintDevice::mirror(bool horizontal, bool vertical)
quint8 * mirror = 0;
for (int y = 0; y < h ; y++){
+ // TODO: implement better flipping of the data
+
memcpy(row, dabPointer, rowSize);
mirror = row;
mirror += (w-1) * pixelSize;
diff --git a/libs/image/kis_fixed_paint_device.h b/libs/image/kis_fixed_paint_device.h
index f09337b..c20c9d8 100644
--- a/libs/image/kis_fixed_paint_device.h
+++ b/libs/image/kis_fixed_paint_device.h
@@ -92,11 +92,16 @@ public:
*/
bool initialize(quint8 defaultValue = 0);
+ void reallocateDataWithoutInitialization();
+ void lazyGrowDataWithoutInitialization();
+
/**
* @return a pointer to the beginning of the data associated with this fixed paint device.
*/
quint8* data();
+ const quint8* constData() const;
+
quint8* data() const;
/**
@@ -180,7 +185,7 @@ private:
const KoColorSpace* m_colorSpace;
QRect m_bounds;
- QVector<quint8> m_data;
+ QByteArray m_data;
};
diff --git a/libs/image/kis_painter_blt_multi_fixed.cpp b/libs/image/kis_painter_blt_multi_fixed.cpp
index 241e3df..5db6f4d 100644
--- a/libs/image/kis_painter_blt_multi_fixed.cpp
+++ b/libs/image/kis_painter_blt_multi_fixed.cpp
@@ -67,7 +67,7 @@ void KisPainter::Private::applyDevice(const QRect &applyRect,
const int dabX = dstX - dabRect.x();
const int dabY = dstY - dabRect.y();
- localParamInfo.srcRowStart = dab.device->data() + dabX * pixelSize + dabY * dabRowStride;
+ localParamInfo.srcRowStart = dab.device->constData() + dabX * pixelSize + dabY * dabRowStride;
localParamInfo.srcRowStride = dabRowStride;
localParamInfo.setOpacityAndAverage(dab.opacity, dab.averageOpacity);
localParamInfo.flow = dab.flow;
@@ -131,7 +131,7 @@ void KisPainter::Private::applyDeviceWithSelection(const QRect &applyRect,
const int dabX = dstX - dabRect.x();
const int dabY = dstY - dabRect.y();
- localParamInfo.srcRowStart = dab.device->data() + dabX * pixelSize + dabY * dabRowStride;
+ localParamInfo.srcRowStart = dab.device->constData() + dabX * pixelSize + dabY * dabRowStride;
localParamInfo.srcRowStride = dabRowStride;
localParamInfo.setOpacityAndAverage(dab.opacity, dab.averageOpacity);
localParamInfo.flow = dab.flow;
diff --git a/plugins/paintops/defaultpaintops/brush/KisDabRenderingJob.cpp b/plugins/paintops/defaultpaintops/brush/KisDabRenderingJob.cpp
index 9f7a5d5..cd54ace 100644
--- a/plugins/paintops/defaultpaintops/brush/KisDabRenderingJob.cpp
+++ b/plugins/paintops/defaultpaintops/brush/KisDabRenderingJob.cpp
@@ -97,7 +97,8 @@ int KisDabRenderingJob::executeOneJob(KisDabRenderingJob *job)
if (!job->postprocessedDevice ||
*job->originalDevice->colorSpace() != *job->postprocessedDevice->colorSpace()) {
- job->postprocessedDevice = new KisFixedPaintDevice(*job->originalDevice);
+ job->postprocessedDevice = job->parentQueue->fetchCachedPaintDevce();
+ *job->postprocessedDevice = *job->originalDevice;
} else {
*job->postprocessedDevice = *job->originalDevice;
}
diff --git a/plugins/paintops/defaultpaintops/brush/KisDabRenderingQueue.cpp b/plugins/paintops/defaultpaintops/brush/KisDabRenderingQueue.cpp
index 0b55834..0c9b661 100644
--- a/plugins/paintops/defaultpaintops/brush/KisDabRenderingQueue.cpp
+++ b/plugins/paintops/defaultpaintops/brush/KisDabRenderingQueue.cpp
@@ -296,8 +296,10 @@ void KisDabRenderingQueue::Private::cleanPaintedDabs()
if (sourceJob >= 1) {
// recycle and remove first 'sourceJob' jobs
+ // cache unique 'original' devices
for (auto it = jobs.begin(); it != jobs.begin() + sourceJob; ++it) {
- if (it->job.postprocessedDevice != it->job.originalDevice) {
+ if (it->job.type == KisDabRenderingJob::Dab &&
+ it->job.postprocessedDevice != it->job.originalDevice) {
cachedPaintDevices << it->job.originalDevice;
it->job.originalDevice = 0;
}
diff --git a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
index e6e8185..9ad5c80 100644
--- a/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
+++ b/plugins/paintops/defaultpaintops/brush/kis_brushop.cpp
@@ -226,6 +226,8 @@ void KisBrushOp::addMirroringJobs(Qt::Orientation direction,
int KisBrushOp::doAsyncronousUpdate(QVector<KisRunnableStrokeJobData*> &jobs)
{
+ // FIXME: add mutex for entering the function from concurrent jobs!
+
if (!m_updateSharedState && m_dabExecutor->hasPreparedDabs()) {
m_updateSharedState = toQShared(new UpdateSharedState());
dkazakov edited the content of this paste. (Show Details)Oct 10 2017, 1:50 PM
dkazakov changed the title of this paste from untitled to Masterwork From Distant Lands.
dkazakov updated the paste's language from autodetect to autodetect.