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
79QPolygon convexHull(const QVector<QPoint> &points)
80{
81 QPolygon hull;
82 boost::geometry::convex_hull(QPolygon(points), hull);
83 return hull;
84}
85
86// From libs/image/kis_paint_device.cc
87struct CheckFullyTransparent {
88 CheckFullyTransparent(const KoColorSpace *colorSpace)
89 : m_colorSpace(colorSpace)
90 {
91 }
92
93 bool isPixelEmpty(const quint8 *pixelData)
94 {
95 return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8;
96 }
97
98private:
99 const KoColorSpace *m_colorSpace;
100};
101
102struct CheckNonDefault {
103 CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
104 : m_pixelSize(pixelSize),
105 m_defaultPixel(defaultPixel)
106 {
107 }
108
109 bool isPixelEmpty(const quint8 *pixelData)
110 {
111 return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0;
112 }
113
114private:
115 int m_pixelSize;
116 const quint8 *m_defaultPixel;
117};
118
119struct CheckDeselected {
120 CheckDeselected(const KoColorSpace *colorSpace)
121 : m_colorSpace(colorSpace),
122 m_deselectedColor(Qt::black, colorSpace),
123 m_pixelSize(colorSpace->pixelSize())
124 {
126 colorSpace->colorModelId() == GrayAColorModelID);
127 }
128
129 bool isPixelEmpty(const quint8 *pixelData)
130 {
131 return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8 ||
132 memcmp(m_deselectedColor.data(), pixelData, m_pixelSize) == 0;
133 }
134
135private:
136 const KoColorSpace *m_colorSpace;
137 const KoColor m_deselectedColor;
138 const int m_pixelSize;
139};
140
141template <class ComparePixelOp>
142QVector<QPoint> retrieveAllBoundaryPointsImpl(const KisPaintDevice *device, const QRect &rect, const QRect &skip, ComparePixelOp compareOp)
143{
144 QVector<QPoint> points;
145 int defaultMin = rect.x() + rect.width() + 1;
146 int defaultMax = rect.x() - 1;
147 QVector<int> minX(rect.height(), defaultMin);
148 QVector<int> maxX(rect.height(), defaultMax);
149 int base = rect.top();
150 if (!skip.isEmpty()) {
151 for (int y = skip.top(); y <= skip.bottom(); y++) {
152 minX[y - base] = skip.left();
153 maxX[y - base] = skip.right();
154 }
155 }
156
157 int pixelSize = device->pixelSize();
159 for (int y = rect.top(); y <= rect.bottom();) {
160 int rows = accessor->numContiguousRows(y);
161 for (int x = rect.left(); x <= rect.right();) {
162 int columns = accessor->numContiguousColumns(x);
163 accessor->moveTo(x, y);
164 int strideBytes = accessor->rowStride(x, y);
165 const quint8 *data = accessor->rawDataConst();
166 for (int r = 0; r < rows; r++) {
167 for (int c = 0; c < columns; c++) {
168 if (!compareOp.isPixelEmpty(data + c * pixelSize)) {
169 int index = y + r - base;
170 minX[index] = std::min(minX[index], x + c);
171 maxX[index] = std::max(maxX[index], x + c);
172 }
173 }
174 data += strideBytes;
175 }
176 x += columns;
177 }
178 y += rows;
179 }
180
181 for (int y = rect.top(); y <= rect.bottom(); y++) {
182 int index = y - base;
183 if (minX[index] < defaultMin) {
184 points << QPoint(minX[index], y);
185 points << QPoint(minX[index], y + 1);
186 }
187 if (maxX[index] > defaultMax) {
188 points << QPoint(maxX[index] + 1, y);
189 points << QPoint(maxX[index] + 1, y + 1);
190 }
191 }
192
193 return points;
194}
195// This matches the behavior of KisPaintDevice::calculateExactBounds(false), whose result is returned by KisPaintDevice::exactBounds()
196QVector<QPoint> retrieveAllBoundaryPoints(const KisPaintDevice *device) {
197 QRect rect = device->extent();
198
199 const KoColor defaultPixel = device->defaultPixel();
200 const quint8 defaultOpacity = defaultPixel.opacityU8();
201
202 QVector<QPoint> points;
203
204 if (defaultOpacity != OPACITY_TRANSPARENT_U8) {
205 QRect skip = device->defaultBounds()->bounds();
206 CheckNonDefault compareOp(device->pixelSize(), defaultPixel.data());
207
208 points = retrieveAllBoundaryPointsImpl(device, rect, skip, compareOp);
209 if (!skip.isEmpty()) {
210 int x, y, w, h;
211 skip.getRect(&x, &y, &w, &h);
212 points << QPoint(x, y) << QPoint(x + w, y) << QPoint(x + w, y + h) << QPoint(x, y + h);
213 }
214 } else {
215 CheckFullyTransparent compareOp(device->colorSpace());
216 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
217 }
218 return points;
219}
220
221QVector<QPoint> retrieveAllBoundaryPointsSelectionLike(const KisPaintDevice *device) {
222 QRect rect = device->extent();
223
224 const KoColor defaultPixel = device->defaultPixel();
225 const quint8 defaultOpacity = defaultPixel.opacityU8();
226
227 QVector<QPoint> points;
228
229 if (defaultOpacity != OPACITY_TRANSPARENT_U8 &&
230 defaultPixel != KoColor(Qt::black, defaultPixel.colorSpace())) {
231
232 QRect skip = device->defaultBounds()->bounds();
233 CheckNonDefault compareOp(device->pixelSize(), defaultPixel.data());
234
235 points = retrieveAllBoundaryPointsImpl(device, rect, skip, compareOp);
236 if (!skip.isEmpty()) {
237 int x, y, w, h;
238 skip.getRect(&x, &y, &w, &h);
239 points << QPoint(x, y) << QPoint(x + w, y) << QPoint(x + w, y + h) << QPoint(x, y + h);
240 }
241 } else if (device->colorSpace()->colorModelId() == AlphaColorModelID) {
242 CheckFullyTransparent compareOp(device->colorSpace());
243 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
244 } else {
245 // pre-condition:
246 // defaultOpacity == OPACITY_TRANSPARENT_U8 ||
247 // defaultPixel == "deselected"
248
249 CheckDeselected compareOp(device->colorSpace());
250 points = retrieveAllBoundaryPointsImpl(device, rect, QRect(), compareOp);
251 }
252 return points;
253}
254
255}
256
257namespace KisConvexHull {
258
259QPolygon findConvexHull(const QVector<QPoint> &points)
260{
261 QPolygon hull = convexHull(points);
262 return hull;
263}
264
266{
267 QElapsedTimer timer;
268 timer.start();
269 auto ps = retrieveAllBoundaryPoints(device);
270 auto p = findConvexHull(ps);
271 return p;
272}
273
275{
276 QElapsedTimer timer;
277 timer.start();
278 auto ps = retrieveAllBoundaryPointsSelectionLike(device);
279 auto p = findConvexHull(ps);
280 return p;
281}
282
283}
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)