diff options
Diffstat (limited to 'krita/plugins/tools/tool_curves')
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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 = ⌖} + + CurveIterator (const CurveIterator &it) + {m_position = it.position(); m_target = it.target();} + + CurveIterator (const KisCurve &target, BaseIterator it) + {m_position = it; m_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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 Binary files differnew file mode 100644 index 00000000..78dae446 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_bezier_cursor.png diff --git a/krita/plugins/tools/tool_curves/tool_bezier_paint.png b/krita/plugins/tools/tool_curves/tool_bezier_paint.png Binary files differnew file mode 100644 index 00000000..dcbd93d5 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_bezier_paint.png diff --git a/krita/plugins/tools/tool_curves/tool_bezier_select.png b/krita/plugins/tools/tool_curves/tool_bezier_select.png Binary files differnew file mode 100644 index 00000000..84fb3036 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_bezier_select.png diff --git a/krita/plugins/tools/tool_curves/tool_curve_dragging.png b/krita/plugins/tools/tool_curves/tool_curve_dragging.png Binary files differnew file mode 100644 index 00000000..e8fb92f4 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_curve_dragging.png 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 <emanuele@valinor.it> + * + * 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 <emanuele@valinor.it> + * + * 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 Binary files differnew file mode 100644 index 00000000..02f821ce --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_example.png diff --git a/krita/plugins/tools/tool_curves/tool_example_cursor.png b/krita/plugins/tools/tool_curves/tool_example_cursor.png Binary files differnew file mode 100644 index 00000000..78dae446 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_example_cursor.png diff --git a/krita/plugins/tools/tool_curves/tool_moutline.png b/krita/plugins/tools/tool_curves/tool_moutline.png Binary files differnew file mode 100644 index 00000000..2f75d945 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_moutline.png diff --git a/krita/plugins/tools/tool_curves/tool_moutline_cursor.png b/krita/plugins/tools/tool_curves/tool_moutline_cursor.png Binary files differnew file mode 100644 index 00000000..039b9e50 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_moutline_cursor.png diff --git a/krita/plugins/tools/tool_curves/tool_moutline_editing.png b/krita/plugins/tools/tool_curves/tool_moutline_editing.png Binary files differnew file mode 100644 index 00000000..568d9fd4 --- /dev/null +++ b/krita/plugins/tools/tool_curves/tool_moutline_editing.png 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> |