...
 
Commits (81)
......@@ -37,7 +37,7 @@
void KisFloodFillBenchmark::initTestCase()
{
m_colorSpace = KoColorSpaceRegistry::instance()->rgb8();
m_device = new KisPaintDevice(m_colorSpace);
m_deviceStandardFloodFill = new KisPaintDevice(m_colorSpace);
m_color = KoColor(m_colorSpace);
QColor qcolor(Qt::red);
......@@ -47,11 +47,11 @@ void KisFloodFillBenchmark::initTestCase()
int tileh = 56;
m_color.fromQColor(QColor(0,0,0,0)); // default pixel
m_device->fill( 0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT,m_color.data() );
m_deviceStandardFloodFill->fill( 0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT,m_color.data() );
// fill the image with red ellipses (like some random dabs)
m_color.fromQColor(Qt::red);
KisPainter painter(m_device);
KisPainter painter(m_deviceStandardFloodFill);
painter.setFillStyle(KisPainter::FillStyleForegroundColor);
painter.setPaintColor(m_color);
......@@ -64,6 +64,23 @@ void KisFloodFillBenchmark::initTestCase()
painter.paintEllipse(x+ 10, y+ 10, tilew, tileh);
}
// copy to other tests
m_deviceWithoutSelectionAsBoundary = new KisPaintDevice(m_colorSpace);
m_deviceWithSelectionAsBoundary = new KisPaintDevice(m_colorSpace);
KisPainter::copyAreaOptimized(QPoint(), m_deviceStandardFloodFill,
m_deviceWithoutSelectionAsBoundary, m_deviceWithoutSelectionAsBoundary->exactBounds());
KisPainter::copyAreaOptimized(QPoint(), m_deviceStandardFloodFill,
m_deviceWithSelectionAsBoundary, m_deviceWithSelectionAsBoundary->exactBounds());
//m_deviceWithoutSelectionAsBoundary = m_deviceStandardFloodFill->
const KoColorSpace* alphacs = KoColorSpaceRegistry::instance()->alpha8();
KoColor defaultSelected = KoColor(alphacs);
defaultSelected.fromQColor(QColor(255, 255, 255));
m_existingSelection = new KisPaintDevice(alphacs);
m_existingSelection->fill(0, 0, GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT, defaultSelected.data());
}
......@@ -76,7 +93,7 @@ void KisFloodFillBenchmark::benchmarkFlood()
QBENCHMARK
{
KisFillPainter fillPainter(m_device);
KisFillPainter fillPainter(m_deviceStandardFloodFill);
//setupPainter(&fillPainter);
fillPainter.setPaintColor( fg );
fillPainter.setBackgroundColor( bg );
......@@ -93,7 +110,7 @@ void KisFloodFillBenchmark::benchmarkFlood()
fillPainter.setHeight(GMP_IMAGE_HEIGHT);
// fill twice
fillPainter.fillColor(1, 1, m_device);
fillPainter.fillColor(1, 1, m_deviceStandardFloodFill);
fillPainter.deleteTransaction();
}
......@@ -103,6 +120,66 @@ void KisFloodFillBenchmark::benchmarkFlood()
//out.save("fill_output.png");
}
void KisFloodFillBenchmark::benchmarkFloodWithoutSelectionAsBoundary()
{
KoColor fg(m_colorSpace);
KoColor bg(m_colorSpace);
fg.fromQColor(Qt::blue);
bg.fromQColor(Qt::black);
QBENCHMARK
{
KisFillPainter fillPainter(m_deviceWithoutSelectionAsBoundary);
fillPainter.setPaintColor( fg );
fillPainter.setBackgroundColor( bg );
fillPainter.beginTransaction(kundo2_noi18n("Flood Fill"));
fillPainter.setOpacity(OPACITY_OPAQUE_U8);
// default
fillPainter.setFillThreshold(15);
fillPainter.setCompositeOp(COMPOSITE_OVER);
fillPainter.setCareForSelection(true);
fillPainter.setWidth(GMP_IMAGE_WIDTH);
fillPainter.setHeight(GMP_IMAGE_HEIGHT);
fillPainter.setUseSelectionAsBoundary(false);
fillPainter.createFloodSelection(1, 1, m_deviceWithoutSelectionAsBoundary, m_existingSelection);
fillPainter.deleteTransaction();
}
}
void KisFloodFillBenchmark::benchmarkFloodWithSelectionAsBoundary()
{
KoColor fg(m_colorSpace);
KoColor bg(m_colorSpace);
fg.fromQColor(Qt::blue);
bg.fromQColor(Qt::black);
QBENCHMARK
{
KisFillPainter fillPainter(m_deviceWithSelectionAsBoundary);
fillPainter.setPaintColor( fg );
fillPainter.setBackgroundColor( bg );
fillPainter.beginTransaction(kundo2_noi18n("Flood Fill"));
fillPainter.setOpacity(OPACITY_OPAQUE_U8);
// default
fillPainter.setFillThreshold(15);
fillPainter.setCompositeOp(COMPOSITE_OVER);
fillPainter.setCareForSelection(true);
fillPainter.setWidth(GMP_IMAGE_WIDTH);
fillPainter.setHeight(GMP_IMAGE_HEIGHT);
fillPainter.setUseSelectionAsBoundary(true);
fillPainter.createFloodSelection(1, 1, m_deviceWithSelectionAsBoundary, m_existingSelection);
fillPainter.deleteTransaction();
}
}
void KisFloodFillBenchmark::cleanupTestCase()
{
......
......@@ -33,7 +33,10 @@ class KisFloodFillBenchmark : public QObject
private:
const KoColorSpace * m_colorSpace;
KoColor m_color;
KisPaintDeviceSP m_device;
KisPaintDeviceSP m_deviceStandardFloodFill;
KisPaintDeviceSP m_deviceWithSelectionAsBoundary;
KisPaintDeviceSP m_deviceWithoutSelectionAsBoundary;
KisPaintDeviceSP m_existingSelection;
int m_startX;
int m_startY;
......@@ -42,6 +45,9 @@ private Q_SLOTS:
void cleanupTestCase();
void benchmarkFlood();
void benchmarkFloodWithoutSelectionAsBoundary();
void benchmarkFloodWithSelectionAsBoundary();
......
......@@ -104,7 +104,17 @@ if (APPLE)
set_target_properties(krita PROPERTIES MACOSX_BUNDLE_COPYRIGHT "GNU Public License, V2 or, at your option, any later version.")
endif ()
set(KRITAVERSION_SRCS kritaversion.cpp)
add_executable(krita_version ${KRITAVERSION_SRCS})
target_link_libraries(krita_version
PRIVATE
Qt5::Core
kritaversion
)
install(TARGETS krita ${INSTALL_TARGETS_DEFAULT_ARGS})
install(TARGETS krita_version ${INSTALL_TARGETS_DEFAULT_ARGS})
install(PROGRAMS org.kde.krita.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
install(FILES krita.action kritamenu.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions)
install(FILES org.kde.krita.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} )
......
......@@ -1815,6 +1815,16 @@
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="brushslider4">
<icon></icon>
<text>Brush option slider 4</text>
<whatsThis></whatsThis>
<toolTip>Brush option slider 4</toolTip>
<iconText>Brush option slider 4</iconText>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="mirror_actions">
<icon></icon>
......
/*
* Copyright (c) 1999 Matthias Elter <me@kde.org>
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2015 Boudewijn Rempt <boud@valdyas.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <QString>
#include <QTextStream>
#include <kritaversion.h>
QTextStream& qStdOut()
{
static QTextStream ts( stdout );
return ts;
}
extern "C" int main(int , char **)
{
qStdOut() << KRITA_VERSION_STRING << "\n";
return 0;
}
......@@ -211,7 +211,6 @@ set(kritaflake_SRCS
svg/parsers/SvgTransformParser.cpp
svg/SvgMeshGradient.cpp
svg/SvgMeshPatch.cpp
svg/SvgMeshStop.cpp
svg/SvgMeshArray.cpp
text/KoSvgText.cpp
......
......@@ -11,15 +11,12 @@ public:
, gradient(0)
{}
~Private() { delete gradient; }
SvgMeshGradient *gradient;
QTransform matrix;
};
KoMeshGradientBackground::~KoMeshGradientBackground()
{
delete d->gradient;
}
KoMeshGradientBackground::KoMeshGradientBackground(SvgMeshGradient *gradient, const QTransform &matrix)
: KoShapeBackground()
, d(new Private)
......@@ -29,6 +26,10 @@ KoMeshGradientBackground::KoMeshGradientBackground(SvgMeshGradient *gradient, co
Q_ASSERT(d->gradient);
}
KoMeshGradientBackground::~KoMeshGradientBackground()
{
}
void KoMeshGradientBackground::paint(QPainter &painter,
KoShapePaintingContext &context,
const QPainterPath &fillPath) const
......@@ -59,11 +60,11 @@ void KoMeshGradientBackground::fillPatch(QPainter &painter, const SvgMeshPatch *
delete p;
}
} else {
QPen pen(patch->getStop(SvgMeshPatch::Bottom)->color);
QPen pen(patch->getStop(SvgMeshPatch::Bottom).color);
painter.setPen(pen);
painter.drawPath(patch->getPath()->outline());
QBrush brush(patch->getStop(SvgMeshPatch::Bottom)->color);
QBrush brush(patch->getStop(SvgMeshPatch::Bottom).color);
painter.fillPath(patch->getPath()->outline(), brush);
}
}
......
......@@ -10,7 +10,7 @@ class KRITAFLAKE_EXPORT KoMeshGradientBackground : public KoShapeBackground
public:
KoMeshGradientBackground(SvgMeshGradient *gradient, const QTransform &matrix = QTransform());
~KoMeshGradientBackground();
void paint(QPainter &painter, KoShapePaintingContext &context, const QPainterPath &fillPath) const override;
void fillPatch(QPainter &painter, const SvgMeshPatch *patch, int i) const;
......
......@@ -83,7 +83,7 @@ void SvgGradientHelper::setGradient(QGradient * g)
void SvgGradientHelper::setMeshGradient(SvgMeshGradient *g)
{
m_meshgradient.reset(g);
m_meshgradient.reset(new SvgMeshGradient(*g));
}
QScopedPointer<SvgMeshGradient>& SvgGradientHelper::meshgradient()
......
......@@ -25,12 +25,14 @@ SvgMeshArray::SvgMeshArray()
{
}
SvgMeshArray::SvgMeshArray(SvgMeshArray& other)
SvgMeshArray::SvgMeshArray(const SvgMeshArray& other)
{
// FIXME The way SvgParser works with gradients is, it frequently destroys the objects holding reference to
// SvgMeshGradients, so rather than copying we move it. This certainly needs a design refactor.
m_array = std::move(other.m_array);
for (const auto& row: other.m_array) {
newRow();
for (const auto& patch: row) {
m_array.last().append(new SvgMeshPatch(*patch));
}
}
}
SvgMeshArray::~SvgMeshArray()
......@@ -124,10 +126,10 @@ SvgMeshStop SvgMeshArray::getStop(const SvgMeshPatch::Type edge, const int row,
&& row >= 0 && col >= 0);
SvgMeshPatch *patch = m_array[row][col];
SvgMeshStop *node = patch->getStop(edge);
SvgMeshStop node = patch->getStop(edge);
if (node != nullptr) {
return *node;
if (node.isValid()) {
return node;
}
switch (patch->countPoints()) {
......
......@@ -27,8 +27,7 @@ class KRITAFLAKE_EXPORT SvgMeshArray
public:
SvgMeshArray();
// MOVES the elements of m_array from other to this
SvgMeshArray(SvgMeshArray& other);
SvgMeshArray(const SvgMeshArray& other);
~SvgMeshArray();
......
......@@ -28,11 +28,6 @@ SvgMeshGradient::SvgMeshGradient(const SvgMeshGradient& other)
{
}
SvgMeshGradient::~SvgMeshGradient()
{
delete m_mesharray;
}
void SvgMeshGradient::setType(SvgMeshGradient::Type type)
{
m_type = type;
......@@ -48,7 +43,7 @@ bool SvgMeshGradient::isValid() const
return m_mesharray->numRows() != 0;
}
SvgMeshArray* SvgMeshGradient::getMeshArray() const
QScopedPointer<SvgMeshArray>& SvgMeshGradient::getMeshArray()
{
return m_mesharray;
}
......@@ -33,18 +33,17 @@ public:
SvgMeshGradient();
SvgMeshGradient(const SvgMeshGradient& other);
~SvgMeshGradient();
void setType(Type type);
SvgMeshGradient::Type type() const;
bool isValid() const;
SvgMeshArray* getMeshArray() const;
QScopedPointer<SvgMeshArray>& getMeshArray();
private:
Type m_type;
SvgMeshArray* m_mesharray;
QScopedPointer<SvgMeshArray> m_mesharray;
};
#endif // KISMESHGRADIENT_H
......@@ -32,20 +32,17 @@ SvgMeshPatch::SvgMeshPatch(QPointF startingPoint)
}
SvgMeshPatch::SvgMeshPatch(const SvgMeshPatch& other)
: m_newPath(other.m_newPath)
, m_startingPoint(other.m_startingPoint)
, m_nodes(other.m_nodes)
, m_path(static_cast<KoPathShape*>(other.m_path->cloneShape()))
{
}
SvgMeshPatch::~SvgMeshPatch()
{
for (auto &node: m_nodes.values()) {
delete node;
}
}
SvgMeshStop* SvgMeshPatch::getStop(SvgMeshPatch::Type type) const
SvgMeshStop SvgMeshPatch::getStop(SvgMeshPatch::Type type) const
{
if (m_nodes.find(type) == m_nodes.end())
return nullptr;
return SvgMeshStop();
return *m_nodes.find(type);
}
......@@ -170,10 +167,10 @@ void SvgMeshPatch::subdivide(QVector<SvgMeshPatch*>& subdivided) const
QList<QPointF> reversedMidVerSecond = midVer.second.controlPoints();
std::reverse(reversedMidVerSecond.begin(), reversedMidVerSecond.end());
QColor c1 = getStop(Top)->color;
QColor c2 = getStop(Right)->color;
QColor c3 = getStop(Bottom)->color;
QColor c4 = getStop(Left)->color;
QColor c1 = getStop(Top).color;
QColor c2 = getStop(Right).color;
QColor c3 = getStop(Bottom).color;
QColor c4 = getStop(Left).color;
QColor midc12 = midPointColor(c1, c2);
QColor midc23 = midPointColor(c2, c3);
QColor midc34 = midPointColor(c3, c4);
......@@ -219,7 +216,7 @@ void SvgMeshPatch::addStop(const QString& pathStr,
bool pathIncomplete,
QPointF lastPoint)
{
SvgMeshStop *node = new SvgMeshStop(color, m_startingPoint);
SvgMeshStop node(color, m_startingPoint);
m_nodes.insert(edge, node);
m_startingPoint = parseMeshPath(pathStr, pathIncomplete, lastPoint);
......@@ -227,7 +224,7 @@ void SvgMeshPatch::addStop(const QString& pathStr,
void SvgMeshPatch::addStop(const QList<QPointF>& pathPoints, QColor color, Type edge)
{
SvgMeshStop *stop = new SvgMeshStop(color, pathPoints.first());
SvgMeshStop stop(color, pathPoints.first());
m_nodes.insert(edge, stop);
if (edge == SvgMeshPatch::Top) {
......
......@@ -36,6 +36,8 @@ struct SvgMeshStop {
SvgMeshStop(QColor color, QPointF point)
: color(color), point(point)
{}
bool isValid() { return color.isValid(); }
};
......@@ -53,9 +55,8 @@ public:
SvgMeshPatch(QPointF startingPoint);
SvgMeshPatch(const SvgMeshPatch& other);
~SvgMeshPatch();
SvgMeshStop* getStop(Type type) const;
SvgMeshStop getStop(Type type) const;
/// Get a segment of the path in the meshpatch
KoPathSegment getPathSegment(Type type) const;
......@@ -92,7 +93,7 @@ private:
/// This is the starting point for each path
QPointF m_startingPoint;
QMap<Type, SvgMeshStop*> m_nodes;
QMap<Type, SvgMeshStop> m_nodes;
QScopedPointer<KoPathShape> m_path;
};
......
#include "SvgMeshStop.h"
SvgMeshStop::SvgMeshStop()
{
}
#ifndef SVGMESHSTOP_H
#define SVGMESHSTOP_H
class SvgMeshStop
{
public:
SvgMeshStop();
};
#endif // SVGMESHSTOP_H
......@@ -1115,7 +1115,6 @@ void SvgParser::applyFillStyle(KoShape *shape)
if (gradient->isMeshGradient()) {
QSharedPointer<KoMeshGradientBackground> bg;
// NOTE: this will MOVE SvgMeshPatch elements in mesharray
SvgMeshGradient *result = new SvgMeshGradient(*gradient->meshgradient());
// TODO handle transform
......
......@@ -54,22 +54,20 @@ void KoZoomTool::mouseMoveEvent(KoPointerEvent *event)
{
updateCursor(event->modifiers() & Qt::ControlModifier);
if (currentStrategy()) {
currentStrategy()->handleMouseMove(event->point, event->modifiers());
}
KoInteractionTool::mouseMoveEvent(event);
}
void KoZoomTool::keyPressEvent(QKeyEvent *event)
{
event->ignore();
updateCursor(event->modifiers() & Qt::ControlModifier);
KoInteractionTool::keyPressEvent(event);
}
void KoZoomTool::keyReleaseEvent(QKeyEvent *event)
{
event->ignore();
updateCursor(event->modifiers() & Qt::ControlModifier);
KoInteractionTool::keyReleaseEvent(event);
......
......@@ -179,11 +179,18 @@ KisPaintOpSettingsSP KisPaintOpSettings::createMaskingSettings() const
return maskingSettings;
}
bool KisPaintOpSettings::hasPatternSettings() const
{
return false;
}
QString KisPaintOpSettings::maskingBrushCompositeOp() const
{
return getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT);
}
KisPaintOpSettingsSP KisPaintOpSettings::clone() const
{
QString paintopID = getString("paintop");
......@@ -262,6 +269,14 @@ qreal KisPaintOpSettings::paintOpFlow()
return proxy->getDouble("FlowValue", 1.0);
}
qreal KisPaintOpSettings::paintOpPatternSize()
{
KisLockedPropertiesProxySP proxy(
KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this));
return proxy->getDouble("Texture/Pattern/Scale", 0.5);
}
QString KisPaintOpSettings::paintOpCompositeOp()
{
KisLockedPropertiesProxySP proxy(
......
......@@ -243,6 +243,11 @@ public:
*/
virtual qreal paintOpSize() const = 0;
/**
* @return pattern size saved in the properties
*/
virtual qreal paintOpPatternSize();
void setEraserMode(bool value);
bool eraserMode();
......@@ -331,6 +336,8 @@ public:
*/
QString maskingBrushCompositeOp() const;
virtual bool hasPatternSettings() const;
protected:
/**
......
......@@ -21,12 +21,12 @@
#include <klocalizedstring.h>
const KoID FiltersCategoryAdjustId("adjust_filters", ki18n("Adjust"));
const KoID FiltersCategoryArtisticId("artistic_filters", ki18n("Artistic"));
const KoID FiltersCategoryBlurId("blur_filters", ki18n("Blur"));
const KoID FiltersCategoryColorId("color_filters", ki18n("Colors"));
const KoID FiltersCategoryEdgeDetectionId("edge_filters", ki18n("Edge Detection"));
const KoID FiltersCategoryEmbossId("emboss_filters", ki18n("Emboss"));
const KoID FiltersCategoryEnhanceId("enhance_filters", ki18n("Enhance"));
const KoID FiltersCategoryMapId("map_filters", ki18n("Map"));
const KoID FiltersCategoryOtherId("other_filters", ki18n("Other"));
const KoID FiltersCategoryAdjustId("adjust_filters", ki18nc("The category of color adjustment filters, like levels. Verb.", "Adjust"));
const KoID FiltersCategoryArtisticId("artistic_filters", ki18nc("The category of artistic filters, like raindrops. Adjective.", "Artistic"));
const KoID FiltersCategoryBlurId("blur_filters", ki18nc("The category of blur filters, like gaussian blur. Verb.", "Blur,"));
const KoID FiltersCategoryColorId("color_filters", ki18nc("The category of color transfer filters, like color to alpha. Noun.", "Colors"));
const KoID FiltersCategoryEdgeDetectionId("edge_filters", ki18nc("The category of edge detection filters. Noun.", "Edge Detection"));
const KoID FiltersCategoryEmbossId("emboss_filters", ki18nc("The category of emboss filters. Verb.", "Emboss"));
const KoID FiltersCategoryEnhanceId("enhance_filters", ki18nc("The category of enhancement filters, like sharpen. Verb.", "Enhance"));
const KoID FiltersCategoryMapId("map_filters", ki18nc("The category of mapping filters, like bump map or gradient filter map. Verb.", "Map"));
const KoID FiltersCategoryOtherId("other_filters", ki18nc("The category of filters that do not fit in a category. Noun.", "Other"));
......@@ -52,7 +52,7 @@ public:
/**
* Create a new filter config.
*/
KisFilterConfiguration(const QString & name, qint32 version);
KisFilterConfiguration(const QString &name, qint32 version);
protected:
/**
......
......@@ -211,6 +211,35 @@ private:
int m_threshold;
};
class SelectednessPolicyOptimized
{
typedef quint8 HashKeyType;
typedef QHash<HashKeyType, quint8> HashType;
KisRandomConstAccessorSP m_selectionIt;
public:
ALWAYS_INLINE void initSelectedness(KisPaintDeviceSP device, int threshold) {
m_colorSpace = device->colorSpace();
m_threshold = threshold;
m_selectionIt = device->createRandomConstAccessorNG();
}
ALWAYS_INLINE quint8 calculateSelectedness(int x, int y) {
m_selectionIt->moveTo(x, y);
const quint8* pixelPtr = m_selectionIt->rawDataConst();
return *pixelPtr;
}
private:
HashType m_selectedness;
const KoColorSpace *m_colorSpace;
int m_threshold;
};
template <bool useSmoothSelection,
class DifferencePolicy,
template <class> class PixelFiller>
......@@ -227,9 +256,11 @@ public:
m_srcIt = this->createSourceDeviceAccessor(device);
}
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) {
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr, int x, int y) {
quint8 diff = this->calculateDifference(pixelPtr);
Q_UNUSED(x);
Q_UNUSED(y);
if (!useSmoothSelection) {
return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED;
} else {
......@@ -241,6 +272,48 @@ public:
qreal selectionNorm = qreal(selectionValue) / m_threshold;
result = MAX_SELECTED * selectionNorm;
}
return result;
}
}
private:
int m_threshold;
};
template <bool useSmoothSelection,
class DifferencePolicy,
template <class> class PixelFiller,
class SelectednessCheckPolicy>
class SelectionPolicyExtended : public SelectionPolicy<useSmoothSelection, DifferencePolicy, PixelFiller>
, public SelectednessCheckPolicy
{
public:
SelectionPolicyExtended(KisPaintDeviceSP mainDevice, KisPaintDeviceSP selectionDevice, const KoColor &srcPixel, int threshold)
: SelectionPolicy<useSmoothSelection, DifferencePolicy, PixelFiller>(mainDevice, srcPixel, threshold)
{
m_threshold = threshold;
this->initSelectedness(selectionDevice, threshold);
}
public:
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr, int x, int y) {
quint8 diff = this->calculateDifference(pixelPtr);
quint8 selectedness = this->calculateSelectedness(x, y);
if (!useSmoothSelection) {
return (diff <= m_threshold && selectedness > 0) ? MAX_SELECTED : MIN_SELECTED;
} else {
quint8 selectionValue = qMax(0, m_threshold - diff);
quint8 result = MIN_SELECTED;
if (selectionValue > 0 && selectedness > 0) {
qreal selectionNorm = qreal(selectionValue) / m_threshold;
result = MAX_SELECTED * selectionNorm;
}
return result;
}
......@@ -248,8 +321,14 @@ public:
private:
int m_threshold;
};
class IsNonNullPolicySlow
{
public:
......@@ -308,11 +387,13 @@ public:
m_groupMapIt = groupMapDevice->createRandomAccessorNG();
}
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) {
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr, int x, int y) {
// TODO: either threshold should always be null, or there should be a special
// case for *pixelPtr == 0, which is different from all the other groups,
// whatever the threshold is
Q_UNUSED(x);
Q_UNUSED(y);
int diff = qAbs(int(*pixelPtr) - m_referenceValue);
return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED;
}
......@@ -425,7 +506,7 @@ void KisScanlineFill::extendedPass(KisFillInterval *currentInterval, int srcRow,
pixelPolicy.m_srcIt->moveTo(x, srcRow);
quint8 *pixelPtr = const_cast<quint8*>(pixelPolicy.m_srcIt->rawDataConst()); // TODO: avoid doing const_cast
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr);
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr, x, srcRow);
if (opacity) {
*intervalBorder = x;
......@@ -473,7 +554,7 @@ void KisScanlineFill::processLine(KisFillInterval interval, const int rowIncreme
}
quint8 *pixelPtr = dataPtr;
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr);
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr, x, row);
if (opacity) {
if (!currentForwardInterval.isValid()) {
......@@ -624,6 +705,41 @@ void KisScanlineFill::fillColor(const KoColor &originalFillColor, KisPaintDevice
}
}
void KisScanlineFill::fillSelectionWithBoundary(KisPixelSelectionSP pixelSelection, KisPaintDeviceSP existingSelection)
{
KoColor srcColor(m_d->device->pixel(m_d->startPoint));
const int pixelSize = m_d->device->pixelSize();
if (pixelSize == 1) {
SelectionPolicyExtended<true, DifferencePolicyOptimized<quint8>, CopyToSelection, SelectednessPolicyOptimized>
policy(m_d->device, existingSelection, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 2) {
SelectionPolicyExtended<true, DifferencePolicyOptimized<quint16>, CopyToSelection, SelectednessPolicyOptimized>
policy(m_d->device, existingSelection, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 4) {
SelectionPolicyExtended<true, DifferencePolicyOptimized<quint32>, CopyToSelection, SelectednessPolicyOptimized>
policy(m_d->device, existingSelection, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 8) {
SelectionPolicyExtended<true, DifferencePolicyOptimized<quint64>, CopyToSelection, SelectednessPolicyOptimized>
policy(m_d->device, existingSelection, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else {
SelectionPolicyExtended<true, DifferencePolicySlow, CopyToSelection, SelectednessPolicyOptimized>
policy(m_d->device, existingSelection, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
}
}
void KisScanlineFill::fillSelection(KisPixelSelectionSP pixelSelection)
{
KoColor srcColor(m_d->device->pixel(m_d->startPoint));
......
......@@ -45,6 +45,12 @@ public:
*/
void fillColor(const KoColor &fillColor, KisPaintDeviceSP externalDevice);
/**
* Fill \p pixelSelection with the opacity of the contiguous area.
* This method uses an existing selection as boundary for the flood fill.
*/
void fillSelectionWithBoundary(KisPixelSelectionSP pixelSelection, KisPaintDeviceSP existingSelection);
/**
* Fill \p pixelSelection with the opacity of the contiguous area
*/
......
......@@ -163,3 +163,55 @@ QRect KisSelectionEmptyBounds::bounds() const
{
return QRect(0, 0, 0, 0);
}
/******************************************************************/
/* KisWrapAroundBoundsWrapper */
/******************************************************************/
struct Q_DECL_HIDDEN KisWrapAroundBoundsWrapper::Private
{
KisDefaultBoundsBaseSP base;
QRect bounds;
};
KisWrapAroundBoundsWrapper::KisWrapAroundBoundsWrapper(KisDefaultBoundsBaseSP base, QRect bounds)
: m_d(new Private())
{
m_d->base = base;
m_d->bounds = bounds;
}
KisWrapAroundBoundsWrapper::~KisWrapAroundBoundsWrapper()
{
}
QRect KisWrapAroundBoundsWrapper::bounds() const
{
return m_d->bounds;
}
bool KisWrapAroundBoundsWrapper::wrapAroundMode() const
{
return true;
}
int KisWrapAroundBoundsWrapper::currentLevelOfDetail() const
{
return m_d->base->currentLevelOfDetail();
}
int KisWrapAroundBoundsWrapper::currentTime() const
{
return m_d->base->currentTime();
}
bool KisWrapAroundBoundsWrapper::externalFrameActive() const
{
return m_d->base->externalFrameActive();
}
void *KisWrapAroundBoundsWrapper::sourceCookie() const
{
return m_d->base->sourceCookie();
}
......@@ -26,9 +26,11 @@
class KisDefaultBounds;
class KisSelectionDefaultBounds;
class KisSelectionEmptyBounds;
class KisWrapAroundBoundsWrapper;
typedef KisSharedPtr<KisDefaultBounds> KisDefaultBoundsSP;
typedef KisSharedPtr<KisSelectionDefaultBounds> KisSelectionDefaultBoundsSP;
typedef KisSharedPtr<KisSelectionEmptyBounds> KisSelectionEmptyBoundsSP;
typedef KisSharedPtr<KisWrapAroundBoundsWrapper> KisWrapAroundBoundsWrapperSP;
class KRITAIMAGE_EXPORT KisDefaultBounds : public KisDefaultBoundsBase
{
......@@ -83,4 +85,32 @@ public:
QRect bounds() const override;
};
/**
* @brief The KisWrapAroundBoundsWrapper class
* wrapper around a KisDefaultBoundsBaseSP to enable
* wraparound. Used for patterns.
*/
class KRITAIMAGE_EXPORT KisWrapAroundBoundsWrapper : public KisDefaultBoundsBase
{
public:
KisWrapAroundBoundsWrapper(KisDefaultBoundsBaseSP base, QRect bounds);
~KisWrapAroundBoundsWrapper() override;
QRect bounds() const override;
bool wrapAroundMode() const override;
int currentLevelOfDetail() const override;
int currentTime() const override;
bool externalFrameActive() const override;
void * sourceCookie() const override;
protected:
friend class KisPaintDeviceTest;
private:
Q_DISABLE_COPY(KisWrapAroundBoundsWrapper)
struct Private;
const QScopedPointer<Private> m_d;
};
#endif // KIS_DEFAULT_BOUNDS_H
......@@ -54,6 +54,7 @@
#include <KoCompositeOpRegistry.h>
#include <floodfill/kis_scanline_fill.h>
#include "kis_selection_filters.h"
#include <kis_perspectivetransform_worker.h>
KisFillPainter::KisFillPainter()
: KisPainter()
......@@ -81,6 +82,7 @@ void KisFillPainter::initFillPainter()
m_feather = 0;
m_useCompositioning = false;
m_threshold = 0;
m_useSelectionAsBoundary = false;
}
void KisFillPainter::fillSelection(const QRect &rc, const KoColor &color)
......@@ -133,6 +135,31 @@ void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const Ko
fillRect(x1, y1, w, h, patternLayer, QRect(offset.x(), offset.y(), pattern->width(), pattern->height()));
}
void KisFillPainter::fillRect(const QRect &rc, const KoPattern * pattern, const QTransform transform)
{
if (!device()) return;
if (rc.width() < 1) return;
if (rc.height() < 1) return;
KisPaintDeviceSP patternLayer = new KisPaintDevice(device()->compositionSourceColorSpace(), pattern->name());
patternLayer->convertFromQImage(pattern->pattern(), 0);
fillRect(rc.x(), rc.y(), rc.width(), rc.height(), patternLayer, QRect(0, 0, pattern->width(), pattern->height()), transform);
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect, const QTransform transform)
{
KisPaintDeviceSP wrapped = device;
KisDefaultBoundsBaseSP oldBounds = wrapped->defaultBounds();
wrapped->setDefaultBounds(new KisWrapAroundBoundsWrapper(oldBounds, deviceRect));
KisPerspectiveTransformWorker worker = KisPerspectiveTransformWorker(this->device(), transform, this->progressUpdater());
worker.runPartialDst(device, this->device(), QRect(x1, y1, w, h));
addDirtyRect(QRect(x1, y1, w, h));
wrapped->setDefaultBounds(oldBounds);
}
void KisFillPainter::fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect)
{
const QRect &patternRect = deviceRect;
......@@ -225,7 +252,7 @@ void KisFillPainter::fillColor(int startX, int startY, KisPaintDeviceSP sourceDe
}
}
void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice)
void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP sourceDevice, QTransform patternTransform)
{
genericFillStart(startX, startY, sourceDevice);
......@@ -233,7 +260,7 @@ void KisFillPainter::fillPattern(int startX, int startY, KisPaintDeviceSP source
KisPaintDeviceSP filled = device()->createCompositionSourceDevice();
Q_CHECK_PTR(filled);
KisFillPainter painter(filled);
painter.fillRect(0, 0, m_width, m_height, pattern());
painter.fillRect(QRect(0, 0, m_width, m_height), pattern(), patternTransform);
painter.end();
genericFillEnd(filled);
......@@ -246,7 +273,7 @@ void KisFillPainter::genericFillStart(int startX, int startY, KisPaintDeviceSP s
// Create a selection from the surrounding area
KisPixelSelectionSP pixelSelection = createFloodSelection(startX, startY, sourceDevice);
KisPixelSelectionSP pixelSelection = createFloodSelection(startX, startY, sourceDevice, selection()->pixelSelection());
KisSelectionSP newSelection = new KisSelection(pixelSelection->defaultBounds());
newSelection->pixelSelection()->applySelection(pixelSelection, SELECTION_REPLACE);
m_fillSelection = newSelection;
......@@ -284,13 +311,15 @@ void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled)
m_width = m_height = -1;
}
KisPixelSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice)
KisPixelSelectionSP KisFillPainter::createFloodSelection(int startX, int startY, KisPaintDeviceSP sourceDevice,
KisPaintDeviceSP existingSelection)
{
KisPixelSelectionSP newSelection = new KisPixelSelection(new KisSelectionDefaultBounds(device()));
return createFloodSelection(newSelection, startX, startY, sourceDevice);
return createFloodSelection(newSelection, startX, startY, sourceDevice, existingSelection);
}
KisPixelSelectionSP KisFillPainter::createFloodSelection(KisPixelSelectionSP pixelSelection, int startX, int startY, KisPaintDeviceSP sourceDevice)
KisPixelSelectionSP KisFillPainter::createFloodSelection(KisPixelSelectionSP pixelSelection, int startX, int startY,
KisPaintDeviceSP sourceDevice, KisPaintDeviceSP existingSelection)
{
if (m_width < 0 || m_height < 0) {
......@@ -313,7 +342,11 @@ KisPixelSelectionSP KisFillPainter::createFloodSelection(KisPixelSelectionSP pix
KisScanlineFill gc(sourceDevice, startPoint, fillBoundsRect);
gc.setThreshold(m_threshold);
gc.fillSelection(pixelSelection);
if (m_useSelectionAsBoundary) {
gc.fillSelectionWithBoundary(pixelSelection, existingSelection);
} else {
gc.fillSelection(pixelSelection);
}
if (m_sizemod > 0) {
KisGrowSelectionFilter biggy(m_sizemod, m_sizemod);
......
......@@ -107,6 +107,8 @@ public:
/**
* Fill a rectangle with a certain pattern. The pattern is repeated if it does not fit the
* entire rectangle.
*
* This one uses blitting and thus makes use of proper composition.
*/
void fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRec