Commit 6593a82f authored by Dmitry Kazakov's avatar Dmitry Kazakov

New implementation of the brush outline display

This patch effectively fixes two things:
1) Makes the outline painted correctly of rotated/mirrored canvas or/and dab.
   This is achieved by using a special class for making all the transforms.
   The class is called KisCurrentOulineFetcher.

2) Fixes the Drawing Angle sensor on the mirrored canvas, which has become
   broken quite recently.
parent feafe5a5
......@@ -78,6 +78,19 @@ struct KisPaintInformation::Private {
}
};
KisPaintInformation::DistanceInformaionRegistrar::
DistanceInformaionRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo)
: p(_p)
{
p->d->registerDistanceInfo(distanceInfo);
}
KisPaintInformation::DistanceInformaionRegistrar::
~DistanceInformaionRegistrar()
{
p->d->unregisterDistanceInfo();
}
KisPaintInformation::KisPaintInformation(const QPointF & pos_,
qreal pressure_,
qreal xTilt_, qreal yTilt_,
......@@ -193,6 +206,12 @@ qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distan
return atan2(diff.y(), diff.x());
}
KisPaintInformation::DistanceInformaionRegistrar
KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance)
{
return DistanceInformaionRegistrar(this, distance);
}
qreal KisPaintInformation::drawingAngle() const
{
if (d->drawingAngleOverride) return *d->drawingAngleOverride;
......
......@@ -56,6 +56,19 @@ class KisDistanceInformation;
**/
class KRITAIMAGE_EXPORT KisPaintInformation
{
public:
/**
* Note, that this class is relied on the compiler optimization
* of the return value. So if it doesn't work for some reason,
* please implement a proper copy c-tor
*/
class DistanceInformaionRegistrar {
public:
DistanceInformaionRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo);
~DistanceInformaionRegistrar();
private:
KisPaintInformation *p;
};
public:
......@@ -99,6 +112,8 @@ public:
qreal drawingAngleSafe(const KisDistanceInformation &distance) const;
DistanceInformaionRegistrar registerDistanceInformation(KisDistanceInformation *distance);
/**
* Current brush direction computed from the cursor movement
*
......
......@@ -51,7 +51,6 @@ KisPaintOpSettings::KisPaintOpSettings()
KisPaintOpSettings::~KisPaintOpSettings()
{
delete d;
}
void KisPaintOpSettings::setOptionsWidget(KisPaintOpSettingsWidget* widget)
......@@ -145,22 +144,15 @@ QString KisPaintOpSettings::indirectPaintingCompositeOp() const {
return COMPOSITE_ALPHA_DARKEN;
}
QPainterPath KisPaintOpSettings::brushOutline(const QPointF& pos, OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
Q_UNUSED(info);
QPainterPath path;
if (mode == CursorIsOutline){
QRectF rc(-5,-5, 10, 10);
path.moveTo(rc.topLeft());
path.lineTo(rc.bottomRight());
path.moveTo(rc.topRight());
path.lineTo(rc.bottomLeft());
QTransform m;
m.reset();
m.scale(scale, scale);
m.rotateRadians(rotation);
path = m.map(path);
path.translate(pos);
path = ellipseOutline(10,10,0,0);
}
return path;
}
......@@ -192,3 +184,13 @@ void KisPaintOpSettings::setCanvasMirroring(bool xAxisMirrored, bool yAxisMirror
setProperty("runtimeCanvasMirroredY", yAxisMirrored);
setPropertyNotSaved("runtimeCanvasMirroredY");
}
void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value)
{
KisPropertiesConfiguration::setProperty(name, value);
onPropertyChanged();
}
void KisPaintOpSettings::onPropertyChanged()
{
}
......@@ -23,6 +23,7 @@
#include "krita_export.h"
#include <QImage>
#include <QScopedPointer>
#include "kis_shared.h"
#include "kis_properties_configuration.h"
......@@ -132,7 +133,7 @@ public:
* Outline mode has to be passed to the paintop which builds the outline as some paintops have to paint outline
* always like duplicate paintop indicating the duplicate position
*/
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
virtual QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
/**
* Useful for simple elliptical brush outline.
......@@ -185,17 +186,27 @@ public:
void setCanvasRotation(qreal angle);
void setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored);
/**
* Overrides the method in KisPropertiesCofiguration to allow
* onPropertyChanged() callback
*/
void setProperty(const QString & name, const QVariant & value);
protected:
/**
* @return the option widget of the paintop (can be 0 is no option widgets is set)
*/
KisPaintOpSettingsWidget* optionsWidget() const;
private:
/**
* The callback is called every time when a property changes
*/
virtual void onPropertyChanged();
struct Private;
Private* const d;
private:
struct Private;
const QScopedPointer<Private> d;
};
#endif
......@@ -42,18 +42,13 @@ int KisChalkPaintOpSettings::rate() const
return getInt(AIRBRUSH_RATE);
}
QPainterPath KisChalkPaintOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisChalkPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
Q_UNUSED(scale);
Q_UNUSED(rotation);
QPainterPath path;
if (mode == CursorIsOutline){
qreal size = getInt(CHALK_RADIUS) * 2 + 1;
QRectF rc(0, 0, size, size);
rc.translate(-rc.center());
path.addEllipse(rc);
path.translate(pos);
path = ellipseOutline(size, size, 1.0, 0.0);
path.translate(info.pos());
}
return path;
}
......@@ -48,7 +48,7 @@ public:
KisChalkPaintOpSettings();
virtual ~KisChalkPaintOpSettings() {}
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
bool paintIncremental();
bool isAirbrushing() const;
......
......@@ -31,10 +31,30 @@
class TestBrushOp : public TestUtil::QImageBasedTest
{
public:
TestBrushOp()
TestBrushOp(const QString &presetFileName)
: QImageBasedTest("brushop")
{
m_presetFileName = "LR_simple.kpp";
m_presetFileName = presetFileName;
}
virtual ~TestBrushOp() {}
void test() {
test(false, false, 0.0);
test(false, false, 10.0);
test(false, false, 20.0);
test(true, false, 0.0);
test(true, false, 10.0);
test(true, false, 20.0);
test(false, true, 0.0);
test(false, true, 10.0);
test(false, true, 20.0);
test(true, true, 0.0);
test(true, true, 10.0);
test(true, true, 20.0);
}
void test(bool mirrorX, bool mirrorY, qreal rotation) {
......@@ -122,37 +142,56 @@ public:
resources->setupPainter(&gc);
doPaint(gc);
checkOneLayer(image, paint1, testName);
}
virtual void doPaint(KisPainter &gc) {
KisPaintInformation pi(QPointF(100,100), 1.0);
KisDistanceInformation dist;
gc.paintAt(pi, &dist);
checkOneLayer(image, paint1, testName);
}
QString m_presetFileName;
};
void KisBrushOpTest::testRotationMirroring()
class TestBrushOpLines : public TestBrushOp
{
TestBrushOp t;
public:
TestBrushOpLines(const QString &presetFileName)
: TestBrushOp(presetFileName)
{
}
void doPaint(KisPainter &gc) {
QVector<KisPaintInformation> vector;
vector << KisPaintInformation(QPointF(100,100));
vector << KisPaintInformation(QPointF(200,150));
vector << KisPaintInformation(QPointF(100,350));
KisDistanceInformation dist;
t.test(false, false, 0.0);
t.test(false, false, 10.0);
t.test(false, false, 20.0);
for (int i = 1; i < vector.size(); i++) {
gc.paintLine(vector[i-1], vector[i], &dist);
}
}
};
t.test(true, false, 0.0);
t.test(true, false, 10.0);
t.test(true, false, 20.0);
t.test(false, true, 0.0);
t.test(false, true, 10.0);
t.test(false, true, 20.0);
void KisBrushOpTest::testRotationMirroring()
{
TestBrushOp t("LR_simple.kpp");
t.test();
}
t.test(true, true, 0.0);
t.test(true, true, 10.0);
t.test(true, true, 20.0);
void KisBrushOpTest::testRotationMirroringDrawingAngle()
{
TestBrushOpLines t("LR_drawing_angle.kpp");
t.test();
}
QTEST_KDEMAIN(KisBrushOpTest, GUI)
......@@ -26,6 +26,7 @@ class KisBrushOpTest : public QObject
Q_OBJECT
private slots:
void testRotationMirroring();
void testRotationMirroringDrawingAngle();
};
#endif /* __KIS_BRUSHOP_TEST_H */
......@@ -136,24 +136,24 @@ KisPaintOpSettingsSP KisDuplicateOpSettings::clone() const
}
QPainterPath KisDuplicateOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisDuplicateOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
path = KisBrushBasedPaintOpSettings::brushOutline(QPointF(), mode, scale, rotation);
path = KisBrushBasedPaintOpSettings::brushOutline(info, mode);
QPainterPath copy(path);
QRectF rect2 = copy.boundingRect();
if (m_isOffsetNotUptodate || !getBool(DUPLICATE_MOVE_SOURCE_POINT)) {
copy.translate(m_position - pos);
copy.translate(m_position - info.pos());
} else {
copy.translate(-m_offset);
}
path.addPath(copy);
QTransform m;
m.scale(0.5,0.5);
rect2 = m.mapRect(rect2);
qreal dx = rect2.width() / 4.0;
qreal dy = rect2.height() / 4.0;
rect2.adjust(dx, dy, -dx, -dy);
path.moveTo(rect2.topLeft());
path.lineTo(rect2.bottomRight());
......@@ -161,5 +161,5 @@ QPainterPath KisDuplicateOpSettings::brushOutline(const QPointF& pos, KisPaintOp
path.moveTo(rect2.topRight());
path.lineTo(rect2.bottomLeft());
return path.translated(pos);
return path;
}
......@@ -52,7 +52,7 @@ public:
void toXML(QDomDocument& doc, QDomElement& rootElt) const;
KisPaintOpSettingsSP clone() const;
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
public:
QPointF m_offset;
......
......@@ -24,6 +24,12 @@
#include <kis_airbrush_option.h>
#include <kis_deform_option.h>
KisDeformPaintOpSettings::KisDeformPaintOpSettings()
: KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION |
KisCurrentOutlineFetcher::ROTATION_OPTION)
{
}
bool KisDeformPaintOpSettings::paintIncremental()
{
return true;
......@@ -48,16 +54,14 @@ int KisDeformPaintOpSettings::rate() const
}
}
QPainterPath KisDeformPaintOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisDeformPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
if (mode == CursorIsOutline){
qreal width = getInt(BRUSH_DIAMETER);
qreal height = getInt(BRUSH_DIAMETER) * getDouble(BRUSH_ASPECT);
path = ellipseOutline(width, height,getDouble(BRUSH_SCALE),getDouble(BRUSH_ROTATION) );
QTransform m; m.reset(); m.scale(scale,scale); m.rotateRadians(rotation);
path = m.map(path);
path.translate(pos);
path = ellipseOutline(width, height, getDouble(BRUSH_SCALE), getDouble(BRUSH_ROTATION));
path = outlineFetcher()->fetchOutline(info, this, path);
}
return path;
}
......@@ -23,24 +23,16 @@ class KisDeformPaintOpSettingsWidget;
#include <kis_paintop_settings.h>
#include <kis_types.h>
#include <kis_outline_generation_policy.h>
#include <opengl/kis_opengl.h>
#if defined(_WIN32) || defined(_WIN64)
#ifndef __MINGW32__
# include <windows.h>
#endif
#endif
class KisDeformPaintOpSettings : public KisPaintOpSettings
class KisDeformPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
KisDeformPaintOpSettings(){}
virtual ~KisDeformPaintOpSettings() {}
KisDeformPaintOpSettings();
QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
bool paintIncremental();
bool isAirbrushing() const;
int rate() const;
......
......@@ -25,26 +25,27 @@
#include "kis_grid_shape_option.h"
#include <kis_color_option.h>
KisGridPaintOpSettings::KisGridPaintOpSettings()
: KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::NO_OPTION)
{
}
bool KisGridPaintOpSettings::paintIncremental()
{
return (enumPaintActionType)getInt("PaintOpAction", WASH) == BUILDUP;
}
QPainterPath KisGridPaintOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisGridPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
if (mode == CursorIsOutline) {
qreal sizex = getInt(GRID_WIDTH) * getDouble(GRID_SCALE) * scale;
qreal sizey = getInt(GRID_HEIGHT) * getDouble(GRID_SCALE) * scale;
qreal sizex = getInt(GRID_WIDTH) * getDouble(GRID_SCALE);
qreal sizey = getInt(GRID_HEIGHT) * getDouble(GRID_SCALE);
QRectF rc(0, 0, sizex, sizey);
rc.translate(-rc.center());
QTransform m;
m.reset();
m.rotate(rotation);
path = m.map(path);
path.addRect(rc);
path.translate(pos);
path = outlineFetcher()->fetchOutline(info, this, path);
}
return path;
}
......@@ -22,6 +22,7 @@
#include <kis_paintop_settings.h>
#include <kis_types.h>
#include <kis_outline_generation_policy.h>
#include "kis_grid_paintop_settings_widget.h"
class QWidget;
......@@ -29,11 +30,12 @@ class QDomElement;
class QDomDocument;
class KisGridPaintOpSettings : public KisPaintOpSettings
class KisGridPaintOpSettings : public KisOutlineGenerationPolicy<KisPaintOpSettings>
{
public:
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
KisGridPaintOpSettings();
QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
bool paintIncremental();
private:
......
......@@ -34,17 +34,21 @@ KisHairyPaintOpSettings::KisHairyPaintOpSettings()
setProperty(HAIRY_VERSION, "2");
}
QPainterPath KisHairyPaintOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisHairyPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
if (mode == CursorIsOutline){
path = KisBrushBasedPaintOpSettings::brushOutline(QPointF(0.0,0.0),mode, scale, rotation);
double scaleFactor = getDouble(HAIRY_BRISTLE_SCALE);
QTransform m;
m.reset();
m.scale(scaleFactor * scale, scaleFactor * scale);
path = m.map(path);
path.translate(pos);
KisBrushBasedPaintopOptionWidget *widget = dynamic_cast<KisBrushBasedPaintopOptionWidget*>(optionsWidget());
if(!widget) {
return KisPaintOpSettings::brushOutline(info, mode);
}
KisBrushSP brush = widget->brush();
qreal additionalScale = brush->scale() * getDouble(HAIRY_BRISTLE_SCALE);
return outlineFetcher()->fetchOutline(info, this, brush->outline(), additionalScale, brush->angle());
}
return path;
}
......
......@@ -34,7 +34,7 @@ public:
using KisPaintOpSettings::fromXML;
KisHairyPaintOpSettings();
virtual QPainterPath brushOutline(const QPointF& pos, OutlineMode mode, qreal scale = 1.0, qreal rotation = 0.0) const;
QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const;
virtual void fromXML(const QDomElement&);
};
......
......@@ -45,6 +45,7 @@ set(kritalibpaintop_LIB_SRCS
kis_pressure_flow_opacity_option.cpp
kis_pressure_flow_opacity_option_widget.cpp
kis_pressure_spacing_option_widget.cpp
kis_current_outline_fetcher.cpp
kis_sensor_selector.cc
kis_text_brush_chooser.cpp
kis_brush_based_paintop_options_widget.cpp
......
......@@ -24,6 +24,14 @@
#include <kis_boundary.h>
#include "kis_brush_server.h"
KisBrushBasedPaintOpSettings::KisBrushBasedPaintOpSettings()
: KisOutlineGenerationPolicy(KisCurrentOutlineFetcher::SIZE_OPTION |
KisCurrentOutlineFetcher::ROTATION_OPTION |
KisCurrentOutlineFetcher::MIRROR_OPTION)
{
}
bool KisBrushBasedPaintOpSettings::paintIncremental()
{
if(hasProperty("PaintOpAction")) {
......@@ -43,31 +51,19 @@ int KisBrushBasedPaintOpSettings::rate() const
return getInt(AIRBRUSH_RATE);
}
QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const QPointF& pos, KisPaintOpSettings::OutlineMode mode, qreal scale, qreal rotation) const
QPainterPath KisBrushBasedPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) const
{
QPainterPath path;
if (mode == CursorIsOutline) {
KisBrushBasedPaintopOptionWidget* options = dynamic_cast<KisBrushBasedPaintopOptionWidget*>(optionsWidget());
if(!options) {
return KisPaintOpSettings::brushOutline(pos,mode);
}
KisBrushSP brush = options->brush();
QPointF hotSpot = brush->hotSpot(1.0/brush->scale(),1.0/brush->scale(), -brush->angle(), KisPaintInformation());
if (mode != CursorIsOutline) return QPainterPath();
QTransform m;
m.reset();
m.rotateRadians(-rotation - brush->angle());
m.scale(brush->scale() * scale, brush->scale() * scale);
m.translate(-hotSpot.x(), -hotSpot.y());
KisBrushBasedPaintopOptionWidget *widget = dynamic_cast<KisBrushBasedPaintopOptionWidget*>(optionsWidget());
path = brush->outline();
path = m.map(path);
path.translate(pos);
if(!widget) {
return KisPaintOpSettings::brushOutline(info, mode);
}
return path;
KisBrushSP brush = widget->brush();
return outlineFetcher()->fetchOutline(info, this, brush->outline(), brush->scale(), brush->angle());
}
bool KisBrushBasedPaintOpSettings::isValid()
......