summaryrefslogtreecommitdiffstats
path: root/krita/plugins/tools/tool_curves
diff options
context:
space:
mode:
Diffstat (limited to 'krita/plugins/tools/tool_curves')
-rw-r--r--krita/plugins/tools/tool_curves/Makefile.am55
-rw-r--r--krita/plugins/tools/tool_curves/kis_curve_framework.cc260
-rw-r--r--krita/plugins/tools/tool_curves/kis_curve_framework.h354
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier.cc366
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier.h97
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier_paint.cc115
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier_paint.h62
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier_select.cc104
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_bezier_select.h62
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_curve.cc593
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_curve.h204
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_example.cc108
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_example.h66
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_moutline.cc809
-rw-r--r--krita/plugins/tools/tool_curves/kis_tool_moutline.h131
-rw-r--r--krita/plugins/tools/tool_curves/kritatoolcurves.desktop36
-rw-r--r--krita/plugins/tools/tool_curves/tool_bezier_cursor.pngbin0 -> 2853 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_bezier_paint.pngbin0 -> 679 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_bezier_select.pngbin0 -> 636 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_curve_dragging.pngbin0 -> 2955 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_curves.cc67
-rw-r--r--krita/plugins/tools/tool_curves/tool_curves.h35
-rw-r--r--krita/plugins/tools/tool_curves/tool_example.pngbin0 -> 2917 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_example_cursor.pngbin0 -> 2853 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_moutline.pngbin0 -> 777 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_moutline_cursor.pngbin0 -> 2857 bytes
-rw-r--r--krita/plugins/tools/tool_curves/tool_moutline_editing.pngbin0 -> 2967 bytes
-rw-r--r--krita/plugins/tools/tool_curves/wdg_tool_example.ui128
28 files changed, 3652 insertions, 0 deletions
diff --git a/krita/plugins/tools/tool_curves/Makefile.am b/krita/plugins/tools/tool_curves/Makefile.am
new file mode 100644
index 00000000..d809eaee
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/Makefile.am
@@ -0,0 +1,55 @@
+kde_services_DATA = kritatoolcurves.desktop
+
+# all_includes must remain last!
+INCLUDES = -I$(srcdir)/../../../sdk \
+ -I$(srcdir)/../../../core \
+ -I$(srcdir)/../../../kritacolor/ \
+ -I$(srcdir)/../../../ui \
+ -I$/../../../ui \
+ $(KOFFICE_INCLUDES) \
+ $(all_includes)
+
+kritatoolcurves_la_SOURCES = \
+ kis_curve_framework.cc \
+ kis_tool_curve.cc \
+ tool_curves.cc \
+ wdg_tool_example.ui \
+ kis_tool_example.cc \
+ kis_tool_bezier.cc \
+ kis_tool_bezier_paint.cc \
+ kis_tool_bezier_select.cc \
+ kis_tool_moutline.cc
+
+# Install this plugin in the KDE modules directory
+kde_module_LTLIBRARIES = kritatoolcurves.la
+
+noinst_HEADERS = \
+ kis_curve_framework.h \
+ kis_tool_curve.h \
+ tool_curves.h \
+ kis_tool_example.h \
+ kis_tool_bezier.h \
+ kis_tool_bezier_paint.h \
+ kis_tool_bezier_select.h \
+ kis_tool_moutline.h
+
+kritatoolcurves_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kritatoolcurves_la_LIBADD = ../../../libkritacommon.la
+
+kritatoolcurves_la_METASOURCES = AUTO
+
+KDE_OPTIONS = nofinal
+
+kritapics_DATA = \
+ tool_example.png \
+ tool_example_cursor.png \
+ tool_bezier_paint.png \
+ tool_bezier_select.png \
+ tool_bezier_cursor.png \
+ tool_moutline.png \
+ tool_moutline_cursor.png \
+ tool_curve_dragging.png \
+ tool_moutline_editing.png
+
+kritapicsdir = $(kde_datadir)/krita/pics
+
diff --git a/krita/plugins/tools/tool_curves/kis_curve_framework.cc b/krita/plugins/tools/tool_curves/kis_curve_framework.cc
new file mode 100644
index 00000000..2ccb87a4
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_curve_framework.cc
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <qvaluelist.h>
+#include <qrect.h>
+#include "kis_point.h"
+
+#include "kis_curve_framework.h"
+
+/* **************************** *
+ * KisCurve methods definitions *
+ * **************************** */
+
+KisCurve::iterator KisCurve::addPivot (KisCurve::iterator it, const KisPoint& point)
+{
+ return iterator(*this,m_curve.insert(it.position(), CurvePoint(point,true,false,NOHINTS)));
+}
+
+KisCurve::iterator KisCurve::pushPivot (const KisPoint& point)
+{
+ return selectPivot(iterator(*this,m_curve.append(CurvePoint(point,true,false,NOHINTS))), true);
+}
+
+KisCurve::iterator KisCurve::addPoint (KisCurve::iterator it, const KisPoint& point, bool pivot, bool selected, int hint)
+{
+ return iterator(*this,m_curve.insert(it.position(), CurvePoint(point,pivot,selected, hint)));
+}
+
+KisCurve::iterator KisCurve::addPoint (KisCurve::iterator it, const CurvePoint& point)
+{
+ return iterator(*this,m_curve.insert(it.position(), point));
+}
+
+KisCurve::iterator KisCurve::pushPoint (const KisPoint& point, bool pivot, bool selected,int hint)
+{
+ return iterator(*this,m_curve.append(CurvePoint(point,pivot,selected,hint)));
+}
+
+KisCurve::iterator KisCurve::pushPoint (const CurvePoint& point)
+{
+ return iterator(*this,m_curve.append(point));
+}
+
+KisCurve KisCurve::pivots()
+{
+ KisCurve temp;
+
+ for (iterator it = begin(); it != end(); it = it.nextPivot())
+ temp.pushPoint((*it));
+
+ return temp;
+}
+
+KisCurve KisCurve::selectedPivots(bool selected)
+{
+ KisCurve temp;
+
+ for (iterator it = begin(); it != end(); it = it.nextPivot())
+ if ((*it).isSelected() == selected)
+ temp.pushPoint((*it));
+
+ return temp;
+}
+
+KisCurve KisCurve::subCurve(const KisPoint& tend)
+{
+ return subCurve(find(tend).previousPivot(),find(tend));
+}
+
+KisCurve KisCurve::subCurve(const CurvePoint& tend)
+{
+ return subCurve(find(tend).previousPivot(),find(tend));
+}
+
+KisCurve KisCurve::subCurve(iterator tend)
+{
+ return subCurve(tend.previousPivot(),tend);
+}
+
+KisCurve KisCurve::subCurve(const KisPoint& tstart, const KisPoint& tend)
+{
+ return subCurve(find(tstart),find(tend));
+}
+
+KisCurve KisCurve::subCurve(const CurvePoint& tstart, const CurvePoint& tend)
+{
+ return subCurve(find(tstart),find(tend));
+}
+
+KisCurve KisCurve::subCurve(iterator tstart, iterator tend)
+{
+ KisCurve temp;
+
+ while (tstart != tend && tstart != m_curve.end())
+ temp.pushPoint((*++tstart));
+
+ return temp;
+}
+
+void KisCurve::deleteFirstPivot ()
+{
+ if (!m_curve.isEmpty()) {
+ m_curve.pop_front();
+ while (m_curve.count() > 1 && !first().isPivot())
+ m_curve.pop_front();
+ }
+}
+
+void KisCurve::deleteLastPivot ()
+{
+ if (!m_curve.isEmpty()) {
+ m_curve.pop_back();
+ while (m_curve.count() > 1 && !last().isPivot())
+ m_curve.pop_back();
+ }
+}
+
+KisCurve::iterator KisCurve::deleteCurve (const KisPoint& pos1, const KisPoint& pos2)
+{
+ return deleteCurve (CurvePoint(pos1),CurvePoint(pos2));
+}
+
+KisCurve::iterator KisCurve::deleteCurve (const CurvePoint& pos1, const CurvePoint& pos2)
+{
+ return deleteCurve (find(pos1),find(pos2));
+}
+
+KisCurve::iterator KisCurve::deleteCurve (KisCurve::iterator pos1, KisCurve::iterator pos2)
+{
+ if (pos1 == pos2)
+ return end();
+ iterator pos = pos1;
+ pos++;
+ while (pos != pos2 && pos != end()) {
+ pos = m_curve.erase(pos.position());
+ }
+ return pos;
+}
+
+KisCurve::iterator KisCurve::selectPivot(const KisPoint& pt, bool isSelected)
+{
+ return selectPivot(find(CurvePoint(pt,true)),isSelected);
+}
+
+KisCurve::iterator KisCurve::selectPivot(const CurvePoint& pt, bool isSelected)
+{
+ return selectPivot(find(pt),isSelected);
+}
+
+KisCurve::iterator KisCurve::selectPivot(KisCurve::iterator it, bool isSelected)
+{
+ bool sel = false;
+ if (m_standardkeepselected) {
+ if (m_actionOptions & KEEPSELECTEDOPTION)
+ sel = true;
+ }
+ KisCurve selected = pivots();
+ for (iterator i = selected.begin(); i != selected.end(); i++)
+ (*find((*i))).setSelected(sel);
+ (*it).setSelected(isSelected);
+
+ return it;
+}
+
+KisCurve::iterator KisCurve::movePivot(const KisPoint& oldPt, const KisPoint& newPt)
+{
+ return movePivot(CurvePoint(oldPt,true), newPt);
+}
+
+KisCurve::iterator KisCurve::movePivot(const CurvePoint& oldPt, const KisPoint& newPt)
+{
+ return movePivot(find(oldPt), newPt);
+}
+
+KisCurve::iterator KisCurve::movePivot(KisCurve::iterator it, const KisPoint& newPt)
+{
+ if (!(*it).isPivot())
+ return end();
+
+ (*it).setPoint(newPt);
+
+ if ((*it) != first()) {
+ deleteCurve (it.previousPivot(), it);
+ calculateCurve (it.previousPivot(), it, it);
+ }
+ if ((*it) != last()) {
+ deleteCurve (it, it.nextPivot());
+ calculateCurve (it, it.nextPivot(), it.nextPivot());
+ }
+
+ return it;
+}
+
+void KisCurve::deletePivot (const KisPoint& pt)
+{
+ deletePivot(CurvePoint(pt));
+}
+
+void KisCurve::deletePivot (const CurvePoint& pt)
+{
+ deletePivot(find(pt));
+}
+
+void KisCurve::deletePivot (KisCurve::iterator it)
+{
+ if (!(*it).isPivot())
+ return;
+
+ iterator start = it.previousPivot();
+ iterator end = it.nextPivot();
+
+ if (end == m_curve.end())
+ deleteLastPivot();
+ else if (start == it)
+ deleteFirstPivot();
+ else {
+ deleteCurve(start,end);
+ calculateCurve(start,end,end);
+ }
+}
+
+// Probably it can be optimized - it is smooth though.
+void KisCurve::moveSelected (const KisPoint& trans)
+{
+ KisPoint p;
+ KisCurve sel = selectedPivots();
+
+ for (iterator it = sel.begin(); it != sel.end(); it++) {
+ p = (*it).point() + trans;
+ movePivot((*it),p);
+ }
+}
+
+void KisCurve::deleteSelected ()
+{
+ KisCurve sel = selectedPivots();
+ for (iterator it = sel.begin(); it != sel.end(); it++)
+ deletePivot((*it));
+}
+
+void KisCurve::selectAll(bool sel)
+{
+ for (iterator i = begin(); i != end(); i = i.nextPivot())
+ (*i).setSelected(sel);
+}
diff --git a/krita/plugins/tools/tool_curves/kis_curve_framework.h b/krita/plugins/tools/tool_curves/kis_curve_framework.h
new file mode 100644
index 00000000..23e326c8
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_curve_framework.h
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_CURVE_FRAMEWORK_H_
+#define KIS_CURVE_FRAMEWORK_H_
+
+#include "kis_point.h"
+
+const int NOHINTS = 0x0000;
+const int POINTHINT = 0x0001;
+const int LINEHINT = 0x0002;
+
+const int NOOPTIONS = 0x0000;
+const int SHIFTOPTION = 0x0001;
+const int CONTROLOPTION = 0x0002;
+const int ALTOPTION = 0x0004;
+
+const int KEEPSELECTEDOPTION = CONTROLOPTION;
+
+class CurvePoint {
+
+ KisPoint m_point;
+ bool m_pivot;
+ bool m_selected; // Only pivots can be selected
+
+ int m_hint;
+
+public:
+
+ /* Constructors and Destructor */
+
+ CurvePoint ();
+ CurvePoint (const KisPoint&, bool = false, bool = false, int = POINTHINT);
+ CurvePoint (double, double, bool = false, bool = false, int = POINTHINT);
+
+ ~CurvePoint () {}
+
+public:
+
+ /* Generic Functions */
+
+ bool operator!= (KisPoint p2) const { if (p2 != m_point) return true; else return false; }
+ bool operator!= (CurvePoint p2) const { if (p2.point() != m_point ||
+ p2.isPivot() != m_pivot ||
+ p2.hint() != m_hint) return true; else return false; }
+
+ bool operator== (KisPoint p2) const { if (p2 == m_point) return true; else return false; }
+ bool operator== (CurvePoint p2) const { if (p2.point() == m_point &&
+ p2.isPivot() == m_pivot &&
+ p2.hint() == m_hint) return true; else return false; }
+
+ KisPoint point() const {return m_point;}
+
+ void setPoint(const KisPoint&);
+ void setPoint(double, double);
+
+ bool isPivot() const {return m_pivot;}
+ bool isSelected() const {return m_selected;}
+ int hint() const {return m_hint;}
+
+ void setPivot(bool p) {m_pivot = p;}
+ void setSelected(bool s) {m_selected = ((m_pivot) ? s : false);} /* Only pivots can be selected */
+ void setHint(int h) {m_hint = h;}
+};
+
+typedef QValueList<CurvePoint> PointList;
+typedef QValueList<CurvePoint>::iterator BaseIterator;
+
+class CurveIterator;
+
+class KisCurve {
+
+public:
+
+ KisCurve () {m_actionOptions = NOOPTIONS; m_standardkeepselected = true;}
+ virtual ~KisCurve () {m_curve.clear();}
+
+ friend class CurveIterator;
+ typedef CurveIterator iterator;
+
+protected:
+ /* I need it to be mutable because my iterator needs to access
+ m_curve's end() and begin() functions using a const KisCurve
+ (see below in CurveIterator) */
+ mutable PointList m_curve;
+ int m_actionOptions;
+
+ bool m_standardkeepselected;
+
+ bool checkIterator (iterator checking) const;
+
+public:
+
+ void setActionOptions (int options) {m_actionOptions = options;}
+ void endActionOptions () {m_actionOptions = NOOPTIONS;}
+
+ CurvePoint& operator[](int i) {return m_curve[i];}
+
+ iterator addPoint(iterator, const CurvePoint&);
+ iterator addPoint(iterator, const KisPoint&, bool = false, bool = false, int = POINTHINT);
+
+ iterator pushPoint(const CurvePoint&);
+ iterator pushPoint(const KisPoint&, bool = false, bool = false, int = POINTHINT);
+
+ virtual iterator addPivot(iterator, const KisPoint&);
+ virtual iterator pushPivot(const KisPoint&);
+
+ int count() const {return m_curve.count();}
+ bool isEmpty() const {return m_curve.isEmpty();}
+ CurvePoint first() {return m_curve.front();}
+ CurvePoint last() {return m_curve.back();}
+ void clear() {m_curve.clear();}
+
+ /* These needs iterators so they are implemented inline after the definition of CurveIterator */
+ iterator begin() const;
+ iterator lastIterator() const;
+ iterator end() const;
+ iterator find(const CurvePoint& pt);
+ iterator find(const KisPoint& pt);
+ iterator find(iterator it, const CurvePoint& pt);
+ iterator find(iterator it, const KisPoint& pt);
+
+ KisCurve pivots();
+ KisCurve selectedPivots(bool = true);
+ KisCurve subCurve(const KisPoint&);
+ KisCurve subCurve(const CurvePoint&);
+ KisCurve subCurve(iterator);
+ KisCurve subCurve(const KisPoint&, const KisPoint&);
+ KisCurve subCurve(const CurvePoint&, const CurvePoint&);
+ KisCurve subCurve(iterator,iterator);
+
+ /* Core virtual functions */
+ virtual void deleteFirstPivot();
+ virtual void deleteLastPivot();
+
+ virtual iterator deleteCurve(const KisPoint&, const KisPoint&);
+ virtual iterator deleteCurve(const CurvePoint&, const CurvePoint&);
+ virtual iterator deleteCurve(iterator, iterator);
+
+ /* Core of the Core, calculateCurve is the only function that *needs* an implementation in the derived curves */
+ virtual void calculateCurve(const KisPoint&, const KisPoint&, iterator);
+ virtual void calculateCurve(const CurvePoint&, const CurvePoint&, iterator);
+ virtual void calculateCurve(iterator, iterator, iterator);
+ virtual void calculateCurve(iterator*);
+ virtual void calculateCurve();
+
+ virtual iterator selectPivot(const CurvePoint&, bool = true);
+ virtual iterator selectPivot(const KisPoint&, bool = true);
+ virtual iterator selectPivot(iterator, bool = true);
+
+ virtual iterator movePivot(const CurvePoint&, const KisPoint&);
+ virtual iterator movePivot(const KisPoint&, const KisPoint&);
+ virtual iterator movePivot(iterator, const KisPoint&);
+
+ virtual void deletePivot(const CurvePoint&);
+ virtual void deletePivot(const KisPoint&);
+ virtual void deletePivot(iterator);
+
+ virtual void moveSelected(const KisPoint&);
+ virtual void deleteSelected();
+ virtual void selectAll(bool = true);
+};
+
+class CurveIterator {
+
+ const KisCurve *m_target;
+
+ BaseIterator m_position;
+
+public:
+
+ CurveIterator () { m_target = 0; m_position = 0;}
+
+ CurveIterator (const KisCurve &target)
+ {m_target = &target;}
+
+ CurveIterator (const CurveIterator &it)
+ {m_position = it.position(); m_target = it.target();}
+
+ CurveIterator (const KisCurve &target, BaseIterator it)
+ {m_position = it; m_target = &target;}
+
+ ~CurveIterator () {}
+
+ bool operator==(BaseIterator it) {return m_position == it;}
+ bool operator==(CurveIterator it) {return m_position == it.position();}
+ bool operator!=(BaseIterator it) {return m_position != it;}
+ bool operator!=(CurveIterator it) {return m_position != it.position();}
+
+ CurveIterator operator++() {++m_position;return *this;}
+ CurveIterator operator++(int) {CurveIterator temp = *this; m_position++; return temp;}
+ CurveIterator operator--() {--m_position;return *this;}
+ CurveIterator operator--(int) {CurveIterator temp = *this; m_position--; return temp;}
+ CurveIterator operator+=(int i) {m_position+=i;return *this;}
+ CurveIterator operator-=(int i) {m_position-=i;return *this;}
+ CurveIterator operator=(const BaseIterator &it) {m_position=it; return *this;}
+ CurvePoint& operator*() {return (*m_position);}
+
+ const KisCurve* target() const {return m_target;}
+ BaseIterator position() const {return m_position;}
+
+ CurveIterator next()
+ {
+ CurveIterator it = *this;
+ return ++it;
+ }
+
+ CurveIterator previous()
+ {
+ CurveIterator it = *this;
+ return --it;
+ }
+
+ CurveIterator previousPivot()
+ {
+ CurveIterator it = *this;
+ while (it != m_target->m_curve.begin()) {
+ it-=1;
+ if ((*it).isPivot())
+ return it;
+ }
+
+ return it;
+ }
+
+ CurveIterator nextPivot()
+ {
+ CurveIterator it = *this;
+ while (it != m_target->m_curve.end()) {
+ it+=1;
+ if ((*it).isPivot())
+ return it;
+ }
+ return it;
+ }
+};
+
+/* ************************************* *
+ * CurvePoint inline methods definitions *
+ * ************************************* */
+
+inline CurvePoint::CurvePoint ()
+ : m_pivot(0), m_selected(0), m_hint(POINTHINT)
+{
+
+}
+
+inline CurvePoint::CurvePoint (const KisPoint& pt, bool p, bool s, int h)
+ : m_pivot(p), m_selected((p) ? s : false), m_hint(h)
+{
+ m_point = pt;
+}
+
+inline CurvePoint::CurvePoint (double x, double y, bool p, bool s, int h)
+ : m_pivot(p), m_selected((p) ? s : false), m_hint(h)
+{
+ KisPoint tmp(x,y);
+ m_point = tmp;
+}
+
+inline void CurvePoint::setPoint(const KisPoint& p)
+{
+ m_point = p;
+}
+
+inline void CurvePoint::setPoint(double x, double y)
+{
+ KisPoint tmp(x,y);
+ m_point = tmp;
+}
+
+
+/* *********************************** *
+ * KisCurve inline methods definitions *
+ * *********************************** */
+
+inline bool KisCurve::checkIterator (KisCurve::iterator checking) const
+{
+ if (checking.target() != this)
+ return false;
+ else
+ return true;
+}
+
+inline KisCurve::iterator KisCurve::begin() const
+{
+ return iterator(*this,m_curve.begin());
+}
+
+inline KisCurve::iterator KisCurve::lastIterator() const
+{
+ return (iterator(*this,--m_curve.end()));
+}
+
+inline KisCurve::iterator KisCurve::end() const
+{
+ return iterator(*this,m_curve.end());
+}
+
+inline KisCurve::iterator KisCurve::find (const CurvePoint& pt)
+{
+ return iterator(*this,m_curve.find(pt));
+}
+
+inline KisCurve::iterator KisCurve::find (const KisPoint& pt)
+{
+ return iterator(*this,m_curve.find(CurvePoint(pt)));
+}
+
+inline KisCurve::iterator KisCurve::find (KisCurve::iterator it, const CurvePoint& pt)
+{
+ return iterator(*this,m_curve.find(it.position(),pt));
+}
+
+inline KisCurve::iterator KisCurve::find (iterator it, const KisPoint& pt)
+{
+ return iterator(*this,m_curve.find(it.position(),CurvePoint(pt)));
+}
+
+inline void KisCurve::calculateCurve(const KisPoint& start, const KisPoint& end, KisCurve::iterator it)
+{
+ calculateCurve(find(CurvePoint(start)),find(CurvePoint(end)),it);
+}
+
+inline void KisCurve::calculateCurve(const CurvePoint& start, const CurvePoint& end, KisCurve::iterator it)
+{
+ calculateCurve(find(start),find(end),it);
+}
+
+inline void KisCurve::calculateCurve(KisCurve::iterator, KisCurve::iterator, KisCurve::iterator)
+{
+ return;
+}
+
+/* Really generic functions, provided if someone _really_ needs them: array of iterators and no iterators. */
+inline void KisCurve::calculateCurve(KisCurve::iterator*) {return;}
+inline void KisCurve::calculateCurve() {return;}
+
+#endif // KIS_CURVE_FRAMEWORK_H_
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier.cc b/krita/plugins/tools/tool_curves/kis_tool_bezier.cc
new file mode 100644
index 00000000..b6d16f78
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier.cc
@@ -0,0 +1,366 @@
+/*
+ * kis_tool_bezier.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <math.h>
+
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#include "kis_global.h"
+#include "kis_doc.h"
+#include "kis_painter.h"
+#include "kis_point.h"
+#include "kis_canvas_subject.h"
+#include "kis_canvas_controller.h"
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_cursor.h"
+#include "kis_vec.h"
+
+#include "kis_curve_framework.h"
+#include "kis_tool_bezier.h"
+
+KisCurve::iterator KisCurveBezier::groupEndpoint (KisCurve::iterator it) const
+{
+ iterator temp = it;
+ if ((*it).hint() == BEZIERNEXTCONTROLHINT)
+ temp -= 1;
+ if ((*it).hint() == BEZIERPREVCONTROLHINT)
+ temp += 1;
+ return temp;
+}
+
+KisCurve::iterator KisCurveBezier::groupPrevControl (KisCurve::iterator it) const
+{
+ iterator temp = it;
+ if ((*it).hint() == BEZIERENDHINT)
+ temp -= 1;
+ if ((*it).hint() == BEZIERNEXTCONTROLHINT)
+ temp -= 2;
+ return temp;
+}
+
+KisCurve::iterator KisCurveBezier::groupNextControl (KisCurve::iterator it) const
+{
+ iterator temp = it;
+ if ((*it).hint() == BEZIERENDHINT)
+ temp += 1;
+ if ((*it).hint() == BEZIERPREVCONTROLHINT)
+ temp += 2;
+ return temp;
+}
+
+bool KisCurveBezier::groupSelected (KisCurve::iterator it) const
+{
+ if ((*groupPrevControl(it)).isSelected() || (*groupEndpoint(it)).isSelected() || (*groupNextControl(it)).isSelected())
+ return true;
+ return false;
+}
+
+KisCurve::iterator KisCurveBezier::nextGroupEndpoint (KisCurve::iterator it) const
+{
+ iterator temp = it;
+ if ((*it).hint() == BEZIERPREVCONTROLHINT) {
+ temp += 2;
+ temp = temp.nextPivot();
+ }
+ if ((*it).hint() == BEZIERENDHINT) {
+ temp += 1;
+ temp = temp.nextPivot();
+ }
+ if ((*it).hint() == BEZIERNEXTCONTROLHINT) {
+ temp = temp.nextPivot();
+ }
+ temp = temp.nextPivot();
+ return temp;
+}
+
+KisCurve::iterator KisCurveBezier::prevGroupEndpoint (KisCurve::iterator it) const
+{
+ iterator temp = it;
+ if ((*it).hint() == BEZIERNEXTCONTROLHINT) {
+ temp -= 1;
+ temp = temp.previousPivot().previousPivot();
+ }
+ if ((*it).hint() == BEZIERENDHINT) {
+ temp = temp.previousPivot().previousPivot();
+ }
+ if ((*it).hint() == BEZIERPREVCONTROLHINT) {
+ temp = temp.previousPivot();
+ }
+ temp = temp.previousPivot();
+ return temp;
+}
+
+KisPoint KisCurveBezier::midpoint (const KisPoint& P1, const KisPoint& P2)
+{
+ KisPoint temp;
+ temp.setX((P1.x()+P2.x())/2);
+ temp.setY((P1.y()+P2.y())/2);
+ return temp;
+}
+
+void KisCurveBezier::recursiveCurve (const KisPoint& P1, const KisPoint& P2, const KisPoint& P3,
+ const KisPoint& P4, int level, KisCurve::iterator it)
+{
+ if (level > m_maxLevel) {
+ addPoint(it,midpoint(P1,P4),false,false,LINEHINT);
+ return;
+ }
+
+ KisPoint L1, L2, L3, L4;
+ KisPoint H, R1, R2, R3, R4;
+
+ L1 = P1;
+ L2 = midpoint(P1, P2);
+ H = midpoint(P2, P3);
+ R3 = midpoint(P3, P4);
+ R4 = P4;
+ L3 = midpoint(L2, H);
+ R2 = midpoint(R3, H);
+ L4 = midpoint(L3, R2);
+ R1 = L4;
+ recursiveCurve(L1, L2, L3, L4, level + 1, it);
+ recursiveCurve(R1, R2, R3, R4, level + 1, it);
+}
+
+void KisCurveBezier::calculateCurve(KisCurve::iterator tstart, KisCurve::iterator tend, KisCurve::iterator)
+{
+ if (pivots().count() < 4)
+ return;
+
+ iterator origin, dest, control1, control2;
+
+ if ((*tstart).hint() == BEZIERENDHINT) {
+ origin = tstart;
+ control1 = tstart.nextPivot();
+ } else if ((*tstart).hint() == BEZIERNEXTCONTROLHINT) {
+ origin = tstart.previousPivot();
+ control1 = tstart;
+ } else if ((*tstart).hint() == BEZIERPREVCONTROLHINT) {
+ origin = tstart.nextPivot();
+ control1 = origin.nextPivot();
+ } else
+ return;
+
+ if ((*tend).hint() == BEZIERENDHINT) {
+ dest = tend;
+ control2 = tend.previousPivot();
+ } else if ((*tend).hint() == BEZIERPREVCONTROLHINT) {
+ dest = tend.nextPivot();
+ control2 = tend;
+ } else if ((*tend).hint() == BEZIERNEXTCONTROLHINT) {
+ dest = tend.previousPivot();
+ control2 = dest.previousPivot();
+ } else
+ return;
+
+ deleteCurve(control1,control2);
+ recursiveCurve((*origin).point(),(*control1).point(),(*control2).point(),(*dest).point(),1,control2);
+
+}
+
+KisCurve::iterator KisCurveBezier::pushPivot (const KisPoint& point)
+{
+ iterator it;
+
+ it = pushPoint(point,true,false,BEZIERENDHINT);
+ if (count() > 1)
+ addPoint(it,point,true,false,BEZIERPREVCONTROLHINT);
+
+ it = pushPoint(point,true,false,BEZIERNEXTCONTROLHINT);
+
+ return selectPivot(it);
+}
+
+KisCurve::iterator KisCurveBezier::movePivot(KisCurve::iterator it, const KisPoint& newPt)
+{
+ if (!(*it).isPivot())
+ return end();
+
+ int hint = (*it).hint();
+ iterator thisEnd, prevEnd, nextEnd;
+
+ thisEnd = groupEndpoint(it);
+ prevEnd = prevGroupEndpoint(it);
+ nextEnd = nextGroupEndpoint(it);
+
+ if (hint == BEZIERENDHINT) {
+ KisPoint trans = newPt - (*it).point();
+ (*thisEnd).setPoint((*thisEnd).point()+trans);
+ (*thisEnd.previous()).setPoint((*thisEnd.previous()).point()+trans);
+ (*thisEnd.next()).setPoint((*thisEnd.next()).point()+trans);
+ } else if (!(m_actionOptions & KEEPSELECTEDOPTION))
+ (*it).setPoint(newPt);
+ if (!(m_actionOptions & KEEPSELECTEDOPTION) && hint != BEZIERENDHINT) {
+ if (nextEnd == end() || (m_actionOptions & SYMMETRICALCONTROLSOPTION)) {
+ KisPoint trans = (*it).point() - (*thisEnd).point();
+ trans = KisPoint(-trans.x()*2,-trans.y()*2);
+ if (hint == BEZIERNEXTCONTROLHINT)
+ (*groupPrevControl(it)).setPoint(newPt+trans);
+ else
+ (*groupNextControl(it)).setPoint(newPt+trans);
+ }
+ }
+
+ if (nextEnd != end() && count() > 4)
+ calculateCurve (thisEnd,nextEnd,iterator());
+ if (prevEnd != thisEnd && count() > 4)
+ calculateCurve (prevEnd,thisEnd,iterator());
+
+ return it;
+}
+
+void KisCurveBezier::deletePivot (KisCurve::iterator it)
+{
+ if (!(*it).isPivot())
+ return;
+
+ iterator prevControl,thisEnd,nextControl;
+
+ prevControl = prevGroupEndpoint(it).nextPivot();
+ thisEnd = groupEndpoint(it);
+ nextControl = nextGroupEndpoint(it).previousPivot();
+
+ if ((*thisEnd) == first()) {
+ deleteFirstPivot();
+ deleteFirstPivot();
+ deleteFirstPivot();
+ } else if ((*thisEnd.next()) == last()) {
+ deleteLastPivot();
+ deleteLastPivot();
+ deleteLastPivot();
+ } else {
+ deleteCurve(prevControl,nextControl);
+ calculateCurve(prevControl,nextControl,iterator());
+ }
+}
+
+KisToolBezier::KisToolBezier(const QString& UIName)
+ : super(UIName)
+{
+ m_derivated = new KisCurveBezier;
+ m_curve = m_derivated;
+
+ m_supportMinimalDraw = false;
+
+ m_transactionMessage = i18n("Bezier Curve");
+}
+
+KisToolBezier::~KisToolBezier()
+{
+
+}
+
+KisCurve::iterator KisToolBezier::handleUnderMouse(const QPoint& pos)
+{
+ QPoint qpos;
+ KisCurve pivs = m_curve->pivots(), inHandle;
+ KisCurve::iterator it;
+ int hint;
+ for (it = pivs.begin(); it != pivs.end(); it++) {
+ qpos = m_subject->canvasController()->windowToView((*it).point().toQPoint());
+ hint = (*it).hint();
+ if (hint != BEZIERENDHINT && !m_derivated->groupSelected(it))
+ continue;
+ if (hint == BEZIERENDHINT && (m_actionOptions & SHIFTOPTION))
+ continue;
+ if (pivotRect(qpos).contains(pos)) {
+ inHandle.pushPoint((*it));
+ if (hint == BEZIERENDHINT && !(m_actionOptions & SHIFTOPTION))
+ break;
+ if (hint != BEZIERENDHINT && (m_actionOptions & SHIFTOPTION))
+ break;
+ }
+ }
+ if (inHandle.isEmpty())
+ return m_curve->end();
+
+ return m_curve->find(inHandle.last());
+}
+
+KisCurve::iterator KisToolBezier::drawPoint (KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ if ((*point).hint() != BEZIERENDHINT)
+ return ++point;
+
+ KisCanvasController *controller = m_subject->canvasController();
+
+ // Now draw the bezier
+
+ KisCurve::iterator origin,control1,control2,destination;
+
+ origin = point;
+ control1 = origin.next();
+ control2 = control1.nextPivot();
+ destination = control2.next();
+
+ if (control2 != m_curve->end()) {
+ point = control2;
+ QPointArray vec(4);
+ vec[0] = controller->windowToView((*origin).point().toQPoint());
+ vec[1] = controller->windowToView((*control1).point().toQPoint());
+ vec[2] = controller->windowToView((*control2).point().toQPoint());
+ vec[3] = controller->windowToView((*destination).point().toQPoint());
+ gc.drawCubicBezier(vec);
+ }
+
+ point += 1;
+
+ return point;
+}
+
+void KisToolBezier::drawPivotHandle (KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ if ((*point).hint() != BEZIERENDHINT)
+ return;
+
+ KisCanvasController *controller = m_subject->canvasController();
+
+ QPoint endpPos = controller->windowToView((*point).point().toQPoint());
+
+ if (!m_derivated->groupSelected(point)) {
+ gc.setPen(m_pivotPen);
+ gc.drawRoundRect(pivotRect(endpPos),m_pivotRounding,m_pivotRounding);
+ } else {
+ QPoint nextControlPos = controller->windowToView((*point.next()).point().toQPoint());
+ QPoint prevControlPos = controller->windowToView((*point.previousPivot()).point().toQPoint());
+
+ gc.setPen(m_selectedPivotPen);
+ gc.drawRoundRect(selectedPivotRect(endpPos),m_selectedPivotRounding,m_selectedPivotRounding);
+ if ((prevControlPos != endpPos || nextControlPos != endpPos) && !(m_actionOptions & CONTROLOPTION)) {
+ gc.drawRoundRect(pivotRect(nextControlPos),m_pivotRounding,m_pivotRounding);
+ gc.drawLine(endpPos,nextControlPos);
+ gc.drawRoundRect(pivotRect(prevControlPos),m_pivotRounding,m_pivotRounding);
+ gc.drawLine(prevControlPos,endpPos);
+ }
+ }
+
+ gc.setPen(m_drawingPen);
+}
+
+#include "kis_tool_bezier.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier.h b/krita/plugins/tools/tool_curves/kis_tool_bezier.h
new file mode 100644
index 00000000..ec4ce7bb
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier.h
@@ -0,0 +1,97 @@
+/*
+ * kis_tool_bezier.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_BEZIER_H_
+#define KIS_TOOL_BEZIER_H_
+
+#include "kis_tool_factory.h"
+#include "kis_tool_curve.h"
+#include "kis_point.h"
+
+class CurvePoint;
+class KisPoint;
+class KisCanvas;
+class KisCurve;
+class KisPainter;
+class KisPoint;
+
+const int BEZIERENDHINT = 0x0010;
+const int BEZIERPREVCONTROLHINT = 0x0020;
+const int BEZIERNEXTCONTROLHINT = 0x0040;
+
+const int SYMMETRICALCONTROLSOPTION = ALTOPTION;
+const int PREFERCONTROLSOPTION = SHIFTOPTION;
+
+class KisCurveBezier : public KisCurve {
+
+ typedef KisCurve super;
+
+ void recursiveCurve (const KisPoint& P1, const KisPoint& P2, const KisPoint& P3,
+ const KisPoint& P4, int level, iterator it);
+ KisPoint midpoint (const KisPoint&, const KisPoint&);
+
+ int m_maxLevel;
+
+public:
+
+ KisCurveBezier() : super() {m_maxLevel = 5;}
+
+ ~KisCurveBezier() {}
+
+ virtual void calculateCurve(iterator, iterator, iterator);
+ virtual iterator pushPivot(const KisPoint&);
+ virtual iterator movePivot(iterator, const KisPoint&);
+ virtual void deletePivot(iterator);
+
+public:
+
+ iterator groupEndpoint (iterator) const;
+ iterator groupPrevControl (iterator) const;
+ iterator groupNextControl (iterator) const;
+
+ bool groupSelected (iterator) const;
+
+ iterator nextGroupEndpoint (iterator) const;
+ iterator prevGroupEndpoint (iterator) const;
+
+};
+
+class KisToolBezier : public KisToolCurve {
+
+ typedef KisToolCurve super;
+ Q_OBJECT
+
+public:
+ KisToolBezier(const QString&);
+ virtual ~KisToolBezier();
+
+protected:
+
+ virtual KisCurve::iterator handleUnderMouse(const QPoint& pos);
+ virtual void drawPivotHandle(KisCanvasPainter& gc, KisCurve::iterator point);
+ virtual KisCurve::iterator drawPoint(KisCanvasPainter& gc, KisCurve::iterator point);
+
+protected:
+
+ KisCurveBezier *m_derivated;
+
+};
+
+#endif //__KIS_TOOL_BEZIER_H__
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.cc b/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.cc
new file mode 100644
index 00000000..94ee2b79
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.cc
@@ -0,0 +1,115 @@
+/*
+ * kis_tool_curve.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include "kis_cmb_composite.h"
+#include "kis_colorspace.h"
+#include "kis_config.h"
+#include "kis_cursor.h"
+#include "kis_doc.h"
+#include "kis_global.h"
+#include "kis_image.h"
+#include "kis_int_spinbox.h"
+#include "kis_paint_device.h"
+#include "kis_painter.h"
+#include "kis_paintop_registry.h"
+#include "kis_point.h"
+#include "kis_tool_controller.h"
+#include "kis_tool_paint.h"
+
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_canvas_subject.h"
+
+#include "kis_curve_framework.h"
+#include "kis_tool_bezier_paint.h"
+
+KisToolBezierPaint::KisToolBezierPaint()
+ : super(i18n("Bezier Painting Tool"))
+{
+ setName("tool_bezier_paint");
+ m_cursor = "tool_bezier_cursor.png";
+ setCursor(KisCursor::load(m_cursor, 6, 6));
+}
+
+KisToolBezierPaint::~KisToolBezierPaint()
+{
+
+}
+
+KisCurve::iterator KisToolBezierPaint::paintPoint (KisPainter& painter, KisCurve::iterator point)
+{
+ KisCurve::iterator origin,destination,control1,control2;
+ switch ((*point).hint()) {
+ case BEZIERENDHINT:
+ origin = point++;
+ control1 = point;
+ control2 = control1.nextPivot();
+ destination = control2.next();
+ if (m_curve->count() > 4 && (*point) != m_curve->last()) {
+ point = point.nextPivot().next();
+ painter.paintAt((*origin).point(),PRESSURE_DEFAULT,0,0);
+ painter.paintBezierCurve((*origin).point(),PRESSURE_DEFAULT,0,0,(*control1).point(),
+ (*control2).point(),(*destination).point(),PRESSURE_DEFAULT,0,0,0);
+ }
+ break;
+ default:
+ point = super::paintPoint(painter,point);
+ }
+
+ return point;
+}
+
+void KisToolBezierPaint::setup(KActionCollection *collection)
+{
+ m_action = static_cast<KRadioAction *>(collection->action(name()));
+
+ if (m_action == 0) {
+ KShortcut shortcut(Qt::Key_Plus);
+ shortcut.append(KShortcut(Qt::Key_F9));
+ m_action = new KRadioAction(i18n("&Bezier"),
+ "tool_bezier_paint",
+ shortcut,
+ this,
+ SLOT(activate()),
+ collection,
+ name());
+ Q_CHECK_PTR(m_action);
+
+ m_action->setToolTip(i18n("Draw cubic beziers. Keep Alt, Control or Shift pressed for options. Return or double-click to finish."));
+ m_action->setExclusiveGroup("tools");
+ m_ownAction = true;
+ }
+}
+
+#include "kis_tool_bezier_paint.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.h b/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.h
new file mode 100644
index 00000000..052f23e6
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier_paint.h
@@ -0,0 +1,62 @@
+/*
+ * kis_tool_curve_paint.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_BEZIER_PAINT_H_
+#define KIS_TOOL_BEZIER_PAINT_H_
+
+#include "kis_tool_factory.h"
+#include "kis_tool_bezier.h"
+#include "kis_point.h"
+
+class KisToolBezierPaint : public KisToolBezier {
+
+ typedef KisToolBezier super;
+ Q_OBJECT
+
+public:
+ KisToolBezierPaint();
+ virtual ~KisToolBezierPaint();
+
+ virtual void setup(KActionCollection *collection);
+ virtual enumToolType toolType() { return TOOL_SHAPE; }
+ virtual Q_UINT32 priority() { return 7; }
+
+protected:
+
+ virtual KisCurve::iterator paintPoint(KisPainter& painter, KisCurve::iterator point);
+
+};
+
+class KisToolBezierPaintFactory : public KisToolFactory {
+ typedef KisToolFactory super;
+public:
+ KisToolBezierPaintFactory() : super() {};
+ virtual ~KisToolBezierPaintFactory(){};
+
+ virtual KisTool * createTool(KActionCollection * ac) {
+ KisTool * t = new KisToolBezierPaint();
+ Q_CHECK_PTR(t);
+ t->setup(ac);
+ return t;
+ }
+ virtual KisID id() { return KisID("beziershape", i18n("Bezier Painting Tool")); }
+};
+
+#endif //__KIS_TOOL_CURVE_PAINT_H_
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier_select.cc b/krita/plugins/tools/tool_curves/kis_tool_bezier_select.cc
new file mode 100644
index 00000000..94ebde37
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier_select.cc
@@ -0,0 +1,104 @@
+/*
+ * kis_tool_curve.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include "kis_cmb_composite.h"
+#include "kis_colorspace.h"
+#include "kis_config.h"
+#include "kis_cursor.h"
+#include "kis_doc.h"
+#include "kis_global.h"
+#include "kis_image.h"
+#include "kis_int_spinbox.h"
+#include "kis_paint_device.h"
+#include "kis_painter.h"
+#include "kis_paintop_registry.h"
+#include "kis_point.h"
+#include "kis_tool_controller.h"
+#include "kis_tool_paint.h"
+
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_canvas_subject.h"
+
+#include "kis_curve_framework.h"
+#include "kis_tool_bezier_select.h"
+
+KisToolBezierSelect::KisToolBezierSelect()
+ : super(i18n("Bezier Selection Tool"))
+{
+ setName("tool_bezier_select");
+ m_cursor = "tool_bezier_cursor.png";
+ setCursor(KisCursor::load(m_cursor, 6, 6));
+}
+
+KisToolBezierSelect::~KisToolBezierSelect()
+{
+
+}
+
+QValueVector<KisPoint> KisToolBezierSelect::convertCurve()
+{
+ QValueVector<KisPoint> points;
+
+ for (KisCurve::iterator i = m_curve->begin(); i != m_curve->end(); i++) {
+ if (((*i).hint() != BEZIERPREVCONTROLHINT) && ((*i).hint() != BEZIERNEXTCONTROLHINT))
+ points.append((*i).point());
+ }
+
+ return points;
+}
+
+void KisToolBezierSelect::setup(KActionCollection *collection)
+{
+ m_action = static_cast<KRadioAction *>(collection->action(name()));
+
+ if (m_action == 0) {
+ KShortcut shortcut(Qt::Key_Plus);
+ shortcut.append(KShortcut(Qt::Key_F9));
+ m_action = new KRadioAction(i18n("&Bezier"),
+ "tool_bezier_select",
+ shortcut,
+ this,
+ SLOT(activate()),
+ collection,
+ name());
+ Q_CHECK_PTR(m_action);
+
+ m_action->setToolTip(i18n("Select areas of the image with bezier paths."));
+ m_action->setExclusiveGroup("tools");
+ m_ownAction = true;
+ }
+}
+
+#include "kis_tool_bezier_select.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_bezier_select.h b/krita/plugins/tools/tool_curves/kis_tool_bezier_select.h
new file mode 100644
index 00000000..ac596eeb
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_bezier_select.h
@@ -0,0 +1,62 @@
+/*
+ * kis_tool_curve_paint.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_BEZIER_SELECT_H_
+#define KIS_TOOL_BEZIER_SELECT_H_
+
+#include "kis_tool_factory.h"
+#include "kis_tool_bezier.h"
+#include "kis_point.h"
+
+class KisToolBezierSelect : public KisToolBezier {
+
+ typedef KisToolBezier super;
+ Q_OBJECT
+
+public:
+ KisToolBezierSelect();
+ virtual ~KisToolBezierSelect();
+
+ virtual void setup(KActionCollection *collection);
+ virtual enumToolType toolType() { return TOOL_SELECT; }
+ virtual Q_UINT32 priority() { return 10; }
+
+protected:
+
+ virtual QValueVector<KisPoint> convertCurve();
+
+};
+
+class KisToolBezierSelectFactory : public KisToolFactory {
+ typedef KisToolFactory super;
+public:
+ KisToolBezierSelectFactory() : super() {};
+ virtual ~KisToolBezierSelectFactory(){};
+
+ virtual KisTool * createTool(KActionCollection * ac) {
+ KisTool * t = new KisToolBezierSelect();
+ Q_CHECK_PTR(t);
+ t->setup(ac);
+ return t;
+ }
+ virtual KisID id() { return KisID("bezierselection", i18n("Bezier Selection Tool")); }
+};
+
+#endif //__KIS_TOOL_CURVE_PAINT_H_
diff --git a/krita/plugins/tools/tool_curves/kis_tool_curve.cc b/krita/plugins/tools/tool_curves/kis_tool_curve.cc
new file mode 100644
index 00000000..526311e3
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_curve.cc
@@ -0,0 +1,593 @@
+/*
+ * kis_tool_curve.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <math.h>
+#include <limits.h>
+
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kis_global.h"
+#include "kis_doc.h"
+#include "kis_painter.h"
+#include "kis_point.h"
+#include "kis_canvas_subject.h"
+#include "kis_canvas_controller.h"
+#include "kis_button_press_event.h"
+#include "kis_button_release_event.h"
+#include "kis_move_event.h"
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_cursor.h"
+#include "kis_tool_controller.h"
+#include "kis_vec.h"
+#include "kis_selection.h"
+#include "kis_selection_options.h"
+#include "kis_selected_transaction.h"
+#include "kis_paintop_registry.h"
+
+#include "kis_curve_framework.h"
+#include "kis_tool_curve.h"
+
+QRect KisToolCurve::pivotRect (const QPoint& pos)
+{
+ return QRect (pos-QPoint(4,4),pos+QPoint(4,4));
+}
+
+QRect KisToolCurve::selectedPivotRect (const QPoint& pos)
+{
+ return QRect (pos-QPoint(5,5),pos+QPoint(5,5));
+}
+
+KisToolCurve::KisToolCurve(const QString& UIName)
+ : super(UIName)
+{
+ m_UIName = UIName;
+ m_currentImage = 0;
+ m_optWidget = 0;
+
+ m_curve = 0;
+
+ m_dragging = false;
+ m_draggingCursor = false;
+ m_drawPivots = true;
+ m_drawingPen = QPen(Qt::white, 0, Qt::SolidLine);
+ m_pivotPen = QPen(Qt::gray, 0, Qt::SolidLine);
+ m_selectedPivotPen = QPen(Qt::yellow, 0, Qt::SolidLine);
+ m_pivotRounding = m_selectedPivotRounding = 55;
+
+ m_actionOptions = NOOPTIONS;
+ m_supportMinimalDraw = true;
+ m_selectAction = SELECTION_ADD;
+}
+
+KisToolCurve::~KisToolCurve()
+{
+
+}
+
+void KisToolCurve::update (KisCanvasSubject *subject)
+{
+ super::update(subject);
+ if (m_subject)
+ m_currentImage = m_subject->currentImg();
+}
+
+void KisToolCurve::deactivate()
+{
+ draw(false);
+ if (m_curve) {
+ m_curve->clear();
+ m_curve->endActionOptions();
+ }
+
+ m_actionOptions = NOOPTIONS;
+ m_dragging = false;
+ m_drawPivots = true;
+}
+
+void KisToolCurve::buttonPress(KisButtonPressEvent *event)
+{
+ updateOptions(event->state());
+ if (!m_currentImage)
+ return;
+ if (event->button() == Qt::LeftButton) {
+ m_dragging = true;
+ m_currentPoint = event->pos();
+ PointPair temp = pointUnderMouse (m_subject->canvasController()->windowToView(event->pos().toQPoint()));
+ if (temp.first == m_curve->end() && !(m_actionOptions)) {
+ draw(true, true);
+ m_curve->selectAll(false);
+ draw(true, true);
+ draw(m_curve->end());
+ m_previous = m_curve->find(m_curve->last());
+ m_current = m_curve->pushPivot(event->pos());
+ if (m_curve->pivots().count() > 1)
+ m_curve->calculateCurve(m_previous,m_current,m_current);
+ draw(m_current);
+ } else {
+ draw(true, true);
+ if (temp.second)
+ m_current = m_curve->selectPivot(temp.first);
+ else
+ m_current = selectByMouse(temp.first);
+
+ if (!(*m_current).isSelected())
+ m_dragging = false;
+ draw(true, true);
+ }
+ }
+}
+
+void KisToolCurve::keyPress(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Return) {
+ m_dragging = false;
+ commitCurve();
+ } else
+ if (event->key() == Qt::Key_Escape) {
+ m_dragging = false;
+ draw(false);
+ m_curve->clear();
+ } else
+ if (event->key() == Qt::Key_Delete) {
+ draw(false);
+ m_dragging = false;
+ m_curve->deleteSelected();
+ m_current = m_curve->find(m_curve->last());
+ m_previous = m_curve->selectPivot(m_current);
+ draw(false);
+ }
+}
+
+void KisToolCurve::keyRelease(QKeyEvent *)
+{
+
+}
+
+void KisToolCurve::buttonRelease(KisButtonReleaseEvent *event)
+{
+ updateOptions(event->state());
+ m_dragging = false;
+}
+
+void KisToolCurve::doubleClick(KisDoubleClickEvent *)
+{
+ commitCurve();
+}
+
+void KisToolCurve::move(KisMoveEvent *event)
+{
+ updateOptions(event->state());
+ PointPair temp = pointUnderMouse(m_subject->canvasController()->windowToView(event->pos().toQPoint()));
+ if (temp.first == m_curve->end() && !m_dragging) {
+ if (m_draggingCursor) {
+ setCursor(KisCursor::load(m_cursor, 6, 6));
+ m_draggingCursor = false;
+ }
+ } else {
+ setCursor(KisCursor::load("tool_curve_dragging.png", 6, 6));
+ m_draggingCursor = true;
+ }
+ if (m_dragging) {
+ draw();
+ KisPoint trans = event->pos() - m_currentPoint;
+ m_curve->moveSelected(trans);
+ m_currentPoint = event->pos();
+ draw();
+ }
+}
+
+double pointToSegmentDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
+{
+ double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
+ double distance = 0;
+ KisVector2D v0(l0), v1(l1), v(p), seg(v0-v1), dist0(v0-p), dist1(v1-p);
+
+ if (seg.length() < dist0.length() ||
+ seg.length() < dist1.length()) // the point doesn't perpendicolarly intersecate the segment (or it's too far from the segment)
+ return (double)INT_MAX;
+
+ if (lineLength > DBL_EPSILON) {
+ distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
+ distance = fabs(distance);
+ }
+
+ return distance;
+}
+
+PointPair KisToolCurve::pointUnderMouse(const QPoint& pos)
+{
+ KisCurve::iterator it, next;
+ QPoint pos1, pos2;
+ it = handleUnderMouse(pos);
+ if (it != m_curve->end())
+ return PointPair(it,true);
+
+ for (it = m_curve->begin(); it != m_curve->end(); it++) {
+ next = it.next();
+ if (next == m_curve->end() || it == m_curve->end())
+ return PointPair(m_curve->end(),false);
+ if ((*it).hint() > LINEHINT || (*next).hint() > LINEHINT)
+ continue;
+ pos1 = m_subject->canvasController()->windowToView((*it).point().toQPoint());
+ pos2 = m_subject->canvasController()->windowToView((*next).point().toQPoint());
+ if (pos1 == pos2)
+ continue;
+ if (pointToSegmentDistance(pos,pos1,pos2) <= MAXDISTANCE)
+ break;
+ }
+
+ return PointPair(it,false);
+}
+
+KisCurve::iterator KisToolCurve::handleUnderMouse(const QPoint& pos)
+{
+ KisCurve pivs = m_curve->pivots(), inHandle;
+ KisCurve::iterator it;
+ for (it = pivs.begin(); it != pivs.end(); it++) {
+ if (pivotRect(m_subject->canvasController()->windowToView((*it).point().toQPoint())).contains(pos))
+ inHandle.pushPoint((*it));
+ }
+ if (inHandle.isEmpty())
+ return m_curve->end();
+ return m_curve->find(inHandle.last());
+}
+
+KisCurve::iterator KisToolCurve::selectByMouse(KisCurve::iterator it)
+{
+ KisCurve::iterator prevPivot, nextPivot;
+
+ if ((*it).isPivot())
+ prevPivot = it;
+ else
+ prevPivot = it.previousPivot();
+ nextPivot = it.nextPivot();
+
+ m_curve->selectPivot(prevPivot);
+ (*nextPivot).setSelected(true);
+
+ return prevPivot;
+}
+
+int KisToolCurve::updateOptions(int key)
+{
+ int options = 0x0000;
+
+ if (key & Qt::ControlButton)
+ options |= CONTROLOPTION;
+
+ if (key & Qt::ShiftButton)
+ options |= SHIFTOPTION;
+
+ if (key & Qt::AltButton)
+ options |= ALTOPTION;
+
+ if (options != m_actionOptions) {
+ draw(false);
+ m_actionOptions = options;
+ m_curve->setActionOptions(m_actionOptions);
+ draw(false);
+ }
+
+ return m_actionOptions;
+}
+
+void KisToolCurve::draw(bool m, bool o)
+{
+ draw(KisCurve::iterator(), o, m);
+}
+
+void KisToolCurve::draw(KisCurve::iterator inf, bool pivotonly, bool minimal)
+{
+ if (m_curve->isEmpty())
+ return;
+ KisCanvasPainter *gc;
+ KisCanvasController *controller;
+ KisCanvas *canvas;
+ if (m_subject && m_currentImage) {
+ controller = m_subject->canvasController();
+ canvas = controller->kiscanvas();
+ gc = new KisCanvasPainter(canvas);
+ } else
+ return;
+
+ gc->setPen(m_drawingPen);
+ gc->setRasterOp(Qt::XorROP);
+
+ KisCurve::iterator it, finish;
+
+ if (minimal && m_supportMinimalDraw) {
+ if (pivotonly) {
+ KisCurve p = m_curve->pivots();
+ for (KisCurve::iterator i = p.begin(); i != p.end(); i++)
+ drawPivotHandle (*gc, i);
+ delete gc;
+ return;
+ }
+ if (inf.target() != 0) {
+ if (inf != m_curve->end()) {
+ it = inf.previousPivot();
+ finish = inf.nextPivot();
+ } else {
+ it = --m_curve->end();
+ finish = m_curve->end();
+ }
+ } else {
+ KisCurve sel = m_curve->selectedPivots();
+ if (sel.isEmpty()) {
+ delete gc;
+ return;
+ }
+ for (KisCurve::iterator i = sel.begin(); i != sel.end(); i++) {
+ it = m_curve->find(*i).previousPivot();
+ finish = m_curve->find(*i).nextPivot();
+ if ((*finish).isSelected())
+ finish = finish.previousPivot();
+ while (it != finish) {
+ if ((*it).isPivot())
+ drawPivotHandle (*gc, it);
+ it = drawPoint (*gc, it);
+ }
+ }
+ delete gc;
+ return;
+ }
+ } else {
+ it = m_curve->begin();
+ finish = m_curve->end();
+ }
+ while (it != finish) {
+ if ((*it).isPivot())
+ drawPivotHandle (*gc, it);
+ it = drawPoint (*gc, it);
+ }
+
+ delete gc;
+}
+
+KisCurve::iterator KisToolCurve::drawPoint(KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ KisCanvasController *controller = m_subject->canvasController();
+
+ QPoint pos1, pos2;
+ pos1 = controller->windowToView((*point).point().toQPoint());
+
+ switch ((*point).hint()) {
+ case POINTHINT:
+ gc.drawPoint(pos1);
+ point += 1;
+ break;
+ case LINEHINT:
+ gc.drawPoint(pos1);
+ if (++point != m_curve->end() && (*point).hint() <= LINEHINT) {
+ pos2 = controller->windowToView((*point).point().toQPoint());
+ gc.drawLine(pos1,pos2);
+ }
+ break;
+ default:
+ point += 1;
+ }
+
+ return point;
+}
+
+void KisToolCurve::drawPivotHandle(KisCanvasPainter& gc, KisCurve::iterator point)
+{
+ KisCanvasController *controller = m_subject->canvasController();
+
+ if (m_drawPivots) {
+ QPoint pos = controller->windowToView((*point).point().toQPoint());
+ if ((*point).isSelected()) {
+ gc.setPen(m_selectedPivotPen);
+ gc.drawRoundRect(selectedPivotRect(pos),m_selectedPivotRounding,m_selectedPivotRounding);
+ } else {
+ gc.setPen(m_pivotPen);
+ gc.drawRoundRect(pivotRect(pos),m_pivotRounding,m_pivotRounding);
+ }
+ gc.setPen(m_drawingPen);
+ }
+}
+
+void KisToolCurve::paint(KisCanvasPainter&)
+{
+ draw(false);
+}
+
+void KisToolCurve::paint(KisCanvasPainter&, const QRect&)
+{
+ draw(false);
+}
+
+void KisToolCurve::commitCurve()
+{
+ if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
+ paintCurve();
+ else if (toolType() == TOOL_SELECT)
+ selectCurve();
+ else
+ kdDebug(0) << "NO SUPPORT FOR THIS TYPE OF TOOL" << endl;
+
+ m_curve->clear();
+ m_curve->endActionOptions();
+}
+
+void KisToolCurve::paintCurve()
+{
+ KisPaintDeviceSP device = m_currentImage->activeDevice ();
+ if (!device) return;
+
+ KisPainter painter (device);
+ if (m_currentImage->undo()) painter.beginTransaction (m_transactionMessage);
+
+ painter.setPaintColor(m_subject->fgColor());
+ painter.setBrush(m_subject->currentBrush());
+ painter.setOpacity(m_opacity);
+ painter.setCompositeOp(m_compositeOp);
+ KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
+ painter.setPaintOp(op); // Painter takes ownership
+
+// Call paintPoint
+ KisCurve::iterator it = m_curve->begin();
+ while (it != m_curve->end())
+ it = paintPoint(painter,it);
+// Finish
+
+ device->setDirty( painter.dirtyRect() );
+ notifyModified();
+
+ if (m_currentImage->undo()) {
+ m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
+ }
+
+ draw(false);
+}
+
+KisCurve::iterator KisToolCurve::paintPoint (KisPainter& painter, KisCurve::iterator point)
+{
+ KisCurve::iterator next = point; next+=1;
+ switch ((*point).hint()) {
+ case POINTHINT:
+ painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
+ break;
+ case LINEHINT:
+ if (next != m_curve->end() && (*next).hint() <= LINEHINT)
+ painter.paintLine((*point++).point(), PRESSURE_DEFAULT, 0, 0, (*next).point(), PRESSURE_DEFAULT, 0, 0);
+ else
+ painter.paintAt((*point++).point(), PRESSURE_DEFAULT, 0, 0);
+ break;
+ default:
+ point += 1;
+ }
+
+ return point;
+}
+
+QValueVector<KisPoint> KisToolCurve::convertCurve()
+{
+ QValueVector<KisPoint> points;
+
+ for (KisCurve::iterator i = m_curve->begin(); i != m_curve->end(); i++)
+ if ((*i).hint() != NOHINTS)
+ points.append((*i).point());
+
+ return points;
+}
+
+void KisToolCurve::selectCurve()
+{
+ QApplication::setOverrideCursor(KisCursor::waitCursor());
+ KisPaintDeviceSP dev = m_currentImage->activeDevice();
+ bool hasSelection = dev->hasSelection();
+ KisSelectedTransaction *t = 0;
+ if (m_currentImage->undo()) t = new KisSelectedTransaction(m_transactionMessage, dev);
+ KisSelectionSP selection = dev->selection();
+
+ if (!hasSelection) {
+ selection->clear();
+ }
+
+ KisPainter painter(selection.data());
+
+ painter.setPaintColor(KisColor(Qt::black, selection->colorSpace()));
+ painter.setFillStyle(KisPainter::FillStyleForegroundColor);
+ painter.setStrokeStyle(KisPainter::StrokeStyleNone);
+ painter.setBrush(m_subject->currentBrush());
+ painter.setOpacity(OPACITY_OPAQUE);
+ KisPaintOp * op = KisPaintOpRegistry::instance()->paintOp("paintbrush", 0, &painter);
+ painter.setPaintOp(op); // And now the painter owns the op and will destroy it.
+
+ switch (m_selectAction) {
+ case SELECTION_ADD:
+ painter.setCompositeOp(COMPOSITE_OVER);
+ break;
+ case SELECTION_SUBTRACT:
+ painter.setCompositeOp(COMPOSITE_SUBTRACT);
+ break;
+ default:
+ break;
+ }
+
+ painter.paintPolygon(convertCurve());
+
+
+ if(hasSelection) {
+ QRect dirty(painter.dirtyRect());
+ dev->setDirty(dirty);
+ dev->emitSelectionChanged(dirty);
+ } else {
+ dev->setDirty();
+ dev->emitSelectionChanged();
+ }
+
+ if (m_currentImage->undo())
+ m_currentImage->undoAdapter()->addCommand(t);
+
+ QApplication::restoreOverrideCursor();
+
+ draw(false);
+}
+
+QWidget* KisToolCurve::createOptionWidget(QWidget* parent)
+{
+ if (toolType() == TOOL_SHAPE || toolType() == TOOL_FREEHAND)
+ return super::createOptionWidget(parent);
+ else if (toolType() == TOOL_SELECT)
+ return createSelectionOptionWidget(parent);
+ else
+ kdDebug(0) << "NO SUPPORT FOR THIS TOOL TYPE" << endl;
+ return 0;
+}
+
+void KisToolCurve::slotSetAction(int action) {
+ if (action >= SELECTION_ADD && action <= SELECTION_SUBTRACT)
+ m_selectAction =(enumSelectionMode)action;
+}
+
+QWidget* KisToolCurve::createSelectionOptionWidget(QWidget* parent)
+{
+ m_optWidget = new KisSelectionOptions(parent, m_subject);
+ Q_CHECK_PTR(m_optWidget);
+ m_optWidget->setCaption(m_UIName);
+
+ connect (m_optWidget, SIGNAL(actionChanged(int)), this, SLOT(slotSetAction(int)));
+
+ QVBoxLayout * l = dynamic_cast<QVBoxLayout*>(m_optWidget->layout());
+ l->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding));
+
+ return m_optWidget;
+}
+
+QWidget* KisToolCurve::optionWidget()
+{
+ if (toolType() == TOOL_SELECT)
+ return m_optWidget;
+ else
+ return super::optionWidget();
+}
+
+#include "kis_tool_curve.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_curve.h b/krita/plugins/tools/tool_curves/kis_tool_curve.h
new file mode 100644
index 00000000..17a0c418
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_curve.h
@@ -0,0 +1,204 @@
+/*
+ * kis_tool_curve.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_CURVE_H_
+#define KIS_TOOL_CURVE_H_
+
+#include <qpen.h>
+#include <qcursor.h>
+
+#include "kis_selection.h"
+#include "kis_tool_paint.h"
+#include "kis_canvas_subject.h"
+#include "kis_point.h"
+
+#include "kis_curve_framework.h"
+
+class QRect;
+class KisPainter;
+class KisSelectionOptions;
+
+typedef QPair<KisCurve::iterator,bool> PointPair;
+
+const double MAXDISTANCE = 2.5;
+double pointToSegmentDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1);
+
+class KisToolCurve : public KisToolPaint {
+
+ typedef KisToolPaint super;
+ Q_OBJECT
+
+public:
+ KisToolCurve(const QString& UIName);
+ virtual ~KisToolCurve();
+
+ virtual void update (KisCanvasSubject *subject);
+ virtual QWidget* createOptionWidget(QWidget* parent);
+
+ virtual void buttonPress(KisButtonPressEvent *event);
+ virtual void move(KisMoveEvent *event);
+ virtual void buttonRelease(KisButtonReleaseEvent *event);
+ virtual void doubleClick(KisDoubleClickEvent *event);
+ virtual void keyPress(QKeyEvent *event);
+ virtual void keyRelease(QKeyEvent *event);
+
+public slots:
+
+ virtual void deactivate();
+
+protected:
+
+ virtual void paint(KisCanvasPainter&);
+ virtual void paint(KisCanvasPainter&, const QRect&);
+
+ /* ********************** *
+ * KisToolCurve interface *
+ * ********************** */
+
+ /*
+ * This keep in sync the options of the tool with the options of the curve
+ */
+ virtual int updateOptions(int);
+
+ virtual PointPair pointUnderMouse(const QPoint& pos);
+ virtual KisCurve::iterator handleUnderMouse(const QPoint& pos);
+
+ /*
+ * Select the needed points; called after pointUnderMouse
+ */
+ virtual KisCurve::iterator selectByMouse(KisCurve::iterator it);
+
+ /*
+ * draw() initializes the KisCanvasPainter and then loop on the points of the curve for drawing them.
+ */
+ virtual void draw(bool = true, bool = false);
+ virtual void draw(KisCurve::iterator inf, bool = false, bool = true);
+
+ /*
+ * Used by draw() to draw the current point of the curve. Can draw more than one point and then returns the last one
+ */
+ virtual KisCurve::iterator drawPoint(KisCanvasPainter& gc, KisCurve::iterator point);
+
+ /*
+ * Used by draw(), if a point is a pivot, this draw the handle around it (if m_drawPivots is set to true)
+ */
+ virtual void drawPivotHandle(KisCanvasPainter& gc, KisCurve::iterator point);
+
+ /*
+ * Methods for commiting the curve
+ */
+
+ /*
+ * Called by selectCurve(), this convert m_curve to a vector of KisPoint in order to be used by paintPolygon()
+ */
+ virtual QValueVector<KisPoint> convertCurve();
+
+ /*
+ * Called by paintCurve(), it behaves essentially like drawPoint(), but this uses a KisPainter
+ */
+ virtual KisCurve::iterator paintPoint(KisPainter&, KisCurve::iterator);
+
+ /*
+ * Finish the curve: if the tool is a TOOL_SHAPE or TOOL_FREEHAND, calls paintCurve(), if it's a TOOL_SELECT, then selectCurve()
+ */
+ virtual void commitCurve();
+
+ /*
+ * Used by commitCurve() if the tool is a painting tool
+ */
+ virtual void paintCurve();
+
+ /*
+ * Used by commitCurve() if the tool is a selection tool
+ */
+ virtual void selectCurve();
+
+ /*
+ * Return the rect around a given point, assuming that that point is an unselected pivot
+ */
+ QRect pivotRect (const QPoint&);
+
+ /*
+ * Same as above for selected pivots
+ */
+ QRect selectedPivotRect (const QPoint&);
+
+protected:
+
+ KisImageSP m_currentImage;
+
+ KisCurve *m_curve;
+ KisCurve::iterator m_current;
+ KisCurve::iterator m_previous;
+ KisPoint m_currentPoint;
+
+ bool m_dragging;
+ bool m_drawPivots;
+ QPen m_drawingPen;
+ QPen m_pivotPen;
+ QPen m_selectedPivotPen;
+ int m_pivotRounding;
+ int m_selectedPivotRounding;
+
+ int m_actionOptions;
+ bool m_supportMinimalDraw;
+ bool m_draggingCursor;
+
+ QString m_transactionMessage;
+ QString m_cursor;
+
+private:
+
+ QString m_UIName;
+
+
+/* ********************************** *
+ * Selection Tools specific functions *
+ * ********************************** */
+
+public:
+
+ /*
+ * This initializes our Option Widget (called by createOptionWidget())
+ */
+ virtual QWidget* createSelectionOptionWidget(QWidget* parent);
+
+ /*
+ * This return our internal KisSelectionOptions if toolType() returns TOOL_SELECT
+ */
+ virtual QWidget* optionWidget();
+
+public slots:
+
+ /*
+ * Slot for createSelectionOptionWidget()
+ */
+ virtual void slotSetAction(int);
+
+private:
+
+ /*
+ * Members used by slotSetAction() and selectCurve()
+ */
+ KisSelectionOptions* m_optWidget;
+ enumSelectionMode m_selectAction;
+};
+
+#endif //__KIS_TOOL_CURVE_H_
diff --git a/krita/plugins/tools/tool_curves/kis_tool_example.cc b/krita/plugins/tools/tool_curves/kis_tool_example.cc
new file mode 100644
index 00000000..9fcc5d01
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_example.cc
@@ -0,0 +1,108 @@
+/*
+ * kis_tool_example.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#include "kis_global.h"
+#include "kis_doc.h"
+#include "kis_painter.h"
+#include "kis_point.h"
+#include "kis_canvas_subject.h"
+#include "kis_canvas_controller.h"
+#include "kis_button_press_event.h"
+#include "kis_button_release_event.h"
+#include "kis_move_event.h"
+#include "kis_paintop_registry.h"
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_cursor.h"
+#include "kis_vec.h"
+
+#include "kis_curve_framework.h"
+
+#include "kis_tool_example.h"
+
+
+class KisCurveExample : public KisCurve {
+
+ typedef KisCurve super;
+
+public:
+
+ KisCurveExample() : super() {}
+
+ ~KisCurveExample() {}
+
+ virtual iterator pushPivot (const KisPoint&);
+
+};
+
+KisCurve::iterator KisCurveExample::pushPivot (const KisPoint& point)
+{
+ return selectPivot(iterator(*this,m_curve.append(CurvePoint(point,true,false,LINEHINT))), true);
+}
+
+KisToolExample::KisToolExample()
+ : super(i18n("Tool for Curves - Example"))
+{
+ setName("tool_example");
+ m_cursor = "tool_example_cursor.png";
+ setCursor(KisCursor::load(m_cursor, 6, 6));
+
+ m_curve = new KisCurveExample;
+}
+
+KisToolExample::~KisToolExample()
+{
+
+}
+
+void KisToolExample::setup(KActionCollection *collection)
+{
+ m_action = static_cast<KRadioAction *>(collection->action(name()));
+
+ if (m_action == 0) {
+ KShortcut shortcut(Qt::Key_Plus);
+ shortcut.append(KShortcut(Qt::Key_F9));
+ m_action = new KRadioAction(i18n("&Example"),
+ "tool_example",
+ shortcut,
+ this,
+ SLOT(activate()),
+ collection,
+ name());
+ Q_CHECK_PTR(m_action);
+
+ m_action->setToolTip(i18n("This is a test tool for the Curve Framework."));
+ m_action->setExclusiveGroup("tools");
+ m_ownAction = true;
+ }
+}
+
+#include "kis_tool_example.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_example.h b/krita/plugins/tools/tool_curves/kis_tool_example.h
new file mode 100644
index 00000000..af1d3574
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_example.h
@@ -0,0 +1,66 @@
+/*
+ * kis_tool_example.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_EXAMPLE_H_
+#define KIS_TOOL_EXAMPLE_H_
+
+#include "kis_tool_factory.h"
+#include "kis_tool_curve.h"
+#include "kis_point.h"
+
+class CurvePoint;
+class KisPoint;
+class KisCanvas;
+class KisCurve;
+class KisPainter;
+class KisPoint;
+class WdgToolExample;
+
+class KisToolExample : public KisToolCurve {
+
+ typedef KisToolCurve super;
+ Q_OBJECT
+
+public:
+ KisToolExample();
+ virtual ~KisToolExample();
+
+ virtual void setup(KActionCollection *collection);
+ virtual enumToolType toolType() { return TOOL_SHAPE; }
+
+};
+
+class KisToolExampleFactory : public KisToolFactory {
+ typedef KisToolFactory super;
+public:
+ KisToolExampleFactory() : super() {};
+ virtual ~KisToolExampleFactory(){};
+
+ virtual KisTool * createTool(KActionCollection * ac) {
+ KisTool * t = new KisToolExample();
+ Q_CHECK_PTR(t);
+ t->setup(ac);
+ return t;
+ }
+ virtual KisID id() { return KisID("exampleshape", i18n("Example Tool")); }
+};
+
+
+#endif //__KIS_TOOL_EXAMPLE_H__
diff --git a/krita/plugins/tools/tool_curves/kis_tool_moutline.cc b/krita/plugins/tools/tool_curves/kis_tool_moutline.cc
new file mode 100644
index 00000000..75ab0205
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_moutline.cc
@@ -0,0 +1,809 @@
+/*
+ * kis_tool_moutline.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <math.h>
+#include <set>
+
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qrect.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qslider.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#include "kis_global.h"
+#include "kis_iterators_pixel.h"
+#include "kis_colorspace.h"
+#include "kis_channelinfo.h"
+#include "kis_doc.h"
+#include "kis_painter.h"
+#include "kis_point.h"
+#include "kis_canvas_subject.h"
+#include "kis_canvas_controller.h"
+#include "kis_button_press_event.h"
+#include "kis_button_release_event.h"
+#include "kis_move_event.h"
+#include "kis_canvas.h"
+#include "kis_canvas_painter.h"
+#include "kis_cursor.h"
+#include "kis_tool_controller.h"
+#include "kis_vec.h"
+#include "kis_selection.h"
+#include "kis_selection_options.h"
+#include "kis_selected_transaction.h"
+#include "kis_paintop_registry.h"
+#include "kis_convolution_painter.h"
+
+#include "kis_tool_moutline.h"
+
+using namespace std;
+
+#define RMS(a, b) (sqrt ((a) * (a) + (b) * (b)))
+#define ROUND(x) ((int) ((x) + 0.5))
+
+const int NOEDGE = 0x0000;
+
+const int ORTHOGONAL_COST = 10; // 1*10
+const int DIAGONAL_COST = 14; // sqrt(2)*10
+const int MALUS = 20; // This applies to NOEDGE nodes
+
+const int DEFAULTDIST = 40; // Default distance between two automatic pivots
+const int MAXDIST = 55; // Max distance
+const int MINDIST = 15;
+const int PAGESTEP = 5;
+
+class Node {
+
+ QPoint m_pos;
+ int m_gCost;
+ int m_hCost;
+ int m_tCost;
+ bool m_malus;
+ QPoint m_parent;
+
+public:
+
+ Node()
+ {
+ m_pos = m_parent = QPoint(-1,-1);
+ m_gCost = m_hCost = m_tCost = 0;
+ m_malus = false;
+ }
+
+ Node(const Node& node)
+ {
+ m_pos = node.pos();
+ m_gCost = node.gCost();
+ m_hCost = node.hCost();
+ m_tCost = node.tCost();
+ m_malus = node.malus();
+ m_parent = node.parent();
+ }
+
+ Node(const QPoint& parent, const QPoint& pos, int g, int h, bool malus)
+ : m_pos(pos), m_hCost(h), m_malus(malus)
+ {
+ setGCost(g);
+ m_parent = parent;
+ }
+ ~Node ()
+ {
+ }
+
+ int gCost () const {return m_gCost;}
+ int hCost () const {return m_hCost;}
+ int tCost () const {return m_tCost;}
+ bool malus () const {return m_malus;}
+ QPoint pos () const {return m_pos;}
+ int col () const {return m_pos.x();}
+ int row () const {return m_pos.y();}
+ QPoint parent () const {return m_parent;}
+
+ void setGCost (int g)
+ {
+ m_gCost = g+(m_malus?MALUS:0);
+ m_tCost = m_gCost+m_hCost;
+ }
+ void setHCost (int h)
+ {
+ m_hCost = h;
+ m_tCost = m_gCost+m_hCost;
+ }
+ void setPos (const QPoint& pos)
+ {
+ m_pos = pos;
+ }
+ void setMalus (bool malus)
+ {
+ m_malus = malus;
+ }
+ void clear ()
+ {
+ m_pos = QPoint(-1,-1);
+ }
+
+ bool operator== (const Node& n2) const
+ {
+ return m_pos == n2.pos();
+ }
+ bool operator!= (const Node& n2) const
+ {
+ return m_pos != n2.pos();
+ }
+ bool operator== (const QPoint& n2) const
+ {
+ return m_pos == n2;
+ }
+ bool operator!= (const QPoint& n2) const
+ {
+ return m_pos != n2;
+ }
+ bool operator< (const Node& n2) const
+ {
+ return m_tCost < n2.tCost();
+ }
+ bool operator> (const Node& n2) const
+ {
+ return m_tCost > n2.tCost();
+ }
+
+ QValueList<Node> getNeighbor(const GrayMatrix& src, const Node& end)
+ {
+ QPoint tmpdist;
+ QValueList<Node> temp;
+ int dcol, drow;
+ int g, h;
+ bool malus;
+ int x[8] = { 1, 1, 0,-1,-1,-1, 0, 1},
+ y[8] = { 0,-1,-1,-1, 0, 1, 1, 1};
+
+ for (int i = 0; i < 8; i++) {
+ dcol = m_pos.x() + x[i];
+ drow = m_pos.y() + y[i];
+ tmpdist = QPoint(dcol,drow) - end.pos();
+ // I use src[0] here because all cols have same number of rows
+ if (dcol == (int)src.count() || dcol < 0 ||
+ drow == (int)src[0].count() || drow < 0)
+ continue;
+ if (src[dcol][drow])
+ malus = false;
+ else
+ malus = true;
+ if (i%2)
+ g = m_gCost + DIAGONAL_COST;
+ else
+ g = m_gCost + ORTHOGONAL_COST;
+ h = ORTHOGONAL_COST * (abs(tmpdist.x()) + abs(tmpdist.y()));
+ temp.append(Node(m_pos,QPoint(dcol,drow),g,h,malus));
+ }
+ return temp;
+ }
+
+};
+
+KisKernelSP createKernel( Q_INT32 i0, Q_INT32 i1, Q_INT32 i2,
+ Q_INT32 i3, Q_INT32 i4, Q_INT32 i5,
+ Q_INT32 i6, Q_INT32 i7, Q_INT32 i8,
+ Q_INT32 factor, Q_INT32 offset )
+{
+ KisKernelSP kernel = new KisKernel();
+ kernel->width = 3;
+ kernel->height = 3;
+
+ kernel->factor = factor;
+ kernel->offset = offset;
+
+ kernel->data = new Q_INT32[9];
+ kernel->data[0] = i0;
+ kernel->data[1] = i1;
+ kernel->data[2] = i2;
+ kernel->data[3] = i3;
+ kernel->data[4] = i4;
+ kernel->data[5] = i5;
+ kernel->data[6] = i6;
+ kernel->data[7] = i7;
+ kernel->data[8] = i8;
+
+ return kernel;
+}
+
+KisCurveMagnetic::KisCurveMagnetic (KisToolMagnetic *parent)
+ : m_parent(parent)
+{
+ m_standardkeepselected = false;
+}
+
+KisCurveMagnetic::~KisCurveMagnetic ()
+{
+
+}
+
+KisCurve::iterator KisCurveMagnetic::addPivot (KisCurve::iterator it, const KisPoint& point)
+{
+ return iterator(*this,m_curve.insert(it.position(), CurvePoint(point,true,false,LINEHINT)));
+}
+
+KisCurve::iterator KisCurveMagnetic::pushPivot (const KisPoint& point)
+{
+ iterator it;
+
+ it = pushPoint(point,true,false,LINEHINT);
+// if (count() == 1 && !m_parent->editingMode())
+// addPoint(it,point,true,false,LINEHINT);
+
+ return selectPivot(it);
+}
+
+void KisCurveMagnetic::calculateCurve (KisCurve::iterator p1, KisCurve::iterator p2, KisCurve::iterator it)
+{
+ if (p1 == m_curve.end() || p2 == m_curve.end()) // It happens sometimes, for example on the first click
+ return;
+ if (m_parent->editingMode())
+ return;
+ QPoint start = (*p1).point().roundQPoint();
+ QPoint end = (*p2).point().roundQPoint();
+ QRect rc = QRect(start,end).normalize();
+ rc.setTopLeft(rc.topLeft()+QPoint(-8,-8)); // Enlarge the view, so problems with gaussian blur can be removed
+ rc.setBottomRight(rc.bottomRight()+QPoint(8,8)); // and we are able to find paths that go beyond the rect.
+
+ KisPaintDeviceSP src = m_parent->m_currentImage->activeDevice();
+ GrayMatrix dst = GrayMatrix(rc.width(),GrayCol(rc.height()));
+
+ detectEdges (rc, src, dst);
+ reduceMatrix (rc, dst, 3, 3, 3, 3);
+
+ Node startNode, endNode;
+ multiset<Node> openSet;
+ NodeMatrix openMatrix = NodeMatrix(rc.width(),NodeCol(rc.height()));
+ NodeMatrix closedMatrix = NodeMatrix(rc.width(),NodeCol(rc.height()));
+
+ QPoint tl(rc.topLeft().x(),rc.topLeft().y());
+ start -= tl; // Relative to the matrix
+ end -= tl; // Relative to the matrix
+
+ findEdge (start.x(), start.y(), dst, startNode);
+ openMatrix[startNode.col()][startNode.row()] = *openSet.insert(startNode);
+ endNode.setPos(end);
+
+ while (!openSet.empty()) {
+ Node current = *openSet.begin();
+
+ openSet.erase(openSet.begin());
+ openMatrix[current.col()][current.row()].clear();
+
+ QValueList<Node> successors = current.getNeighbor(dst,endNode);
+ for (QValueList<Node>::iterator i = successors.begin(); i != successors.end(); i++) {
+ int col = (*i).col();
+ int row = (*i).row();
+ if ((*i) == endNode) {
+ while (current.parent() != QPoint(-1,-1)) {
+ it = addPoint(it,tl+current.pos(),false,false,LINEHINT);
+ current = closedMatrix[current.parent().x()][current.parent().y()];
+ }
+ return;
+ }
+ Node *openNode = &openMatrix[col][row];
+ if (*openNode != QPoint(-1,-1)) {
+ if (*i > *openNode)
+ continue;
+ else {
+ openSet.erase(qFind(openSet.begin(),openSet.end(),*openNode));
+ openNode->clear(); // Clear the Node
+ }
+ }
+ Node *closedNode = &closedMatrix[col][row];
+ if (*closedNode != QPoint(-1,-1)) {
+ if ((*i) > (*closedNode))
+ continue;
+ else {
+ openMatrix[col][row] = *openSet.insert(*closedNode);
+ closedNode->clear(); // Clear the Node
+ continue;
+ }
+ }
+ openMatrix[col][row] = *openSet.insert(*i);
+ }
+ closedMatrix[current.col()][current.row()] = current;
+ }
+}
+
+void KisCurveMagnetic::findEdge (int col, int row, const GrayMatrix& src, Node& node)
+{
+ int x = -1;
+ int y = -1;
+
+ // tmpdist out of range
+ KisVector2D mindist(5.0,5.0), tmpdist(1000.0,1000.0);
+ for (int i = -5; i < 6; i++) {
+ for (int j = -5; j < 6; j++) {
+ if (src[col+i][row+j] != NOEDGE) {
+ tmpdist = KisVector2D(i,j);
+ if (tmpdist.length() < mindist.length())
+ mindist = tmpdist;
+ }
+ }
+ }
+ if (tmpdist.x() == 1000.0)
+ mindist = KisVector2D(0.0,0.0);
+
+ x = (int)(col + mindist.x());
+ y = (int)(row + mindist.y());
+
+ node.setPos(QPoint(x,y));
+}
+
+void KisCurveMagnetic::reduceMatrix (QRect& rc, GrayMatrix& m, int top, int right, int bottom, int left)
+{
+ QPoint topleft(top, left);
+ QPoint bottomright(bottom, right);
+
+ rc.setTopLeft(rc.topLeft()+topleft);
+ rc.setBottomRight(rc.bottomRight()-bottomright);
+
+ if (left)
+ m.erase(m.begin(),m.begin()+left);
+ if (right)
+ m.erase(m.end()-right,m.end());
+ if (top) {
+ for (uint i = 0; i < m.count(); i++)
+ m[i].erase(m[i].begin(),m[i].begin()+top);
+ }
+ if (bottom) {
+ for (uint i = 0; i < m.count(); i++)
+ m[i].erase(m[i].end()-bottom,m[i].end());
+ }
+}
+
+void KisCurveMagnetic::detectEdges (const QRect & rect, KisPaintDeviceSP src, GrayMatrix& dst)
+{
+ GrayMatrix graysrc(rect.width(),GrayCol(rect.height()));
+ GrayMatrix xdeltas(rect.width(),GrayCol(rect.height()));
+ GrayMatrix ydeltas(rect.width(),GrayCol(rect.height()));
+ GrayMatrix magnitude(rect.width(),GrayCol(rect.height()));
+ KisPaintDeviceSP smooth = new KisPaintDevice(src->colorSpace());
+
+ gaussianBlur(rect, src, smooth);
+ toGrayScale(rect, smooth, graysrc);
+ getDeltas(graysrc, xdeltas, ydeltas);
+ getMagnitude(xdeltas, ydeltas, magnitude);
+ nonMaxSupp(magnitude, xdeltas, ydeltas, dst);
+}
+
+void KisCurveMagnetic::gaussianBlur (const QRect& rect, KisPaintDeviceSP src, KisPaintDeviceSP dst)
+{
+ int grectx = rect.x();
+ int grecty = rect.y();
+ int grectw = rect.width();
+ int grecth = rect.height();
+ if (dst != src) {
+ KisPainter gc(dst);
+ gc.bitBlt(grectx, grecty, COMPOSITE_COPY, src, grectx, grecty, grectw, grecth);
+ gc.end();
+ }
+
+ KisConvolutionPainter painter( dst );
+ // FIXME createKernel could create dynamic gaussian kernels having sigma as argument
+ KisKernelSP kernel = createKernel( 1, 1, 1, 1, 24, 1, 1, 1, 1, 32, 0);
+ painter.applyMatrix(kernel, grectx, grecty, grectw, grecth, BORDER_AVOID);
+}
+
+void KisCurveMagnetic::toGrayScale (const QRect& rect, KisPaintDeviceSP src, GrayMatrix& dst)
+{
+ int grectx = rect.x();
+ int grecty = rect.y();
+ int grectw = rect.width();
+ int grecth = rect.height();
+ QColor c;
+ KisColorSpace *cs = src->colorSpace();
+
+ for (int row = 0; row < grecth; row++) {
+ KisHLineIteratorPixel srcIt = src->createHLineIterator(grectx, grecty+row, grectw, false);
+ for (int col = 0; col < grectw; col++) {
+ cs->toQColor(srcIt.rawData(),&c);
+ dst[col][row] = qGray(c.rgb());
+ ++srcIt;
+ }
+ }
+}
+
+void KisCurveMagnetic::getDeltas (const GrayMatrix& src, GrayMatrix& xdelta, GrayMatrix& ydelta)
+{
+ uint start = 1, xend = src[0].count()-1, yend = src.count()-1;
+ Q_INT16 deri;
+ for (uint col = 0; col < src.count(); col++) {
+ for (uint row = 0; row < src[col].count(); row++) {
+ if (row >= start && row < xend) {
+ deri = src[col][row+1] - src[col][row-1];
+ xdelta[col][row] = deri;
+ } else
+ xdelta[col][row] = 0;
+ if (col >= start && col < yend) {
+ deri = src[col+1][row] - src[col-1][row];
+ ydelta[col][row] = deri;
+ } else
+ ydelta[col][row] = 0;
+ }
+ }
+}
+
+void KisCurveMagnetic::getMagnitude (const GrayMatrix& xdelta, const GrayMatrix& ydelta, GrayMatrix& gradient)
+{
+ for (uint col = 0; col < xdelta.count(); col++) {
+ for (uint row = 0; row < xdelta[col].count(); row++)
+ gradient[col][row] = (Q_INT16)(ROUND(RMS(xdelta[col][row],ydelta[col][row])));
+ }
+}
+
+void KisCurveMagnetic::nonMaxSupp (const GrayMatrix& magnitude, const GrayMatrix& xdelta, const GrayMatrix& ydelta, GrayMatrix& nms)
+{
+ // Directions:
+ // 1: 0 - 22.5 degrees
+ // 2: 22.5 - 67.5 degrees
+ // 3: 67.5 - 90 degrees
+ // Second direction is relative to a quadrant. The quadrant is known by looking at x and y derivatives
+ // First quadrant: Gx < 0 & Gy >= 0
+ // Second quadrant: Gx < 0 & Gy < 0
+ // Third quadrant: Gx >= 0 & Gy < 0
+ // Fourth quadrant: Gx >= 0 & Gy >= 0
+ // For this reason: first direction is relative to Gy only and third direction to Gx only
+
+ double theta; // theta = invtan (|Gy| / |Gx|) This give the direction relative to a quadrant
+ Q_INT16 mag; // Current magnitude
+ Q_INT16 lmag; // Magnitude at the left (So this pixel is "more internal" than the current
+ Q_INT16 rmag; // Magnitude at the right (So this pixel is "more external")
+ double xdel; // Current xdelta
+ double ydel; // Current ydelta
+ Q_INT16 result;
+
+ for (uint col = 0; col < magnitude.count(); col++) {
+ for (uint row = 0; row < magnitude[col].count(); row++) {
+ mag = magnitude[col][row];
+ if (!mag || row == 0 || row == (magnitude[col].count()-1) ||
+ col == 0 || col == (magnitude.count()-1))
+ {
+ result = NOEDGE;
+ } else {
+ xdel = (double)xdelta[col][row];
+ ydel = (double)ydelta[col][row];
+ theta = atan(fabs(ydel)/fabs(xdel));
+ if (theta < 0)
+ theta = fabs(theta)+M_PI_2;
+ theta = (theta * 360.0) / (2.0*M_PI); // Radians -> degrees
+ if (theta >= 0 && theta < 22.5) { // .0 - .3926990816
+ if (ydel >= 0) {
+ lmag = magnitude[col][row-1];
+ rmag = magnitude[col][row+1];
+ } else {
+ lmag = magnitude[col][row+1];
+ rmag = magnitude[col][row-1];
+ }
+ }
+ if (theta >= 22.5 && theta < 67.5) { // .3926990816 - 1.1780972449
+ if (xdel >= 0) {
+ if (ydel >= 0) {
+ lmag = magnitude[col-1][row-1];
+ rmag = magnitude[col+1][row+1];
+ } else {
+ lmag = magnitude[col+1][row-1];
+ rmag = magnitude[col-1][row+1];
+ }
+ } else {
+ if (ydel >= 0) {
+ lmag = magnitude[col-1][row+1];
+ rmag = magnitude[col+1][row-1];
+ } else {
+ lmag = magnitude[col+1][row+1];
+ rmag = magnitude[col-1][row-1];
+ }
+ }
+ }
+ if (theta >= 67.5 && theta <= 90.0) { // 1.1780972449 - 1.5707963266
+ if (xdel >= 0) {
+ lmag = magnitude[col+1][row];
+ rmag = magnitude[col-1][row];
+ } else {
+ lmag = magnitude[col-1][row];
+ rmag = magnitude[col+1][row];
+ }
+ }
+
+ if ((mag < lmag) || (mag < rmag)) {
+ result = NOEDGE;
+ } else {
+ if (rmag == mag) // If the external magnitude is equal to the current, suppress current.
+ result = NOEDGE;
+ else
+ result = (mag > 255) ? 255 : mag;
+ }
+ }
+ nms[col][row] = result;
+ }
+ }
+}
+
+KisToolMagnetic::KisToolMagnetic ()
+ : super("Magnetic Outline Tool")
+{
+ setName("tool_moutline");
+ setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6));
+
+ m_editingMode = false;
+ m_editingCursor = m_draggingCursor = false;
+
+ m_mode = 0;
+ m_curve = m_derived = 0;
+ m_current = m_previous = 0;
+
+ m_distance = DEFAULTDIST;
+
+ m_transactionMessage = i18n("Magnetic Outline Selection");
+}
+
+KisToolMagnetic::~KisToolMagnetic ()
+{
+ m_curve = 0;
+ delete m_derived;
+}
+
+void KisToolMagnetic::update (KisCanvasSubject *subject)
+{
+ super::update(subject);
+}
+
+void KisToolMagnetic::activate ()
+{
+ super::activate();
+ if (!m_derived) {
+ m_derived = new KisCurveMagnetic(this);
+ m_curve = m_derived;
+ }
+}
+
+void KisToolMagnetic::deactivate ()
+{
+ m_curve->endActionOptions();
+ m_actionOptions = NOOPTIONS;
+ m_dragging = false;
+ m_drawPivots = true;
+}
+
+void KisToolMagnetic::keyPress(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Control) {
+ draw(false);
+ if (m_editingMode) {
+ m_editingMode = false;
+ if (m_current != 0)
+ m_curve->selectPivot(m_current,false);
+ m_mode->setText(i18n("Automatic Mode"));
+ } else {
+ m_editingMode = true;
+ m_mode->setText(i18n("Manual Mode"));
+ }
+ draw(false);
+ } else if (event->key() == Qt::Key_Delete && m_curve->count()) {
+ draw(false);
+ m_dragging = false;
+ if (m_curve->pivots().count() == 2)
+ m_curve->clear();
+ else {
+ if ((*m_current) == m_curve->last() && !(m_editingMode)) {
+ m_curve->deletePivot(m_current.previousPivot());
+ m_previous = m_current.previousPivot();
+ } else {
+ m_editingMode = false;
+ m_curve->deletePivot(m_current);
+ m_previous = m_current = m_curve->selectPivot(m_curve->lastIterator());
+ m_editingMode = true;
+ }
+ }
+ draw(false);
+ } else
+ super::keyPress(event);
+}
+
+void KisToolMagnetic::buttonRelease(KisButtonReleaseEvent *event)
+{
+ if (m_editingMode) {
+ draw(m_current);
+ m_editingMode = false;
+ if (!m_curve->isEmpty())
+ m_curve->movePivot(m_current, m_currentPoint);
+ m_editingMode = true;
+ draw(m_current);
+ }
+ super::buttonRelease(event);
+}
+
+void KisToolMagnetic::buttonPress(KisButtonPressEvent *event)
+{
+ updateOptions(event->state());
+ if (!m_currentImage)
+ return;
+ if (event->button() == Qt::LeftButton) {
+ m_dragging = true;
+ m_currentPoint = event->pos();
+ PointPair temp(m_curve->end(),false);
+ if (m_editingMode)
+ temp = pointUnderMouse (m_subject->canvasController()->windowToView(event->pos().toQPoint()));
+ if (temp.first == m_curve->end() && !(m_actionOptions)) {
+ if (m_editingMode) {
+ draw(true, true);
+ m_curve->selectAll(false);
+ draw(true, true);
+ }
+ draw(m_curve->end());
+ if (!m_curve->isEmpty()) {
+ m_previous = m_current;
+ m_current = m_curve->pushPivot(event->pos());
+ } else {
+ m_previous = m_current = m_curve->pushPivot(event->pos());
+ }
+ if (m_curve->pivots().count() > 1)
+ m_curve->calculateCurve(m_previous,m_current,m_current);
+ if (m_editingMode)
+ draw();
+ else {
+ if ((*m_previous).point() == (*m_current).point())
+ draw(m_curve->end());
+ else
+ draw();
+ }
+ } else if (temp.first != m_curve->end() && m_editingMode) {
+ if (temp.second) {
+ draw(true, true);
+ m_current = m_curve->selectPivot(temp.first);
+ draw(true, true);
+ } else {
+ draw(false);
+ m_current = selectByMouse(temp.first);
+ draw(false);
+ }
+ if (!(*m_current).isSelected())
+ m_dragging = false;
+ }
+ }
+}
+
+void KisToolMagnetic::move(KisMoveEvent *event)
+{
+ updateOptions(event->state());
+ if (m_currentPoint == event->pos().floorQPoint())
+ return;
+ if (m_editingMode) {
+ PointPair temp = pointUnderMouse(m_subject->canvasController()->windowToView(event->pos().toQPoint()));
+ if (temp.first == m_curve->end() && !m_dragging) {
+ if (m_editingCursor || m_draggingCursor) {
+ setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6));
+ m_editingCursor = m_draggingCursor = false;
+ }
+ } else {
+ if (!m_draggingCursor && temp.second) {
+ setCursor(KisCursor::load("tool_moutline_dragging.png", 6, 6));
+ m_editingCursor = false;
+ m_draggingCursor = true;
+ }
+ if (!m_editingCursor && !temp.second) {
+ setCursor(KisCursor::load("tool_moutline_editing.png", 6, 6));
+ m_editingCursor = true;
+ m_draggingCursor = false;
+ }
+ }
+ if (!m_dragging)
+ return;
+ } else {
+ if (m_editingCursor || m_draggingCursor) {
+ setCursor(KisCursor::load("tool_moutline_cursor.png", 6, 6));
+ m_editingCursor = m_draggingCursor = false;
+ }
+ }
+ if (m_curve->selectedPivots().isEmpty())
+ return;
+
+ KisPoint trans = event->pos() - m_currentPoint;
+ KisPoint dist;
+ dist = (*m_current).point() - (*m_current.previousPivot()).point();
+ if ((m_distance >= MINDIST && (fabs(dist.x()) + fabs(dist.y())) > m_distance && !(m_editingMode))
+ || m_curve->pivots().count() == 1) {
+ draw(m_curve->end());
+ m_previous = m_current;
+ m_current = m_curve->pushPivot(event->pos());
+ } else if ((*m_previous).point() == (*m_current).point() && (*m_previous).point() == m_curve->last().point())
+ draw(m_curve->end());
+ else
+ draw(m_current);
+ m_curve->movePivot(m_current,event->pos());
+ m_currentPoint = event->pos().floorQPoint();
+ draw(m_current);
+}
+
+KisCurve::iterator KisToolMagnetic::selectByMouse(KisCurve::iterator it)
+{
+ KisCurve::iterator currPivot = m_curve->selectPivot(m_curve->addPivot(it, KisPoint(0,0)));
+ m_curve->movePivot(currPivot,(*it).point());
+
+ return currPivot;
+}
+
+void KisToolMagnetic::slotCommitCurve ()
+{
+ if (!m_curve->isEmpty())
+ commitCurve();
+}
+
+void KisToolMagnetic::slotSetDistance (int dist)
+{
+ m_distance = dist;
+}
+
+QWidget* KisToolMagnetic::createOptionWidget(QWidget* parent)
+{
+ m_optWidget = super::createOptionWidget(parent);
+ QVBoxLayout * l = dynamic_cast<QVBoxLayout*>(m_optWidget->layout());
+ QGridLayout *box = new QGridLayout(l, 2, 2, 3);
+ box->setColStretch(0, 1);
+ box->setColStretch(1, 1);
+ Q_CHECK_PTR(box);
+
+ m_mode = new QLabel(i18n("Automatic mode"), m_optWidget);
+ m_lbDistance = new QLabel(i18n("Distance: "), m_optWidget);
+ QPushButton *finish = new QPushButton(i18n("To Selection"), m_optWidget);
+ m_slDistance = new QSlider(MINDIST, MAXDIST, PAGESTEP, m_distance, Qt::Horizontal, m_optWidget);
+
+ connect(m_slDistance, SIGNAL(valueChanged(int)), this, SLOT(slotSetDistance(int)));
+ connect(finish, SIGNAL(clicked()), this, SLOT(slotCommitCurve()));
+
+ box->addWidget(m_lbDistance, 0, 0);
+ box->addWidget(m_slDistance, 0, 1);
+ box->addWidget(m_mode, 1, 0);
+ box->addWidget(finish, 1, 1);
+
+ return m_optWidget;
+}
+
+void KisToolMagnetic::setup(KActionCollection *collection)
+{
+ m_action = static_cast<KRadioAction *>(collection->action(name()));
+
+ if (m_action == 0) {
+ KShortcut shortcut(Qt::Key_Plus);
+ shortcut.append(KShortcut(Qt::Key_F9));
+ m_action = new KRadioAction(i18n("Magnetic Outline"),
+ "tool_moutline",
+ shortcut,
+ this,
+ SLOT(activate()),
+ collection,
+ name());
+ Q_CHECK_PTR(m_action);
+
+ m_action->setToolTip(i18n("Magnetic Selection: move around an edge to select it. Hit Ctrl to enter/quit manual mode, and double click to finish."));
+ m_action->setExclusiveGroup("tools");
+ m_ownAction = true;
+ }
+}
+
+#include "kis_tool_moutline.moc"
diff --git a/krita/plugins/tools/tool_curves/kis_tool_moutline.h b/krita/plugins/tools/tool_curves/kis_tool_moutline.h
new file mode 100644
index 00000000..43a140be
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kis_tool_moutline.h
@@ -0,0 +1,131 @@
+/*
+ * kis_tool_moutline.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef KIS_TOOL_MOUTLINE_H_
+#define KIS_TOOL_MOUTLINE_H_
+
+#include "kis_tool_factory.h"
+#include "kis_curve_framework.h"
+#include "kis_tool_curve.h"
+
+class QSlider;
+class KisToolMagnetic;
+class KisVector2D;
+class Node;
+
+typedef QValueVector<Node> NodeCol;
+typedef QValueVector<NodeCol> NodeMatrix;
+typedef QValueVector<Q_INT16> GrayCol;
+typedef QValueVector<GrayCol> GrayMatrix;
+
+class KisCurveMagnetic : public KisCurve {
+
+ typedef KisCurve super;
+
+ KisToolMagnetic *m_parent;
+
+ void reduceMatrix (QRect&, GrayMatrix&, int, int, int, int);
+ void findEdge (int, int, const GrayMatrix&, Node&);
+ void detectEdges (const QRect&, KisPaintDeviceSP, GrayMatrix&);
+
+ void gaussianBlur (const QRect&, KisPaintDeviceSP, KisPaintDeviceSP);
+ void toGrayScale (const QRect&, KisPaintDeviceSP, GrayMatrix&);
+ void getDeltas (const GrayMatrix&, GrayMatrix&, GrayMatrix&);
+ void getMagnitude (const GrayMatrix&, const GrayMatrix&, GrayMatrix&);
+ void nonMaxSupp (const GrayMatrix&, const GrayMatrix&, const GrayMatrix&, GrayMatrix&);
+
+public:
+
+ KisCurveMagnetic (KisToolMagnetic *parent);
+ ~KisCurveMagnetic ();
+
+ virtual KisCurve::iterator addPivot (iterator, const KisPoint&);
+ virtual KisCurve::iterator pushPivot (const KisPoint&);
+ virtual void calculateCurve (iterator, iterator, iterator);
+
+};
+
+class KisToolMagnetic : public KisToolCurve {
+
+ typedef KisToolCurve super;
+ Q_OBJECT
+
+ friend class KisCurveMagnetic;
+
+public:
+
+ KisToolMagnetic();
+ ~KisToolMagnetic();
+
+ virtual void update (KisCanvasSubject*);
+ virtual void setup (KActionCollection*);
+ virtual enumToolType toolType() { return TOOL_SELECT; }
+ virtual Q_UINT32 priority() { return 9; }
+
+ virtual void keyPress(QKeyEvent*);
+ virtual void buttonPress(KisButtonPressEvent*);
+ virtual void buttonRelease(KisButtonReleaseEvent*);
+ virtual void move(KisMoveEvent*);
+
+ virtual KisCurve::iterator selectByMouse(KisCurve::iterator it);
+
+ bool editingMode() {return m_editingMode;}
+ virtual QWidget* createOptionWidget(QWidget* parent);
+
+public slots:
+
+ virtual void activate ();
+ virtual void deactivate ();
+
+ void slotCommitCurve ();
+ void slotSetDistance (int);
+
+private:
+
+ KisCurveMagnetic *m_derived;
+ QWidget* m_optWidget;
+ QLabel* m_mode;
+ QLabel* m_lbDistance;
+ QSlider* m_slDistance;
+ bool m_editingMode;
+ bool m_editingCursor;
+ bool m_draggingCursor;
+ bool m_needNewPivot;
+
+ int m_distance;
+
+};
+
+class KisToolMagneticFactory : public KisToolFactory {
+ typedef KisToolFactory super;
+public:
+ KisToolMagneticFactory() : super() {};
+ virtual ~KisToolMagneticFactory(){};
+
+ virtual KisTool * createTool(KActionCollection * ac) {
+ KisTool * t = new KisToolMagnetic();
+ Q_CHECK_PTR(t);
+ t->setup(ac);
+ return t;
+ }
+ virtual KisID id() { return KisID("magneticoutline", i18n("Magnetic Outline Selection Tool")); }
+};
+
+#endif // KIS_TOOL_MOUTLINE_H_
diff --git a/krita/plugins/tools/tool_curves/kritatoolcurves.desktop b/krita/plugins/tools/tool_curves/kritatoolcurves.desktop
new file mode 100644
index 00000000..60fddc10
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/kritatoolcurves.desktop
@@ -0,0 +1,36 @@
+[Desktop Entry]
+Name=Curves Tool
+Name[bg]=Инструмент криви
+Name[ca]=Eina de corbes
+Name[da]=Kurveværktøj
+Name[de]=Kurvenwerkzeug
+Name[el]=Εργαλείο καμπύλων
+Name[eo]=Kurvado-ilo
+Name[es]=Herramienta Curvas
+Name[et]=Kõverate tööriist
+Name[fa]=ابزار منحنیها
+Name[fy]=Krommings-ark
+Name[gl]=Ferramenta de Curvas
+Name[hu]=Görberajzoló
+Name[it]=Strumento curve
+Name[ja]=曲線ツール
+Name[km]=ឧបករណ៍​ខ្សែ​កោង
+Name[nb]=Kurveverktøy
+Name[nds]=Bagenwarktüüch
+Name[ne]=वक्र उपकरण
+Name[nl]=Krommen-gereedschap
+Name[pl]=Narzędzie krzywych
+Name[pt]=Ferramenta de Curvas
+Name[pt_BR]=Ferramenta de Curvas
+Name[ru]=Кривые
+Name[sk]=Krivky
+Name[sl]=Orodje za krivulje
+Name[sr]=Алат за криве
+Name[sr@Latn]=Alat za krive
+Name[sv]=Kurvverktyg
+Name[uk]=Криві
+Name[zh_TW]=曲線工具
+ServiceTypes=Krita/Tool
+Type=Service
+X-KDE-Library=kritatoolcurves
+X-Krita-Version=2
diff --git a/krita/plugins/tools/tool_curves/tool_bezier_cursor.png b/krita/plugins/tools/tool_curves/tool_bezier_cursor.png
new file mode 100644
index 00000000..78dae446
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_bezier_cursor.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_bezier_paint.png b/krita/plugins/tools/tool_curves/tool_bezier_paint.png
new file mode 100644
index 00000000..dcbd93d5
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_bezier_paint.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_bezier_select.png b/krita/plugins/tools/tool_curves/tool_bezier_select.png
new file mode 100644
index 00000000..84fb3036
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_bezier_select.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_curve_dragging.png b/krita/plugins/tools/tool_curves/tool_curve_dragging.png
new file mode 100644
index 00000000..e8fb92f4
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_curve_dragging.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_curves.cc b/krita/plugins/tools/tool_curves/tool_curves.cc
new file mode 100644
index 00000000..bb4b2736
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_curves.cc
@@ -0,0 +1,67 @@
+/*
+ * tool_bezier.cc -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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 <stdlib.h>
+#include <vector>
+
+#include <qpoint.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kinstance.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include <kis_global.h>
+#include <kis_types.h>
+#include <kis_tool_registry.h>
+
+#include "tool_curves.h"
+#include "kis_tool_bezier_paint.h"
+#include "kis_tool_bezier_select.h"
+#include "kis_tool_moutline.h"
+
+
+typedef KGenericFactory<ToolCurves> ToolCurvesFactory;
+K_EXPORT_COMPONENT_FACTORY( kritatoolcurves, ToolCurvesFactory( "krita" ) )
+
+
+ToolCurves::ToolCurves(QObject *parent, const char *name, const QStringList &)
+ : KParts::Plugin(parent, name)
+{
+ setInstance(ToolCurvesFactory::instance());
+
+ if ( parent->inherits("KisToolRegistry") )
+ {
+ KisToolRegistry * r = dynamic_cast<KisToolRegistry*>( parent );
+ r->add(new KisToolBezierPaintFactory());
+ r->add(new KisToolBezierSelectFactory());
+ r->add(new KisToolMagneticFactory());
+ }
+
+}
+
+ToolCurves::~ToolCurves()
+{
+}
+
+#include "tool_curves.moc"
diff --git a/krita/plugins/tools/tool_curves/tool_curves.h b/krita/plugins/tools/tool_curves/tool_curves.h
new file mode 100644
index 00000000..7a097d46
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_curves.h
@@ -0,0 +1,35 @@
+/*
+ * tool_bezier.h -- part of Krita
+ *
+ * Copyright (c) 2006 Emanuele Tamponi <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef TOOL_BEZIER_H_
+#define TOOL_BEZIER_H_
+
+#include <kparts/plugin.h>
+
+class ToolCurves : public KParts::Plugin
+{
+ Q_OBJECT
+public:
+ ToolCurves(QObject *parent, const char *name, const QStringList &);
+ virtual ~ToolCurves();
+
+};
+
+#endif // TOOL_BEZIER_H__
diff --git a/krita/plugins/tools/tool_curves/tool_example.png b/krita/plugins/tools/tool_curves/tool_example.png
new file mode 100644
index 00000000..02f821ce
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_example.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_example_cursor.png b/krita/plugins/tools/tool_curves/tool_example_cursor.png
new file mode 100644
index 00000000..78dae446
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_example_cursor.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_moutline.png b/krita/plugins/tools/tool_curves/tool_moutline.png
new file mode 100644
index 00000000..2f75d945
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_moutline.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_moutline_cursor.png b/krita/plugins/tools/tool_curves/tool_moutline_cursor.png
new file mode 100644
index 00000000..039b9e50
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_moutline_cursor.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/tool_moutline_editing.png b/krita/plugins/tools/tool_curves/tool_moutline_editing.png
new file mode 100644
index 00000000..568d9fd4
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/tool_moutline_editing.png
Binary files differ
diff --git a/krita/plugins/tools/tool_curves/wdg_tool_example.ui b/krita/plugins/tools/tool_curves/wdg_tool_example.ui
new file mode 100644
index 00000000..3841185f
--- /dev/null
+++ b/krita/plugins/tools/tool_curves/wdg_tool_example.ui
@@ -0,0 +1,128 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WdgToolExample</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WdgToolExample</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>280</width>
+ <height>50</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Example</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Vertices:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>isbX</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>verticesSpinBox</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="minValue">
+ <number>2</number>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Ratio:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>isbWidth</cstring>
+ </property>
+ </widget>
+ <widget class="KisIntSpinbox">
+ <property name="name">
+ <cstring>ratioSpinBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </hbox>
+</widget>
+<tabstops>
+ <tabstop>verticesSpinBox</tabstop>
+ <tabstop>ratioSpinBox</tabstop>
+</tabstops>
+<customwidgets>
+<customwidget>
+ <class>KisIntSpinbox</class>
+ <header location="global">kis_int_spinbox.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image4</pixmap>
+</customwidget>
+</customwidgets>
+<images>
+ <image name="image4">
+ <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>