Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_outline_generator.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2007, 2010 Sven Langkamp <sven.langkamp@gmail.com>
4 *
5 * Outline algorithm based of the limn of fontutils
6 * SPDX-FileCopyrightText: 1992 Karl Berry <karl@cs.umb.edu>
7 * SPDX-FileCopyrightText: 1992 Kathryn Hargreaves <letters@cs.umb.edu>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
13#include <KoColorSpace.h>
15
16#include "kis_paint_device.h"
17#include <kis_iterator_ng.h>
19
21{
22public:
23 typedef quint8* StorageType;
24public:
25 LinearStorage(quint8 *buffer, int width, int height, int pixelSize)
26 : m_buffer(buffer),
27 m_width(width),
28 m_pixelSize(pixelSize)
29 {
30 m_marks.reset(new quint8[width * height]);
31 memset(m_marks.data(), 0, width * height);
32 }
33
34 quint8* pickPixel(int x, int y) {
35 return m_buffer + (m_width * y + x) * m_pixelSize;
36 }
37
38 quint8* pickMark(int x, int y) {
39 return m_marks.data() + m_width * y + x;
40 }
41
42private:
43 QScopedArrayPointer<quint8> m_marks;
44 quint8 *m_buffer;
47};
48
50{
51public:
53public:
54 PaintDeviceStorage(const KisPaintDevice *device, int /*width*/, int /*height*/, int /*pixelSize*/)
55 : m_device(device)
56 {
58
60 m_marks = new KisPaintDevice(alphaCs);
62 }
63
64 const quint8* pickPixel(int x, int y) {
65 m_deviceIt->moveTo(x, y);
66 return m_deviceIt->rawDataConst();
67 }
68
69 quint8* pickMark(int x, int y) {
70 m_marksIt->moveTo(x, y);
71 return m_marksIt->rawData();
72 }
73
74private:
79};
80
81
82/******************* class KisOutlineGenerator *******************/
83
85 : m_cs(cs), m_defaultOpacity(defaultOpacity), m_simple(false)
86{
87}
88
89template <class StorageStrategy>
90QVector<QPolygon> KisOutlineGenerator::outlineImpl(typename StorageStrategy::StorageType buffer,
91 qint32 xOffset, qint32 yOffset,
92 qint32 width, qint32 height)
93{
95
96 try {
97 StorageStrategy storage(buffer, width, height, m_cs->pixelSize());
98
99 for (qint32 y = 0; y < height; y++) {
100 for (qint32 x = 0; x < width; x++) {
101
102 if (m_cs->opacityU8(storage.pickPixel(x, y)) == m_defaultOpacity)
103 continue;
104
105 const EdgeType initialEdge = TopEdge;
106
107 EdgeType startEdge = initialEdge;
108 while (startEdge != NoEdge &&
109 (*storage.pickMark(x, y) & (1 << startEdge) ||
110 !isOutlineEdge(storage, startEdge, x, y, width, height))) {
111
112 startEdge = nextEdge(startEdge);
113 if (startEdge == initialEdge)
114 startEdge = NoEdge;
115 }
116
117 if (startEdge != NoEdge) {
118 QPolygon path;
119 const bool clockwise = startEdge == BottomEdge;
120
121 qint32 row = y, col = x;
122 EdgeType currentEdge = startEdge;
123 EdgeType lastEdge = NoEdge;
124
125 if (currentEdge == BottomEdge) {
126 appendCoordinate(&path, col + xOffset, row + yOffset, currentEdge, lastEdge);
127 lastEdge = BottomEdge;
128 }
129
130 forever {
131 //qDebug() << "visit" << xOffset + col << yOffset + row << ppVar(currentEdge) << ppVar(lastEdge);
132
133 *storage.pickMark(col, row) |= 1 << currentEdge;
134 nextOutlineEdge(storage, &currentEdge, &row, &col, width, height);
135
136 //While following a straight line no points need to be added
137 if (lastEdge != currentEdge) {
138 appendCoordinate(&path, col + xOffset, row + yOffset, currentEdge, lastEdge);
139 lastEdge = currentEdge;
140 }
141
142 if (row == y && col == x && currentEdge == startEdge) {
143 if (startEdge != BottomEdge) {
144 // add last point of the polygon
145 appendCoordinate(&path, col + xOffset, row + yOffset, NoEdge, NoEdge);
146 }
147 break;
148 }
149 }
150
151 if(!m_simple || !clockwise) {
152 paths.push_back(path);
153 }
154 }
155 }
156 }
157 }
158 catch(const std::bad_alloc&) {
159 warnKrita << "KisOutlineGenerator::outline ran out of memory allocating " << width << "*" << height << "marks";
160 }
161
162 return paths;
163}
164
165QVector<QPolygon> KisOutlineGenerator::outline(quint8* buffer, qint32 xOffset, qint32 yOffset, qint32 width, qint32 height)
166{
167 return outlineImpl<LinearStorage>(buffer, xOffset, yOffset, width, height);
168}
169
170QVector<QPolygon> KisOutlineGenerator::outline(const KisPaintDevice *buffer, qint32 xOffset, qint32 yOffset, qint32 width, qint32 height)
171{
172 return outlineImpl<PaintDeviceStorage>(buffer, xOffset, yOffset, width, height);
173}
174
175template <class StorageStrategy>
176bool KisOutlineGenerator::isOutlineEdge(StorageStrategy &storage, EdgeType edge, qint32 x, qint32 y, qint32 bufWidth, qint32 bufHeight)
177{
178 if (m_cs->opacityU8(storage.pickPixel(x, y)) == m_defaultOpacity)
179 return false;
180
181 switch (edge) {
182 case LeftEdge:
183 return x == 0 || m_cs->opacityU8(storage.pickPixel(x - 1, y)) == m_defaultOpacity;
184 case TopEdge:
185 return y == 0 || m_cs->opacityU8(storage.pickPixel(x, y - 1)) == m_defaultOpacity;
186 case RightEdge:
187 return x == bufWidth - 1 || m_cs->opacityU8(storage.pickPixel(x + 1, y)) == m_defaultOpacity;
188 case BottomEdge:
189 return y == bufHeight - 1 || m_cs->opacityU8(storage.pickPixel(x, y + 1)) == m_defaultOpacity;
190 case NoEdge:
191 return false;
192 }
193 return false;
194}
195
196#define TRY_PIXEL(deltaRow, deltaCol, test_edge) \
197 { \
198 int test_row = *row + deltaRow; \
199 int test_col = *col + deltaCol; \
200 if ( (0 <= (test_row) && (test_row) < height && 0 <= (test_col) && (test_col) < width) && \
201 isOutlineEdge (storage, test_edge, test_col, test_row, width, height)) \
202 { \
203 *row = test_row; \
204 *col = test_col; \
205 *edge = test_edge; \
206 break; \
207 } \
208 }
209
210template <class StorageStrategy>
211void KisOutlineGenerator::nextOutlineEdge(StorageStrategy &storage, EdgeType *edge, qint32 *row, qint32 *col, qint32 width, qint32 height)
212{
213 int original_row = *row;
214 int original_col = *col;
215
216 switch (*edge) {
217 case RightEdge:
218 TRY_PIXEL(-1, 0, RightEdge);
219 TRY_PIXEL(-1, 1, BottomEdge);
220 break;
221
222 case TopEdge:
223 TRY_PIXEL(0, -1, TopEdge);
224 TRY_PIXEL(-1, -1, RightEdge);
225 break;
226
227 case LeftEdge:
228 TRY_PIXEL(1, 0, LeftEdge);
229 TRY_PIXEL(1, -1, TopEdge);
230 break;
231
232 case BottomEdge:
233 TRY_PIXEL(0, 1, BottomEdge);
234 TRY_PIXEL(1, 1, LeftEdge);
235 break;
236
237 default:
238 break;
239
240 }
241
242 if (*row == original_row && *col == original_col)
243 *edge = nextEdge(*edge);
244}
245
246void KisOutlineGenerator::appendCoordinate(QPolygon * path, int x, int y, EdgeType edge, EdgeType prevEdge)
247{
248 Q_UNUSED(prevEdge);
249
250 //const QPoint origPt(x, y);
251
252 if (edge == TopEdge) {
253 x++;
254 } else if (edge == BottomEdge) {
255 y++;
256 } else if (edge == RightEdge) {
257 x++;
258 y++;
259 }
260
261 //qDebug() <<"add" << ppVar(origPt) << ppVar(edge) << ppVar(prevEdge) << "-->" << QPoint(x, y);
262
263 *path << QPoint(x, y);
264}
265
267{
268 m_simple = simple;
269}
virtual quint8 * rawData()=0
virtual const quint8 * rawDataConst() const =0
bool isOutlineEdge(StorageStrategy &storage, EdgeType edge, qint32 x, qint32 y, qint32 bufWidth, qint32 bufHeight)
void setSimpleOutline(bool simple)
void nextOutlineEdge(StorageStrategy &storage, EdgeType *edge, qint32 *row, qint32 *col, qint32 width, qint32 height)
QVector< QPolygon > outline(quint8 *buffer, qint32 xOffset, qint32 yOffset, qint32 width, qint32 height)
void appendCoordinate(QPolygon *path, int x, int y, EdgeType edge, EdgeType prevEdge)
const KoColorSpace * m_cs
QVector< QPolygon > outlineImpl(typename StorageStrategy::StorageType buffer, qint32 xOffset, qint32 yOffset, qint32 width, qint32 height)
KisOutlineGenerator(const KoColorSpace *cs, quint8 defaultOpacity)
EdgeType nextEdge(EdgeType edge)
KisRandomConstAccessorSP createRandomConstAccessorNG() const
KisRandomAccessorSP createRandomAccessorNG()
virtual void moveTo(qint32 x, qint32 y)=0
virtual quint32 pixelSize() const =0
virtual quint8 opacityU8(const quint8 *pixel) const =0
quint8 * pickPixel(int x, int y)
QScopedArrayPointer< quint8 > m_marks
LinearStorage(quint8 *buffer, int width, int height, int pixelSize)
quint8 * pickMark(int x, int y)
KisRandomConstAccessorSP m_deviceIt
const KisPaintDevice * StorageType
quint8 * pickMark(int x, int y)
const KisPaintDevice * m_device
const quint8 * pickPixel(int x, int y)
KisRandomAccessorSP m_marksIt
PaintDeviceStorage(const KisPaintDevice *device, int, int, int)
#define warnKrita
Definition kis_debug.h:87
#define TRY_PIXEL(deltaRow, deltaCol, test_edge)
static KoColorSpaceRegistry * instance()
const KoColorSpace * alpha8()