Commit 59aa2bbc authored by Boudewijn Rempt's avatar Boudewijn Rempt

Merge branch 'master' into rempt/T379-resource-management

parents 0da7f65c d183945d
......@@ -20,6 +20,8 @@
#define __KIS_CACHED_PAINT_DEVICE_H
#include "tiles3/kis_lockless_stack.h"
#include "kis_paint_device.h"
#include "kis_selection.h"
class KisCachedPaintDevice
{
......
......@@ -110,7 +110,12 @@ void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const Ko
}
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern)
void KisFillPainter::fillRect(const QRect &rc, const KoPattern *pattern, const QPoint &offset)
{
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), pattern, offset);
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern, const QPoint &offset)
{
if (!pattern) return;
if (!pattern->valid()) return;
......@@ -121,42 +126,44 @@ void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const Ko
KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->compositionSourceColorSpace(), pattern->name());
patternLayer->convertFromQImage(pattern->pattern(), 0);
fillRect(x1, y1, w, h, patternLayer, QRect(0, 0, pattern->width(), pattern->height()));
if (!offset.isNull()) {
patternLayer->moveTo(offset);
}
fillRect(x1, y1, w, h, patternLayer, QRect(offset.x(), offset.y(), pattern->width(), pattern->height()));
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect)
{
Q_ASSERT(deviceRect.x() == 0); // the case x,y != 0,0 is not yet implemented
Q_ASSERT(deviceRect.y() == 0);
int sx, sy, sw, sh;
const QRect &patternRect = deviceRect;
const QRect fillRect(x1, y1, w, h);
int y = y1;
auto toPatternLocal = [](int value, int offset, int width) {
const int normalizedValue = value - offset;
return offset + (normalizedValue >= 0 ?
normalizedValue % width :
width - (-normalizedValue - 1) % width - 1);
};
if (y >= 0) {
sy = y % deviceRect.height();
} else {
sy = deviceRect.height() - (((-y - 1) % deviceRect.height()) + 1);
}
int dstY = fillRect.y();
while (dstY <= fillRect.bottom()) {
const int dstRowsRemaining = fillRect.bottom() - dstY + 1;
while (y < y1 + h) {
sh = qMin((y1 + h) - y, deviceRect.height() - sy);
const int srcY = toPatternLocal(dstY, patternRect.y(), patternRect.height());
const int height = qMin(patternRect.height() - srcY + patternRect.y(), dstRowsRemaining);
int x = x1;
int dstX = fillRect.x();
while (dstX <= fillRect.right()) {
const int dstColumnsRemaining = fillRect.right() - dstX + 1;
if (x >= 0) {
sx = x % deviceRect.width();
} else {
sx = deviceRect.width() - (((-x - 1) % deviceRect.width()) + 1);
}
const int srcX = toPatternLocal(dstX, patternRect.x(), patternRect.width());
const int width = qMin(patternRect.width() - srcX + patternRect.x(), dstColumnsRemaining);
while (x < x1 + w) {
sw = qMin((x1 + w) - x, deviceRect.width() - sx);
bitBlt(dstX, dstY, device, srcX, srcY, width, height);
bitBlt(x, y, device, sx, sy, sw, sh);
x += sw; sx = 0;
dstX += width;
}
y += sh; sy = 0;
dstY += height;
}
addDirtyRect(QRect(x1, y1, w, h));
......
......@@ -102,7 +102,7 @@ public:
* Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
* entire rectangle.
*/
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern);
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPattern * pattern, const QPoint &offset = QPoint());
/**
* Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
......@@ -113,7 +113,7 @@ public:
/**
* Overloaded version of the above function.
*/
void fillRect(const QRect& rc, const KoPattern * pattern);
void fillRect(const QRect& rc, const KoPattern * pattern, const QPoint &offset = QPoint());
/**
* Fill the specified area with the output of the generator plugin that is configured
......@@ -269,11 +269,7 @@ void KisFillPainter::fillRect(const QRect& rc, const KoColor& c, quint8 opacity)
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), c, opacity);
}
inline
void KisFillPainter::fillRect(const QRect& rc, const KoPattern* pattern)
{
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), pattern);
}
inline
void KisFillPainter::setFillThreshold(int threshold)
......
......@@ -80,7 +80,7 @@ QRect KisLayerStyleFilterProjectionPlane::recalculate(const QRect& rect, KisNode
void KisLayerStyleFilterProjectionPlane::apply(KisPainter *painter, const QRect &rect)
{
m_d->projection.apply(painter->device(), rect);
m_d->projection.apply(painter->device(), rect, m_d->environment.data());
}
KisPaintDeviceList KisLayerStyleFilterProjectionPlane::getLodCapableDevices() const
......
......@@ -416,19 +416,18 @@ void KisLsBevelEmbossFilter::applyBevelEmboss(KisPaintDeviceSP srcDevice,
//selection->convertToQImage(0, QRect(0,0,300,300)).save("4_shadows_sel.png");
{
KisPaintDeviceSP dstDevice = dst->getProjection("00_bevel_shadow", config->shadowBlendMode(), srcDevice);
KisPaintDeviceSP dstDevice = dst->getProjection("00_bevel_shadow",
config->shadowBlendMode(),
config->shadowOpacity(),
QBitArray(),
srcDevice);
const KoColor fillColor(config->shadowColor(), dstDevice->colorSpace());
const QRect &fillRect = d.shadowHighlightsFinalRect;
KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace());
fillDevice->setDefaultPixel(fillColor);
KisPainter gc(dstDevice);
gc.setSelection(baseSelection);
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, config->shadowOpacity(), QBitArray());
gc.bitBlt(fillRect.topLeft(), fillDevice, fillRect);
gc.end();
KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection);
}
selection->clear();
......@@ -439,18 +438,18 @@ void KisLsBevelEmbossFilter::applyBevelEmboss(KisPaintDeviceSP srcDevice,
//selection->convertToQImage(0, QRect(0,0,300,300)).save("5_highlights_sel.png");
{
KisPaintDeviceSP dstDevice = dst->getProjection("01_bevel_highlight", config->highlightBlendMode(), srcDevice);
KisPaintDeviceSP dstDevice = dst->getProjection("01_bevel_highlight",
config->highlightBlendMode(),
config->highlightOpacity(),
QBitArray(),
srcDevice);
const KoColor fillColor(config->highlightColor(), dstDevice->colorSpace());
const QRect &fillRect = d.shadowHighlightsFinalRect;
KisPaintDeviceSP fillDevice = new KisPaintDevice(dstDevice->colorSpace());
fillDevice->setDefaultPixel(fillColor);
KisPainter gc(dstDevice);
gc.setSelection(baseSelection);
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, config->highlightOpacity(), QBitArray());
gc.bitBlt(fillRect.topLeft(), fillDevice, fillRect);
gc.end();
KisPainter::copyAreaOptimized(fillRect.topLeft(), fillDevice, dstDevice, fillRect, baseSelection);
}
}
......
......@@ -62,39 +62,29 @@ void KisLsOverlayFilter::applyOverlay(KisPaintDeviceSP srcDevice,
{
if (applyRect.isEmpty()) return;
KisPaintDeviceSP tempDevice = new KisPaintDevice(srcDevice->colorSpace());
KisPaintDeviceSP overlayDevice = m_cachedDevices.getDevice(srcDevice);
KisLsUtils::fillOverlayDevice(overlayDevice, applyRect, config, env);
{
// Create overlay device
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = 255.0 / 100.0 * config->opacity();
KisPaintDeviceSP fillDevice = new KisPaintDevice(srcDevice->colorSpace());
KisLsUtils::fillOverlayDevice(fillDevice, applyRect, config, env);
KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(),
compositeOp,
opacityU8,
QBitArray(),
srcDevice);
KisPainter gc(tempDevice);
gc.setCompositeOp(COMPOSITE_OVER);
gc.bitBlt(applyRect.topLeft(), srcDevice, applyRect);
KisPainter::copyAreaOptimized(applyRect.topLeft(), srcDevice, dstDevice, applyRect);
QBitArray channelFlags = srcDevice->colorSpace()->channelFlags(true, false);
gc.setChannelFlags(channelFlags);
gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect);
gc.end();
}
{
// Paint over destination
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = 255.0 / 100.0 * config->opacity();
KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(), compositeOp, srcDevice);
KisPainter gc(dstDevice);
gc.setCompositeOp(COMPOSITE_OVER);
KisPainter gc(dstDevice);
const QBitArray channelFlags = srcDevice->colorSpace()->channelFlags(true, false);
gc.setChannelFlags(channelFlags);
gc.bitBlt(applyRect.topLeft(), overlayDevice, applyRect);
gc.end();
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.bitBlt(applyRect.topLeft(), tempDevice, applyRect);
gc.end();
}
m_cachedDevices.putDevice(overlayDevice);
}
const psd_layer_effects_overlay_base*
......
......@@ -23,6 +23,7 @@
#include "kis_layer_style_filter.h"
#include <kritaimage_export.h>
#include "kis_cached_paint_device.h"
struct psd_layer_effects_overlay_base;
......@@ -59,6 +60,7 @@ private:
private:
Mode m_mode;
mutable KisCachedPaintDevice m_cachedDevices;
};
#endif
......@@ -111,15 +111,13 @@ void KisLsStrokeFilter::applyStroke(KisPaintDeviceSP srcDevice,
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = 255.0 / 100.0 * config->opacity();
KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(), compositeOp, srcDevice);
dstDevice->clear(applyRect);
KisPaintDeviceSP dstDevice = dst->getProjection(KisMultipleProjection::defaultProjectionId(),
compositeOp,
opacityU8,
QBitArray(),
srcDevice);
KisPainter gc(dstDevice);
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.setSelection(baseSelection);
gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect);
KisPainter::copyAreaOptimized(applyRect.topLeft(), fillDevice, dstDevice, applyRect, baseSelection);
}
void KisLsStrokeFilter::processDirectly(KisPaintDeviceSP src,
......
......@@ -405,11 +405,8 @@ namespace KisLsUtils
KisFillPainter gc(fillDevice);
gc.fillRect(fillRect.x(), fillRect.y(),
fillRect.width(), fillRect.height(), pattern);
fillRect.width(), fillRect.height(), pattern, -patternOffset);
gc.end();
fillDevice->setX(-patternOffset.x());
fillDevice->setY(-patternOffset.y());
}
void fillOverlayDevice(KisPaintDeviceSP fillDevice,
......@@ -550,12 +547,11 @@ namespace KisLsUtils
const QRect effectRect(dstRect);
const QString compositeOp = config->blendMode();
const quint8 opacityU8 = 255.0 / 100.0 * config->opacity();
KisPaintDeviceSP dstDevice = dst->getProjection(projectionId, compositeOp, srcDevice);
KisPaintDeviceSP dstDevice = dst->getProjection(projectionId, compositeOp, opacityU8, QBitArray(), srcDevice);
if (config->fillType() == psd_fill_solid_color) {
KisFillPainter gc(dstDevice);
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.setCompositeOp(COMPOSITE_COPY);
gc.setSelection(baseSelection);
gc.fillSelection(effectRect, effectColor);
gc.end();
......@@ -566,21 +562,12 @@ namespace KisLsUtils
return;
}
KisPaintDeviceSP overlayDevice =
new KisPaintDevice(dstDevice->colorSpace());
QVector<KoColor> table(256);
Private::getGradientTable(config->gradient().data(), &table, dstDevice->colorSpace());
Private::applyGradient(overlayDevice, baseSelection->pixelSelection(),
Private::applyGradient(dstDevice, baseSelection->pixelSelection(),
effectRect, table,
true, config->jitter(), env);
KisPainter gc(dstDevice);
gc.setCompositeOp(COMPOSITE_OVER);
env->setupFinalPainter(&gc, opacityU8, QBitArray());
gc.bitBlt(effectRect.topLeft(), overlayDevice, effectRect);
gc.end();
}
//dstDevice->convertToQImage(0, QRect(0,0,300,300)).save("6_device_shadow.png");
......
......@@ -26,11 +26,14 @@
#include "kis_painter.h"
#include "kis_paint_device.h"
#include "kis_layer_style_filter_environment.h"
struct ProjectionStruct {
KisPaintDeviceSP device;
QString compositeOpId;
quint8 opacity = OPACITY_OPAQUE_U8;
QBitArray channelFlags;
};
typedef QMap<QString, ProjectionStruct> PlanesMap;
......@@ -56,7 +59,11 @@ QString KisMultipleProjection::defaultProjectionId()
return "00_default";
}
KisPaintDeviceSP KisMultipleProjection::getProjection(const QString &id, const QString &compositeOpId, KisPaintDeviceSP prototype)
KisPaintDeviceSP KisMultipleProjection::getProjection(const QString &id,
const QString &compositeOpId,
quint8 opacity,
const QBitArray &channelFlags,
KisPaintDeviceSP prototype)
{
QReadLocker readLocker(&m_d->lock);
......@@ -64,6 +71,8 @@ KisPaintDeviceSP KisMultipleProjection::getProjection(const QString &id, const Q
if (constIt == m_d->planes.constEnd() ||
constIt->compositeOpId != compositeOpId ||
constIt->opacity != opacity ||
constIt->channelFlags != channelFlags ||
*constIt->device->colorSpace() != *prototype->colorSpace()) {
readLocker.unlock();
......@@ -77,12 +86,16 @@ KisPaintDeviceSP KisMultipleProjection::getProjection(const QString &id, const Q
plane.device = new KisPaintDevice(prototype->colorSpace());
plane.device->prepareClone(prototype);
plane.compositeOpId = compositeOpId;
plane.opacity = opacity;
plane.channelFlags = channelFlags;
writeIt = m_d->planes.insert(id, plane);
} else if (writeIt->compositeOpId != compositeOpId ||
*writeIt->device->colorSpace() != *prototype->colorSpace()) {
writeIt->device->prepareClone(prototype);
writeIt->compositeOpId = compositeOpId;
writeIt->opacity = opacity;
writeIt->channelFlags = channelFlags;
}
return writeIt->device;
......@@ -116,7 +129,7 @@ void KisMultipleProjection::clear(const QRect &rc)
}
}
void KisMultipleProjection::apply(KisPaintDeviceSP dstDevice, const QRect &rect)
void KisMultipleProjection::apply(KisPaintDeviceSP dstDevice, const QRect &rect, KisLayerStyleFilterEnvironment *env)
{
QReadLocker readLocker(&m_d->lock);
......@@ -126,6 +139,7 @@ void KisMultipleProjection::apply(KisPaintDeviceSP dstDevice, const QRect &rect)
for (; it != end; ++it) {
KisPainter gc(dstDevice);
gc.setCompositeOp(it->compositeOpId);
env->setupFinalPainter(&gc, it->opacity, it->channelFlags);
gc.bitBlt(rect.topLeft(), it->device, rect);
}
}
......
......@@ -23,6 +23,7 @@
#include "kis_types.h"
#include "kritaimage_export.h"
class KisLayerStyleFilterEnvironment;
class KRITAIMAGE_EXPORT KisMultipleProjection
{
......@@ -32,13 +33,13 @@ public:
static QString defaultProjectionId();
KisPaintDeviceSP getProjection(const QString &id, const QString &compositeOpId, KisPaintDeviceSP prototype);
KisPaintDeviceSP getProjection(const QString &id, const QString &compositeOpId, quint8 opacity, const QBitArray &channelFlags, KisPaintDeviceSP prototype);
void freeProjection(const QString &id);
void freeAllProjections();
void clear(const QRect &rc);
void apply(KisPaintDeviceSP dstDevice, const QRect &rect);
void apply(KisPaintDeviceSP dstDevice, const QRect &rect, KisLayerStyleFilterEnvironment *env);
KisPaintDeviceList getLodCapableDevices() const;
......
......@@ -149,5 +149,60 @@ void KisFillPainterTest::benchmarkFillingScanlineSelection()
"heavy_labyrinth_top_left_selection"));
}
void KisFillPainterTest::testPatternFill()
{
const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
KisPaintDeviceSP dst = new KisPaintDevice(cs);
KisPaintDeviceSP pattern = new KisPaintDevice(cs);
pattern->fill(QRect(0,0,32,32), KoColor(Qt::red, cs));
pattern->fill(QRect(32,32,32,32), KoColor(Qt::red, cs));
pattern->fill(QRect(32,0,32,32), KoColor(Qt::yellow, cs));
pattern->fill(QRect(0,32,32,32), KoColor(Qt::white, cs));
const QRect fillRect(-128,-128,384,384);
KisFillPainter painter(dst);
{ // fill aligned
const QRect patternRect = pattern->exactBounds();
painter.fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), pattern, patternRect);
dst->fill(QRect(0,0,10,10), KoColor(Qt::black, cs));
QImage resultImage =
dst->convertToQImage(0,
fillRect.x(), fillRect.y(),
fillRect.width(), fillRect.height());
QVERIFY(TestUtil::checkQImage(resultImage,
"fill_painter",
"patterns_fill_",
"null_origin"));
}
{ // fill with offset
dst->clear();
pattern->setX(7);
pattern->setY(-13);
const QRect patternRect = pattern->exactBounds();
painter.fillRect(fillRect.x(), fillRect.y(), fillRect.width(), fillRect.height(), pattern, patternRect);
dst->fill(QRect(0,0,10,10), KoColor(Qt::black, cs));
QImage resultImage =
dst->convertToQImage(0,
fillRect.x(), fillRect.y(),
fillRect.width(), fillRect.height());
QVERIFY(TestUtil::checkQImage(resultImage,
"fill_painter",
"patterns_fill_",
"custom_origin"));
}
}
QTEST_MAIN(KisFillPainterTest)
......@@ -36,6 +36,8 @@ private Q_SLOTS:
void benchmarkFillPainterOffsetCompositioning();
void benchmarkFillingScanlineColor();
void benchmarkFillingScanlineSelection();
void testPatternFill();
};
#endif
......@@ -113,7 +113,7 @@ void testDropShadowImpl(const TestConfig &config,
KisPaintDeviceSP dst = new KisPaintDevice(cs);
projection.apply(dst, dstRect);
projection.apply(dst, dstRect, &env);
QImage resultImage =
dst->convertToQImage(0, dstRect);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment