Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_convex_hull.cpp
Go to the documentation of this file.
1#include "kis_convex_hull.h"
2
3#include "kis_paint_device.h"
5#include "KoColorSpace.h"
6#include "KoColor.h"
8
9#include <boost/geometry.hpp>
10
11#include <QElapsedTimer>
12
13namespace boost
14{
15 namespace geometry
16 {
17 namespace traits
18 {
19 // Adapt QPoint to Boost.Geometry
20
21 template<> struct tag<QPoint>
22 { typedef point_tag type; };
23
24 template<> struct coordinate_type<QPoint>
25 { typedef int type; };
26
27 template<> struct coordinate_system<QPoint>
28 { typedef cs::cartesian type; };
29
30 template<> struct dimension<QPoint> : boost::mpl::int_<2> {};
31
32 template<>
33 struct access<QPoint, 0>
34 {
35 static int get(QPoint const& p)
36 {
37 return p.x();
38 }
39
40 static void set(QPoint& p, int const& value)
41 {
42 p.rx() = value;
43 }
44 };
45
46 template<>
47 struct access<QPoint, 1>
48 {
49 static int get(QPoint const& p)
50 {
51 return p.y();
52 }
53
54 static void set(QPoint& p, int const& value)
55 {
56 p.ry() = value;
57 }
58 };
59
60 // Adapt QPolygon to Boost.Geometry as Linestring
61
62 template<> struct tag<QPolygon>
63 { typedef linestring_tag type; };
64
65 }
66 }
67
68 template <>
69 struct range_iterator<QPolygon>
70 { typedef QPolygon::iterator type; };
71
72 template<>
73 struct range_const_iterator<QPolygon>
74 { typedef QPolygon::const_iterator type; };
75} // namespace boost::geometry::traits
76
77namespace {
78
79inline QPolygon::iterator range_begin(QPolygon& p)
80{return QPolygon::iterator(p.begin());}
81
82inline QPolygon::const_iterator range_begin(const QPolygon& p)
83{return QPolygon::const_iterator(p.begin());}
84
85inline QPolygon::iterator range_end(QPolygon& p)
86{return QPolygon::iterator(p.end());}
87
88inline QPolygon::const_iterator range_end(const QPolygon& p)
89{return QPolygon::const_iterator(p.end());}
90
91QPolygon convexHull(const QVector<QPoint> &points)
92{
93 QPolygon hull;
94 boost::geometry::convex_hull(QPolygon(points), hull);
95 return hull;
96}
97
98// From libs/image/kis_paint_device.cc
99struct CheckFullyTransparent {
100 CheckFullyTransparent(const KoColorSpace *colorSpace)
101 : m_colorSpace(colorSpace)
102 {
103 }
104
105 bool isPixelEmpty(const quint8 *pixelData)
106 {
107 return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8;
108 }
109
110private:
111 const KoColorSpace *m_colorSpace;
112};
113
114struct CheckNonDefault {
115 CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
116 : m_pixelSize(pixelSize),
117 m_defaultPixel(defaultPixel)
118 {
119 }
120
121 bool isPixelEmpty(const quint8 *pixelData)
122 {
123 return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0;
124 }
125
126private:
127 int m_pixelSize;
128 const quint8 *m_defaultPixel;
129};
130
131struct CheckDeselected {
132 CheckDeselected(const KoColorSpace *colorSpace)
133 : m_colorSpace(colorSpace),
134 m_deselectedColor(Qt::black, colorSpace),
135 m_pixelSize(colorSpace->pixelSize())
136 {
138 colorSpace->colorModelId() == GrayAColorModelID);
139 }
140
141 bool isPixelEmpty(const quint8 *pixelData)
142 {
143 return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8 ||
144 memcmp(m_deselectedColor.data(), pixelData, m_pixelSize) == 0;
145 }
146
147private:
148 const KoColorSpace *m_colorSpace;
149 const KoColor m_deselectedColor;
150 const int m_pixelSize;
151};
152
153template <class ComparePixelOp>
154QVector<QPoint> retrieveAllBoundaryPointsImpl(const KisPaintDevice *device, const QRect &rect, const QRect &skip, ComparePixelOp compareOp)
155{
156 QVector<QPoint> points;
157 int defaultMin = rect.x() + rect.width() + 1;
158 int defaultMax = rect.x() - 1;
159 QVector<int> minX(rect.height(), defaultMin);
160 QVector<int> maxX(rect.height(), defaultMax);
161 int base = rect.top();
162 if (!skip.isEmpty()) {
163 for (int y = skip.top(); y <= skip.bottom(); y++) {
164 minX[y - base] = skip.left();
165 maxX[y - base] = skip.right();
166 }
167 }
168
169 int pixelSize = device->pixelSize();
171 for (int y = rect.top(); y <= rect.bottom();) {
172 int rows = accessor->numContiguousRows(y);
173 for (int x = rect.left(); x <= rect.right();) {
174 int columns = accessor->numContiguousColumns(x);
175 accessor->moveTo(x, y);
176 int strideBytes = accessor->rowStride(x, y);
177 const quint8 *data = accessor->rawDataConst();
178 for (int r = 0; r < rows; r++) {
179 for (int c = 0; c < columns; c++) {
180 if (!compareOp.isPixelEmpty(data + c * pixelSize)) {
181 int index = y + r - base;
182 minX[index] = std::min(minX[index], x + c);
183 maxX[index] = std::max(maxX[index], x + c);
184 }
185 }
186 data += strideBytes;
187 }
188 x += columns;
189 }
190 y += rows;
191 }
192
193 for (int y = rect.top(); y <= rect.bottom(); y++) {
194 int index = y - base;
195 if (minX[index] < defaultMin) {
196 points << QPoint(minX[index], y);
197 points << QPoint(minX[index], y + 1);
198 }
199 if (maxX[index] > defaultMax) {
200 points << QPoint(maxX[index] + 1, y);
201 points << QPoint(maxX[index] + 1, y + 1);
202 }
203 }
204
205 return points;
206}
207// This matches the behavior of KisPaintDevice::calculateExactBounds(false), whose result is returned by KisPaintDevice::exactBounds()
208QVector<QPoint> retrieveAllBoundaryPoints(const KisPaintDevice *device) {
209 QRect rect = device->extent();
210
211 const KoColor defaultPixel = device->defaultPixel();
212 const quint8 defaultOpacity = defaultPixel.opacityU8();
213
214 QVector<QPoint> points;
215
216 if (defaultOpacity != OPACITY_TRANSPARENT_U8) {
217 QRect skip = device->defaultBounds()->bounds();
218 CheckNonDefault compareOp(device->pixelSize(), defaultPixel.data());
219
220 points = retrieveAllBoundaryPointsImpl(device, rect, skip, compareOp);
221 if (!skip.isEmpty()) {
222 int x, y, w, h;
223 skip.getRect(&x, &y, &w, &h);
224 points << QPoint(x, y) << QPoint(x + w, y) << QPoint(x + w, y + h) << QPoint(x, y + h);
225 }
226 } else {
227 CheckFullyTransparent compareOp(device->colorSpace());
228 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
229 }
230 return points;
231}
232
233QVector<QPoint> retrieveAllBoundaryPointsSelectionLike(const KisPaintDevice *device) {
234 QRect rect = device->extent();
235
236 const KoColor defaultPixel = device->defaultPixel();
237 const quint8 defaultOpacity = defaultPixel.opacityU8();
238
239 QVector<QPoint> points;
240
241 if (defaultOpacity != OPACITY_TRANSPARENT_U8 &&
242 defaultPixel != KoColor(Qt::black, defaultPixel.colorSpace())) {
243
244 QRect skip = device->defaultBounds()->bounds();
245 CheckNonDefault compareOp(device->pixelSize(), defaultPixel.data());
246
247 points = retrieveAllBoundaryPointsImpl(device, rect, skip, compareOp);
248 if (!skip.isEmpty()) {
249 int x, y, w, h;
250 skip.getRect(&x, &y, &w, &h);
251 points << QPoint(x, y) << QPoint(x + w, y) << QPoint(x + w, y + h) << QPoint(x, y + h);
252 }
253 } else if (device->colorSpace()->colorModelId() == AlphaColorModelID) {
254 CheckFullyTransparent compareOp(device->colorSpace());
255 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
256 } else {
257 // pre-condition:
258 // defaultOpacity == OPACITY_TRANSPARENT_U8 ||
259 // defaultPixel == "deselected"
260
261 CheckDeselected compareOp(device->colorSpace());
262 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
263 }
264 return points;
265}
266
267}
268
269namespace KisConvexHull {
270
271QPolygon findConvexHull(const QVector<QPoint> &points)
272{
273 QPolygon hull = convexHull(points);
274 return hull;
275}
276
278{
279 QElapsedTimer timer;
280 timer.start();
281 auto ps = retrieveAllBoundaryPoints(device);
282 auto p = findConvexHull(ps);
283 return p;
284}
285
287{
288 QElapsedTimer timer;
289 timer.start();
290 auto ps = retrieveAllBoundaryPointsSelectionLike(device);
291 auto p = findConvexHull(ps);
292 return p;
293}
294
295}
float value(const T *src, size_t ch)
const Params2D p
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID AlphaColorModelID("A", ki18n("Alpha mask"))
const quint8 OPACITY_TRANSPARENT_U8
virtual const quint8 * rawDataConst() const =0
virtual QRect bounds() const =0
quint32 pixelSize() const
KisRandomConstAccessorSP createRandomConstAccessorNG() const
QRect extent() const
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
KisDefaultBoundsBaseSP defaultBounds() const
virtual qint32 rowStride(qint32 x, qint32 y) const =0
virtual qint32 numContiguousRows(qint32 y) const =0
virtual void moveTo(qint32 x, qint32 y)=0
virtual qint32 numContiguousColumns(qint32 x) const =0
virtual KoID colorModelId() const =0
quint8 * data()
Definition KoColor.h:144
quint8 opacityU8() const
Definition KoColor.cpp:341
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
QPolygon findConvexHullSelectionLike(KisPaintDeviceSP device)
QPolygon findConvexHull(const QVector< QPoint > &points)
static void set(QPoint &p, int const &value)
static void set(QPoint &p, int const &value)