Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdistancefield.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <qmath.h>
6#include <QtCore/qalloc.h>
7#include <private/qdatabuffer_p.h>
8#include <private/qimage_p.h>
9#include <private/qpathsimplifier_p.h>
10
12
13using namespace Qt::StringLiterals;
14
15Q_STATIC_LOGGING_CATEGORY(lcDistanceField, "qt.distanceField");
16
17namespace
18{
19 enum FillHDir
20 {
21 LeftToRight,
22 RightToLeft
23 };
24
25 enum FillVDir
26 {
27 TopDown,
28 BottomUp
29 };
30
31 enum FillClip
32 {
33 NoClip,
34 Clip
35 };
36}
37
38template <FillClip clip, FillHDir dir>
39inline void fillLine(qint32 *, int, int, int, qint32, qint32)
40{
41}
42
43template <>
44inline void fillLine<Clip, LeftToRight>(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd)
45{
46 int fromX = qMax(0, lx >> 8);
47 int toX = qMin(width, rx >> 8);
48 int x = toX - fromX;
49 if (x <= 0)
50 return;
51 qint32 val = d + (((fromX << 8) + 0xff - lx) * dd >> 8);
52 line += fromX;
53 do {
54 *line = abs(val) < abs(*line) ? val : *line;
55 val += dd;
56 ++line;
57 } while (--x);
58}
59
60template <>
61inline void fillLine<Clip, RightToLeft>(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd)
62{
63 int fromX = qMax(0, lx >> 8);
64 int toX = qMin(width, rx >> 8);
65 int x = toX - fromX;
66 if (x <= 0)
67 return;
68 qint32 val = d + (((toX << 8) + 0xff - rx) * dd >> 8);
69 line += toX;
70 do {
71 val -= dd;
72 --line;
73 *line = abs(val) < abs(*line) ? val : *line;
74 } while (--x);
75}
76
77template <>
78inline void fillLine<NoClip, LeftToRight>(qint32 *line, int, int lx, int rx, qint32 d, qint32 dd)
79{
80 int fromX = lx >> 8;
81 int toX = rx >> 8;
82 int x = toX - fromX;
83 if (x <= 0)
84 return;
85 qint32 val = d + ((~lx & 0xff) * dd >> 8);
86 line += fromX;
87 do {
88 *line = abs(val) < abs(*line) ? val : *line;
89 val += dd;
90 ++line;
91 } while (--x);
92}
93
94template <>
95inline void fillLine<NoClip, RightToLeft>(qint32 *line, int, int lx, int rx, qint32 d, qint32 dd)
96{
97 int fromX = lx >> 8;
98 int toX = rx >> 8;
99 int x = toX - fromX;
100 if (x <= 0)
101 return;
102 qint32 val = d + ((~rx & 0xff) * dd >> 8);
103 line += toX;
104 do {
105 val -= dd;
106 --line;
107 *line = abs(val) < abs(*line) ? val : *line;
108 } while (--x);
109}
110
111template <FillClip clip, FillVDir vDir, FillHDir hDir>
112inline void fillLines(qint32 *bits, int width, int height, int upperY, int lowerY,
113 int &lx, int ldx, int &rx, int rdx, qint32 &d, qint32 ddy, qint32 ddx)
114{
115 Q_UNUSED(height);
116 Q_ASSERT(upperY < lowerY);
117 int y = lowerY - upperY;
118 if (vDir == TopDown) {
119 qint32 *line = bits + upperY * width;
120 do {
121 fillLine<clip, hDir>(line, width, lx, rx, d, ddx);
122 lx += ldx;
123 d += ddy;
124 rx += rdx;
125 line += width;
126 } while (--y);
127 } else {
128 qint32 *line = bits + lowerY * width;
129 do {
130 lx -= ldx;
131 d -= ddy;
132 rx -= rdx;
133 line -= width;
134 fillLine<clip, hDir>(line, width, lx, rx, d, ddx);
135 } while (--y);
136 }
137}
138
139template <FillClip clip>
140void drawTriangle(qint32 *bits, int width, int height, const QPoint *center,
141 const QPoint *v1, const QPoint *v2, qint32 value)
142{
143 const int y1 = clip == Clip ? qBound(0, v1->y() >> 8, height) : v1->y() >> 8;
144 const int y2 = clip == Clip ? qBound(0, v2->y() >> 8, height) : v2->y() >> 8;
145 const int yC = clip == Clip ? qBound(0, center->y() >> 8, height) : center->y() >> 8;
146
147 const int v1Frac = clip == Clip ? (y1 << 8) + 0xff - v1->y() : ~v1->y() & 0xff;
148 const int v2Frac = clip == Clip ? (y2 << 8) + 0xff - v2->y() : ~v2->y() & 0xff;
149 const int centerFrac = clip == Clip ? (yC << 8) + 0xff - center->y() : ~center->y() & 0xff;
150
151 int dx1 = 0, x1 = 0, dx2 = 0, x2 = 0;
152 qint32 dd1, d1, dd2, d2;
153 if (v1->y() != center->y()) {
154 dx1 = ((v1->x() - center->x()) << 8) / (v1->y() - center->y());
155 x1 = center->x() + centerFrac * (v1->x() - center->x()) / (v1->y() - center->y());
156 }
157 if (v2->y() != center->y()) {
158 dx2 = ((v2->x() - center->x()) << 8) / (v2->y() - center->y());
159 x2 = center->x() + centerFrac * (v2->x() - center->x()) / (v2->y() - center->y());
160 }
161
162 const qint32 div = (v2->x() - center->x()) * (v1->y() - center->y())
163 - (v2->y() - center->y()) * (v1->x() - center->x());
164 const qint32 dd = div ? qint32((qint64(value * (v1->y() - v2->y())) << 8) / div) : 0;
165
166 if (y2 < yC) {
167 if (y1 < yC) {
168 // Center at the bottom.
169 if (y2 < y1) {
170 // y2 < y1 < yC
171 // Long right edge.
172 d1 = centerFrac * value / (v1->y() - center->y());
173 dd1 = ((value << 8) / (v1->y() - center->y()));
174 fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y1, yC, x1, dx1,
175 x2, dx2, d1, dd1, dd);
176 dx1 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
177 x1 = v1->x() + v1Frac * (v1->x() - v2->x()) / (v1->y() - v2->y());
178 fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y2, y1, x1, dx1,
179 x2, dx2, value, 0, dd);
180 } else {
181 // y1 <= y2 < yC
182 // Long left edge.
183 d2 = centerFrac * value / (v2->y() - center->y());
184 dd2 = ((value << 8) / (v2->y() - center->y()));
185 fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y2, yC, x1, dx1,
186 x2, dx2, d2, dd2, dd);
187 if (y1 != y2) {
188 dx2 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
189 x2 = v2->x() + v2Frac * (v1->x() - v2->x()) / (v1->y() - v2->y());
190 fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y1, y2, x1, dx1,
191 x2, dx2, value, 0, dd);
192 }
193 }
194 } else {
195 // y2 < yC <= y1
196 // Center to the right.
197 int dx = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
198 int xUp, xDn;
199 xUp = xDn = v2->x() + (clip == Clip ? (yC << 8) + 0xff - v2->y()
200 : (center->y() | 0xff) - v2->y())
201 * (v1->x() - v2->x()) / (v1->y() - v2->y());
202 fillLines<clip, BottomUp, LeftToRight>(bits, width, height, y2, yC, xUp, dx,
203 x2, dx2, value, 0, dd);
204 if (yC != y1)
205 fillLines<clip, TopDown, LeftToRight>(bits, width, height, yC, y1, xDn, dx,
206 x1, dx1, value, 0, dd);
207 }
208 } else {
209 if (y1 < yC) {
210 // y1 < yC <= y2
211 // Center to the left.
212 int dx = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
213 int xUp, xDn;
214 xUp = xDn = v1->x() + (clip == Clip ? (yC << 8) + 0xff - v1->y()
215 : (center->y() | 0xff) - v1->y())
216 * (v1->x() - v2->x()) / (v1->y() - v2->y());
217 fillLines<clip, BottomUp, RightToLeft>(bits, width, height, y1, yC, x1, dx1,
218 xUp, dx, value, 0, dd);
219 if (yC != y2)
220 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yC, y2, x2, dx2,
221 xDn, dx, value, 0, dd);
222 } else {
223 // Center at the top.
224 if (y2 < y1) {
225 // yC <= y2 < y1
226 // Long right edge.
227 if (yC != y2) {
228 d2 = centerFrac * value / (v2->y() - center->y());
229 dd2 = ((value << 8) / (v2->y() - center->y()));
230 fillLines<clip, TopDown, LeftToRight>(bits, width, height, yC, y2, x2, dx2,
231 x1, dx1, d2, dd2, dd);
232 }
233 dx2 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
234 x2 = v2->x() + v2Frac * (v1->x() - v2->x()) / (v1->y() - v2->y());
235 fillLines<clip, TopDown, LeftToRight>(bits, width, height, y2, y1, x2, dx2,
236 x1, dx1, value, 0, dd);
237 } else {
238 // Long left edge.
239 // yC <= y1 <= y2
240 if (yC != y1) {
241 d1 = centerFrac * value / (v1->y() - center->y());
242 dd1 = ((value << 8) / (v1->y() - center->y()));
243 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yC, y1, x2, dx2,
244 x1, dx1, d1, dd1, dd);
245 }
246 if (y1 != y2) {
247 dx1 = ((v1->x() - v2->x()) << 8) / (v1->y() - v2->y());
248 x1 = v1->x() + v1Frac * (v1->x() - v2->x()) / (v1->y() - v2->y());
249 fillLines<clip, TopDown, RightToLeft>(bits, width, height, y1, y2, x2, dx2,
250 x1, dx1, value, 0, dd);
251 }
252 }
253 }
254 }
255}
256
257template <FillClip clip>
258void drawRectangle(qint32 *bits, int width, int height,
259 const QPoint *int1, const QPoint *center1, const QPoint *ext1,
260 const QPoint *int2, const QPoint *center2, const QPoint *ext2,
261 qint32 extValue)
262{
263 if (center1->y() > center2->y()) {
264 qSwap(center1, center2);
265 qSwap(int1, ext2);
266 qSwap(ext1, int2);
267 extValue = -extValue;
268 }
269
270 Q_ASSERT(ext1->x() - center1->x() == center1->x() - int1->x());
271 Q_ASSERT(ext1->y() - center1->y() == center1->y() - int1->y());
272 Q_ASSERT(ext2->x() - center2->x() == center2->x() - int2->x());
273 Q_ASSERT(ext2->y() - center2->y() == center2->y() - int2->y());
274
275 const int yc1 = clip == Clip ? qBound(0, center1->y() >> 8, height) : center1->y() >> 8;
276 const int yc2 = clip == Clip ? qBound(0, center2->y() >> 8, height) : center2->y() >> 8;
277 const int yi1 = clip == Clip ? qBound(0, int1->y() >> 8, height) : int1->y() >> 8;
278 const int yi2 = clip == Clip ? qBound(0, int2->y() >> 8, height) : int2->y() >> 8;
279 const int ye1 = clip == Clip ? qBound(0, ext1->y() >> 8, height) : ext1->y() >> 8;
280 const int ye2 = clip == Clip ? qBound(0, ext2->y() >> 8, height) : ext2->y() >> 8;
281
282 const int center1Frac = clip == Clip ? (yc1 << 8) + 0xff - center1->y() : ~center1->y() & 0xff;
283 const int center2Frac = clip == Clip ? (yc2 << 8) + 0xff - center2->y() : ~center2->y() & 0xff;
284 const int int1Frac = clip == Clip ? (yi1 << 8) + 0xff - int1->y() : ~int1->y() & 0xff;
285 const int ext1Frac = clip == Clip ? (ye1 << 8) + 0xff - ext1->y() : ~ext1->y() & 0xff;
286
287 int dxC = 0, dxE = 0; // cap slope, edge slope
288 qint32 ddC = 0;
289 if (ext1->y() != int1->y()) {
290 dxC = ((ext1->x() - int1->x()) << 8) / (ext1->y() - int1->y());
291 ddC = (extValue << 9) / (ext1->y() - int1->y());
292 }
293 if (ext1->y() != ext2->y())
294 dxE = ((ext1->x() - ext2->x()) << 8) / (ext1->y() - ext2->y());
295
296 const qint32 div = (ext1->x() - int1->x()) * (ext2->y() - int1->y())
297 - (ext1->y() - int1->y()) * (ext2->x() - int1->x());
298 const qint32 dd = div ? qint32((qint64(extValue * (ext2->y() - ext1->y())) << 9) / div) : 0;
299
300 int xe1, xe2, xc1, xc2;
301 qint32 d;
302
303 qint32 intValue = -extValue;
304
305 if (center2->x() < center1->x()) {
306 // Leaning to the right. '/'
307 if (int1->y() < ext2->y()) {
308 // Mostly vertical.
309 Q_ASSERT(ext1->y() != ext2->y());
310 xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
311 xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
312 if (ye1 != yi1) {
313 xc2 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
314 xc2 += (ye1 - yc1) * dxC;
315 fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, yi1, xe1, dxE,
316 xc2, dxC, extValue, 0, dd);
317 }
318 if (yi1 != ye2)
319 fillLines<clip, TopDown, LeftToRight>(bits, width, height, yi1, ye2, xe1, dxE,
320 xe2, dxE, extValue, 0, dd);
321 if (ye2 != yi2) {
322 xc1 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
323 xc1 += (ye2 - yc2) * dxC;
324 fillLines<clip, TopDown, RightToLeft>(bits, width, height, ye2, yi2, xc1, dxC,
325 xe2, dxE, intValue, 0, dd);
326 }
327 } else {
328 // Mostly horizontal.
329 Q_ASSERT(ext1->y() != int1->y());
330 xc1 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
331 xc2 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
332 xc1 += (ye2 - yc2) * dxC;
333 xc2 += (ye1 - yc1) * dxC;
334 if (ye1 != ye2) {
335 xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
336 fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, ye2, xe1, dxE,
337 xc2, dxC, extValue, 0, dd);
338 }
339 if (ye2 != yi1) {
340 d = (clip == Clip ? (ye2 << 8) + 0xff - center2->y()
341 : (ext2->y() | 0xff) - center2->y())
342 * 2 * extValue / (ext1->y() - int1->y());
343 fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye2, yi1, xc1, dxC,
344 xc2, dxC, d, ddC, dd);
345 }
346 if (yi1 != yi2) {
347 xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
348 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, yi2, xc1, dxC,
349 xe2, dxE, intValue, 0, dd);
350 }
351 }
352 } else {
353 // Leaning to the left. '\'
354 if (ext1->y() < int2->y()) {
355 // Mostly vertical.
356 Q_ASSERT(ext1->y() != ext2->y());
357 xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
358 xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
359 if (yi1 != ye1) {
360 xc1 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
361 xc1 += (yi1 - yc1) * dxC;
362 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, ye1, xc1, dxC,
363 xe2, dxE, intValue, 0, dd);
364 }
365 if (ye1 != yi2)
366 fillLines<clip, TopDown, RightToLeft>(bits, width, height, ye1, yi2, xe1, dxE,
367 xe2, dxE, intValue, 0, dd);
368 if (yi2 != ye2) {
369 xc2 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
370 xc2 += (yi2 - yc2) * dxC;
371 fillLines<clip, TopDown, LeftToRight>(bits, width, height, yi2, ye2, xe1, dxE,
372 xc2, dxC, extValue, 0, dd);
373 }
374 } else {
375 // Mostly horizontal.
376 Q_ASSERT(ext1->y() != int1->y());
377 xc1 = center1->x() + center1Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
378 xc2 = center2->x() + center2Frac * (ext1->x() - int1->x()) / (ext1->y() - int1->y());
379 xc1 += (yi1 - yc1) * dxC;
380 xc2 += (yi2 - yc2) * dxC;
381 if (yi1 != yi2) {
382 xe2 = int1->x() + int1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
383 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi1, yi2, xc1, dxC,
384 xe2, dxE, intValue, 0, dd);
385 }
386 if (yi2 != ye1) {
387 d = (clip == Clip ? (yi2 << 8) + 0xff - center2->y()
388 : (int2->y() | 0xff) - center2->y())
389 * 2 * extValue / (ext1->y() - int1->y());
390 fillLines<clip, TopDown, RightToLeft>(bits, width, height, yi2, ye1, xc1, dxC,
391 xc2, dxC, d, ddC, dd);
392 }
393 if (ye1 != ye2) {
394 xe1 = ext1->x() + ext1Frac * (ext1->x() - ext2->x()) / (ext1->y() - ext2->y());
395 fillLines<clip, TopDown, LeftToRight>(bits, width, height, ye1, ye2, xe1, dxE,
396 xc2, dxC, extValue, 0, dd);
397 }
398 }
399 }
400}
401
402static void drawPolygons(qint32 *bits, int width, int height, const QPoint *vertices,
403 const quint32 *indices, int indexCount, qint32 value)
404{
405 Q_ASSERT(indexCount != 0);
406 typedef QVarLengthArray<quint16, 16> ScanLine;
407 QVarLengthArray<ScanLine, 128> scans(height);
408 int first = 0;
409 for (int i = 1; i < indexCount; ++i) {
410 quint32 idx1 = indices[i - 1];
411 quint32 idx2 = indices[i];
412 Q_ASSERT(idx1 != quint32(-1));
413 if (idx2 == quint32(-1)) {
414 idx2 = indices[first];
415 Q_ASSERT(idx2 != quint32(-1));
416 first = ++i;
417 }
418 const QPoint *v1 = &vertices[idx1];
419 const QPoint *v2 = &vertices[idx2];
420 if (v2->y() < v1->y())
421 qSwap(v1, v2);
422 int fromY = qMax(0, v1->y() >> 8);
423 int toY = qMin(height, v2->y() >> 8);
424 if (fromY >= toY)
425 continue;
426 int dx = ((v2->x() - v1->x()) << 8) / (v2->y() - v1->y());
427 int x = v1->x() + ((fromY << 8) + 0xff - v1->y()) * (v2->x() - v1->x()) / (v2->y() - v1->y());
428 for (int y = fromY; y < toY; ++y) {
429 quint32 c = quint32(x >> 8);
430 if (c < quint32(width))
431 scans[y].append(quint16(c));
432 x += dx;
433 }
434 }
435 for (int i = 0; i < height; ++i) {
436 quint16 *scanline = scans[i].data();
437 int size = scans[i].size();
438 for (int j = 1; j < size; ++j) {
439 int k = j;
440 quint16 value = scanline[k];
441 for (; k != 0 && value < scanline[k - 1]; --k)
442 scanline[k] = scanline[k - 1];
443 scanline[k] = value;
444 }
445 qint32 *line = bits + i * width;
446 int j = 0;
447 for (; j + 1 < size; j += 2) {
448 for (quint16 x = scanline[j]; x < scanline[j + 1]; ++x)
449 line[x] = value;
450 }
451 if (j < size) {
452 for (int x = scanline[j]; x < width; ++x)
453 line[x] = value;
454 }
455 }
456}
457
458static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path, int dfScale, int offs)
459{
460 if (!data || !data->data)
461 return;
462
463 if (path.isEmpty()) {
464 memset(data->data, 0, data->nbytes);
465 return;
466 }
467
468 int imgWidth = data->width;
469 int imgHeight = data->height;
470
471 QTransform transform;
472 transform.translate(offs, offs);
473 transform.scale(qreal(1) / dfScale, qreal(1) / dfScale);
474
475 QDataBuffer<quint32> pathIndices(0);
476 QDataBuffer<QPoint> pathVertices(0);
477 qSimplifyPath(path, pathVertices, pathIndices, transform);
478 if (pathVertices.isEmpty()) {
479 qCWarning(lcDistanceField) << "Unexpected glyph path structure, bailing out";
480 memset(data->data, 0, data->nbytes);
481 return;
482 }
483
484 const qint32 interiorColor = -0x7f80; // 8:8 signed format, -127.5
485 const qint32 exteriorColor = 0x7f80; // 8:8 signed format, 127.5
486
487 QScopedArrayPointer<qint32> bits(new qint32[imgWidth * imgHeight]);
488 for (int i = 0; i < imgWidth * imgHeight; ++i)
489 bits[i] = exteriorColor;
490
491 const qreal angleStep = qDegreesToRadians(qreal(15));
492 const QPoint rotation(qRound(qCos(angleStep) * 0x4000),
493 qRound(qSin(angleStep) * 0x4000)); // 2:14 signed
494
495 const quint32 *indices = pathIndices.data();
496 QVarLengthArray<QPoint> normals;
497 QVarLengthArray<QPoint> vertices;
498
499 QVarLengthArray<int> crossProducts;
500 QVarLengthArray<bool> needsClipping;
501
502 drawPolygons(bits.data(), imgWidth, imgHeight, pathVertices.data(),
503 indices, pathIndices.size(), interiorColor);
504
505 int index = 0;
506
507 while (index < pathIndices.size()) {
508 normals.clear();
509 vertices.clear();
510 needsClipping.clear();
511
512 // Find end of polygon.
513 int end = index;
514 while (indices[end] != quint32(-1))
515 ++end;
516
517 // Calculate vertex normals.
518 for (int next = index, prev = end - 1; next < end; prev = next++) {
519 quint32 fromVertexIndex = indices[prev];
520 quint32 toVertexIndex = indices[next];
521
522 const QPoint &from = pathVertices.at(fromVertexIndex);
523 const QPoint &to = pathVertices.at(toVertexIndex);
524
525 QPoint n(to.y() - from.y(), from.x() - to.x());
526 if (n.x() == 0 && n.y() == 0)
527 continue;
528 int scale = qRound((offs << 16) / qSqrt(qreal(n.x()) * n.x() + qreal(n.y()) * n.y())); // 8:16
529 Q_ASSERT(scale != 0);
530
531 n.rx() = n.x() * scale >> 8;
532 n.ry() = n.y() * scale >> 8;
533 normals.append(n);
534 QPoint v(to.x() + 0x7f, to.y() + 0x7f);
535 vertices.append(v);
536 needsClipping.append((to.x() < offs << 8) || (to.x() >= (imgWidth - offs) << 8)
537 || (to.y() < offs << 8) || (to.y() >= (imgHeight - offs) << 8));
538 }
539
540 crossProducts.resize(normals.size());
541 for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
542 crossProducts[prev] = normals.at(prev).x() * normals.at(next).y()
543 - normals.at(prev).y() * normals.at(next).x();
544 }
545
546 // Draw quads.
547 for (int next = 0, prev = normals.size() - 1; next < normals.size(); prev = next++) {
548 QPoint n = normals.at(next);
549 QPoint intPrev = vertices.at(prev);
550 QPoint extPrev = vertices.at(prev);
551 QPoint intNext = vertices.at(next);
552 QPoint extNext = vertices.at(next);
553
554 extPrev.rx() -= n.x();
555 extPrev.ry() -= n.y();
556 intPrev.rx() += n.x();
557 intPrev.ry() += n.y();
558 extNext.rx() -= n.x();
559 extNext.ry() -= n.y();
560 intNext.rx() += n.x();
561 intNext.ry() += n.y();
562
563 if (needsClipping[prev] || needsClipping[next]) {
564 drawRectangle<Clip>(bits.data(), imgWidth, imgHeight,
565 &intPrev, &vertices.at(prev), &extPrev,
566 &intNext, &vertices.at(next), &extNext,
567 exteriorColor);
568 } else {
569 drawRectangle<NoClip>(bits.data(), imgWidth, imgHeight,
570 &intPrev, &vertices.at(prev), &extPrev,
571 &intNext, &vertices.at(next), &extNext,
572 exteriorColor);
573 }
574
575 const int crossProduct = crossProducts.at(prev);
576 if (crossProduct < 0) {
577 QPoint p = extPrev;
578 if (needsClipping[prev]) {
579 for (;;) {
580 QPoint rn((n.x() * rotation.x() - n.y() * rotation.y()) >> 14,
581 (n.y() * rotation.x() + n.x() * rotation.y()) >> 14);
582 n = rn;
583 if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() <= 0) {
584 p.rx() = vertices.at(prev).x() - normals.at(prev).x();
585 p.ry() = vertices.at(prev).y() - normals.at(prev).y();
586 drawTriangle<Clip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
587 &extPrev, &p, exteriorColor);
588 break;
589 }
590
591 p.rx() = vertices.at(prev).x() - n.x();
592 p.ry() = vertices.at(prev).y() - n.y();
593 drawTriangle<Clip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
594 &extPrev, &p, exteriorColor);
595 extPrev = p;
596 }
597 } else {
598 for (;;) {
599 QPoint rn((n.x() * rotation.x() - n.y() * rotation.y()) >> 14,
600 (n.y() * rotation.x() + n.x() * rotation.y()) >> 14);
601 n = rn;
602 if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() <= 0) {
603 p.rx() = vertices.at(prev).x() - normals.at(prev).x();
604 p.ry() = vertices.at(prev).y() - normals.at(prev).y();
605 drawTriangle<NoClip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
606 &extPrev, &p, exteriorColor);
607 break;
608 }
609
610 p.rx() = vertices.at(prev).x() - n.x();
611 p.ry() = vertices.at(prev).y() - n.y();
612 drawTriangle<NoClip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
613 &extPrev, &p, exteriorColor);
614 extPrev = p;
615 }
616 }
617 } else if (crossProduct > 0) {
618 QPoint p = intPrev;
619 if (needsClipping[prev]) {
620 for (;;) {
621 QPoint rn((n.x() * rotation.x() + n.y() * rotation.y()) >> 14,
622 (n.y() * rotation.x() - n.x() * rotation.y()) >> 14);
623 n = rn;
624 if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() >= 0) {
625 p.rx() = vertices.at(prev).x() + normals.at(prev).x();
626 p.ry() = vertices.at(prev).y() + normals.at(prev).y();
627 drawTriangle<Clip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
628 &p, &intPrev, interiorColor);
629 break;
630 }
631
632 p.rx() = vertices.at(prev).x() + n.x();
633 p.ry() = vertices.at(prev).y() + n.y();
634 drawTriangle<Clip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
635 &p, &intPrev, interiorColor);
636 intPrev = p;
637 }
638 } else {
639 for (;;) {
640 QPoint rn((n.x() * rotation.x() + n.y() * rotation.y()) >> 14,
641 (n.y() * rotation.x() - n.x() * rotation.y()) >> 14);
642 n = rn;
643 if (n.x() * normals.at(prev).y() - n.y() * normals.at(prev).x() >= 0) {
644 p.rx() = vertices.at(prev).x() + normals.at(prev).x();
645 p.ry() = vertices.at(prev).y() + normals.at(prev).y();
646 drawTriangle<NoClip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
647 &p, &intPrev, interiorColor);
648 break;
649 }
650
651 p.rx() = vertices.at(prev).x() + n.x();
652 p.ry() = vertices.at(prev).y() + n.y();
653 drawTriangle<NoClip>(bits.data(), imgWidth, imgHeight, &vertices.at(prev),
654 &p, &intPrev, interiorColor);
655 intPrev = p;
656 }
657 }
658 }
659 }
660
661 index = end + 1;
662 }
663
664 const qint32 *inLine = bits.data();
665 uchar *outLine = data->data;
666 for (int y = 0; y < imgHeight; ++y) {
667 for (int x = 0; x < imgWidth; ++x, ++inLine, ++outLine)
668 *outLine = uchar((0x7f80 - *inLine) >> 8);
669 }
670}
671
672static bool imageHasNarrowOutlines(const QImage &im)
673{
674 if (im.isNull() || im.width() < 1 || im.height() < 1)
675 return false;
676 else if (im.width() == 1 || im.height() == 1)
677 return true;
678
679 int minHThick = 999;
680 int minVThick = 999;
681
682 int thick = 0;
683 bool in = false;
684 int y = (im.height() + 1) / 2;
685 for (int x = 0; x < im.width(); ++x) {
686 int a = qAlpha(im.pixel(x, y));
687 if (a > 127) {
688 in = true;
689 ++thick;
690 } else if (in) {
691 in = false;
692 minHThick = qMin(minHThick, thick);
693 thick = 0;
694 }
695 }
696
697 thick = 0;
698 in = false;
699 int x = (im.width() + 1) / 2;
700 for (int y = 0; y < im.height(); ++y) {
701 int a = qAlpha(im.pixel(x, y));
702 if (a > 127) {
703 in = true;
704 ++thick;
705 } else if (in) {
706 in = false;
707 minVThick = qMin(minVThick, thick);
708 thick = 0;
709 }
710 }
711
712 return minHThick == 1 || minVThick == 1;
713}
714
719
721{
722 static bool initialized = false;
723 if (initialized)
724 return;
725 initialized = true;
726
727 if (qEnvironmentVariableIsSet("QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE")) {
728 QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE = qEnvironmentVariableIntValue("QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE");
729 qCDebug(lcDistanceField) << "set the QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE:" << QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE;
730 }
731
732 if (qEnvironmentVariableIsSet("QT_DISTANCEFIELD_DEFAULT_SCALE")) {
733 QT_DISTANCEFIELD_DEFAULT_SCALE = qEnvironmentVariableIntValue("QT_DISTANCEFIELD_DEFAULT_SCALE");
734 qCDebug(lcDistanceField) << "set the QT_DISTANCEFIELD_DEFAULT_SCALE:" << QT_DISTANCEFIELD_DEFAULT_SCALE;
735 }
736 if (qEnvironmentVariableIsSet("QT_DISTANCEFIELD_DEFAULT_RADIUS")) {
737 QT_DISTANCEFIELD_DEFAULT_RADIUS = qEnvironmentVariableIntValue("QT_DISTANCEFIELD_DEFAULT_RADIUS");
738 qCDebug(lcDistanceField) << "set the QT_DISTANCEFIELD_DEFAULT_RADIUS:" << QT_DISTANCEFIELD_DEFAULT_RADIUS;
739 }
740 if (qEnvironmentVariableIsSet("QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT")) {
741 QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT = qEnvironmentVariableIntValue("QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT");
742 qCDebug(lcDistanceField) << "set the QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT:" << QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT;
743 }
744}
745
746bool qt_fontHasNarrowOutlines(QFontEngine *fontEngine)
747{
749 QFontEngine *fe = fontEngine->cloneWithSize(QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE);
750 if (!fe)
751 return false;
752
753 QImage im;
754
755 const glyph_t glyph = fe->glyphIndex('O');
756 if (glyph != 0)
757 im = fe->alphaMapForGlyph(glyph, QFixedPoint(), QTransform());
758
759 Q_ASSERT(fe->ref.loadRelaxed() == 0);
760 delete fe;
761
762 return imageHasNarrowOutlines(im);
763}
764
765bool qt_fontHasNarrowOutlines(const QRawFont &f)
766{
767 QRawFont font = f;
769 font.setPixelSize(QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE);
770 if (!font.isValid())
771 return false;
772
773 QList<quint32> glyphIndices = font.glyphIndexesForString("O"_L1);
774 if (glyphIndices.isEmpty() || glyphIndices[0] == 0)
775 return false;
776
777 return imageHasNarrowOutlines(font.alphaMapForGlyph(glyphIndices.at(0),
778 QRawFont::PixelAntialiasing));
779}
780
781int QT_DISTANCEFIELD_BASEFONTSIZE(bool narrowOutlineFont)
782{
784
785 if (Q_UNLIKELY(narrowOutlineFont))
787 else
789}
790
791int QT_DISTANCEFIELD_SCALE(bool narrowOutlineFont)
792{
794
795 if (Q_UNLIKELY(narrowOutlineFont))
797 else
799}
800
801int QT_DISTANCEFIELD_RADIUS(bool narrowOutlineFont)
802{
804
805 if (Q_UNLIKELY(narrowOutlineFont))
807 else
809}
810
816
817QDistanceFieldData::QDistanceFieldData(const QDistanceFieldData &other)
818 : QSharedData(other)
819 , glyph(other.glyph)
820 , width(other.width)
821 , height(other.height)
822 , nbytes(other.nbytes)
823{
824 if (nbytes && other.data)
825 data = (uchar *)memcpy(malloc(nbytes), other.data, nbytes);
826 else
827 data = nullptr;
828}
829
830QDistanceFieldData::~QDistanceFieldData()
831{
832 QtPrivate::sizedFree(data, nbytes);
833}
834
835QDistanceFieldData *QDistanceFieldData::create(const QSize &size)
836{
837 QDistanceFieldData *data = new QDistanceFieldData;
838
839 if (size.isValid()) {
840 data->width = size.width();
841 data->height = size.height();
842 // pixel data stored as a 1-byte alpha value
843 data->nbytes = data->width * data->height; // tightly packed
844 data->data = (uchar *)malloc(data->nbytes);
845 }
846
847 return data;
848}
849
850QDistanceFieldData *QDistanceFieldData::create(QSize size, const QPainterPath &path, bool doubleResolution)
851{
852 QDistanceFieldData *data = create(size);
853 makeDistanceField(data,
854 path,
855 QT_DISTANCEFIELD_SCALE(doubleResolution),
856 QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution));
857 return data;
858}
859
860
861QDistanceFieldData *QDistanceFieldData::create(const QPainterPath &path, bool doubleResolution)
862{
863 int dfMargin = QT_DISTANCEFIELD_RADIUS(doubleResolution) / QT_DISTANCEFIELD_SCALE(doubleResolution);
864 int glyphWidth = qCeil(path.boundingRect().width() / QT_DISTANCEFIELD_SCALE(doubleResolution)) + dfMargin * 2;
865 int glyphHeight = qCeil(path.boundingRect().height() / QT_DISTANCEFIELD_SCALE(doubleResolution)) + dfMargin * 2;
866
867 return create(QSize(glyphWidth, glyphHeight), path, doubleResolution);
868}
869
870
871QDistanceField::QDistanceField()
872 : d(new QDistanceFieldData)
873{
874}
875
876QDistanceField::QDistanceField(int width, int height)
877 : d(QDistanceFieldData::create(QSize(width, height)))
878{
879}
880
881QDistanceField::QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution)
882{
883 setGlyph(font, glyph, doubleResolution);
884}
885
886QDistanceField::QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution)
887{
888 setGlyph(fontEngine, glyph, doubleResolution);
889}
890
891QDistanceField::QDistanceField(QSize size, const QPainterPath &path, glyph_t glyph, bool doubleResolution)
892{
893 QPainterPath dfPath = path;
894 dfPath.translate(-dfPath.boundingRect().topLeft());
895 dfPath.setFillRule(Qt::WindingFill);
896
897 d = QDistanceFieldData::create(size, dfPath, doubleResolution);
898 d->glyph = glyph;
899}
900
901QDistanceField::QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution)
902{
903 QPainterPath dfPath = path;
904 dfPath.translate(-dfPath.boundingRect().topLeft());
905 dfPath.setFillRule(Qt::WindingFill);
906
907 d = QDistanceFieldData::create(dfPath, doubleResolution);
908 d->glyph = glyph;
909}
910
911
912QDistanceField::QDistanceField(QDistanceFieldData *data)
913 : d(data)
914{
915}
916
917bool QDistanceField::isNull() const
918{
919 return !d->data;
920}
921
922glyph_t QDistanceField::glyph() const
923{
924 return d->glyph;
925}
926
927void QDistanceField::setGlyph(const QRawFont &font, glyph_t glyph, bool doubleResolution)
928{
929 QRawFont renderFont = font;
930 renderFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(doubleResolution) * QT_DISTANCEFIELD_SCALE(doubleResolution));
931
932 QPainterPath path = renderFont.pathForGlyph(glyph);
933 path.translate(-path.boundingRect().topLeft());
934 path.setFillRule(Qt::WindingFill);
935
936 d = QDistanceFieldData::create(path, doubleResolution);
937 d->glyph = glyph;
938}
939
940void QDistanceField::setGlyph(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution)
941{
942 QFixedPoint position;
943 QPainterPath path;
944 fontEngine->addGlyphsToPath(&glyph, &position, 1, &path, { });
945 path.translate(-path.boundingRect().topLeft());
946 path.setFillRule(Qt::WindingFill);
947
948 d = QDistanceFieldData::create(path, doubleResolution);
949 d->glyph = glyph;
950}
951
952int QDistanceField::width() const
953{
954 return d->width;
955}
956
957int QDistanceField::height() const
958{
959 return d->height;
960}
961
962QDistanceField QDistanceField::copy(const QRect &r) const
963{
964 if (isNull())
965 return QDistanceField();
966
967 if (r.isNull())
968 return QDistanceField(new QDistanceFieldData(*d));
969
970 int x = r.x();
971 int y = r.y();
972 int w = r.width();
973 int h = r.height();
974
975 int dx = 0;
976 int dy = 0;
977 if (w <= 0 || h <= 0)
978 return QDistanceField();
979
980 QDistanceField df(w, h);
981 if (df.isNull())
982 return df;
983
984 if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
985 memset(df.d->data, 0, df.d->nbytes);
986 if (x < 0) {
987 dx = -x;
988 x = 0;
989 }
990 if (y < 0) {
991 dy = -y;
992 y = 0;
993 }
994 }
995
996 int pixels_to_copy = qMax(w - dx, 0);
997 if (x > d->width)
998 pixels_to_copy = 0;
999 else if (pixels_to_copy > d->width - x)
1000 pixels_to_copy = d->width - x;
1001 int lines_to_copy = qMax(h - dy, 0);
1002 if (y > d->height)
1003 lines_to_copy = 0;
1004 else if (lines_to_copy > d->height - y)
1005 lines_to_copy = d->height - y;
1006
1007 const uchar *src = d->data + x + y * d->width;
1008 uchar *dest = df.d->data + dx + dy * df.d->width;
1009 for (int i = 0; i < lines_to_copy; ++i) {
1010 memcpy(dest, src, pixels_to_copy);
1011 src += d->width;
1012 dest += df.d->width;
1013 }
1014
1015 df.d->glyph = d->glyph;
1016
1017 return df;
1018}
1019
1020uchar *QDistanceField::bits()
1021{
1022 return d->data;
1023}
1024
1025const uchar *QDistanceField::bits() const
1026{
1027 return d->data;
1028}
1029
1030const uchar *QDistanceField::constBits() const
1031{
1032 return d->data;
1033}
1034
1035uchar *QDistanceField::scanLine(int i)
1036{
1037 if (isNull())
1038 return nullptr;
1039
1040 Q_ASSERT(i >= 0 && i < d->height);
1041 return d->data + i * d->width;
1042}
1043
1044const uchar *QDistanceField::scanLine(int i) const
1045{
1046 if (isNull())
1047 return nullptr;
1048
1049 Q_ASSERT(i >= 0 && i < d->height);
1050 return d->data + i * d->width;
1051}
1052
1053const uchar *QDistanceField::constScanLine(int i) const
1054{
1055 if (isNull())
1056 return nullptr;
1057
1058 Q_ASSERT(i >= 0 && i < d->height);
1059 return d->data + i * d->width;
1060}
1061
1062QImage QDistanceField::toImage(QImage::Format format) const
1063{
1064 if (isNull())
1065 return QImage();
1066
1067 QImage image(d->width, d->height, qt_depthForFormat(format) == 8 ?
1068 format : QImage::Format_ARGB32_Premultiplied);
1069 if (image.isNull())
1070 return image;
1071
1072 if (image.depth() == 8) {
1073 for (int y = 0; y < d->height; ++y)
1074 memcpy(image.scanLine(y), scanLine(y), d->width);
1075 } else {
1076 for (int y = 0; y < d->height; ++y) {
1077 for (int x = 0; x < d->width; ++x) {
1078 uint alpha = *(d->data + x + y * d->width);
1079 image.setPixel(x, y, alpha << 24);
1080 }
1081 }
1082
1083 if (image.format() != format)
1084 image = std::move(image).convertToFormat(format);
1085 }
1086
1087 return image;
1088}
1089
1090QT_END_NAMESPACE
The QRawFont class provides access to a single physical instance of a font.
Definition qrawfont.h:24
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
static void makeDistanceField(QDistanceFieldData *data, const QPainterPath &path, int dfScale, int offs)
int QT_DISTANCEFIELD_HIGHGLYPHCOUNT()
bool qt_fontHasNarrowOutlines(const QRawFont &f)
void fillLines(qint32 *bits, int width, int height, int upperY, int lowerY, int &lx, int ldx, int &rx, int rdx, qint32 &d, qint32 ddy, qint32 ddx)
static int QT_DISTANCEFIELD_DEFAULT_BASEFONTSIZE
static void drawPolygons(qint32 *bits, int width, int height, const QPoint *vertices, const quint32 *indices, int indexCount, qint32 value)
static int QT_DISTANCEFIELD_DEFAULT_HIGHGLYPHCOUNT
void drawRectangle(qint32 *bits, int width, int height, const QPoint *int1, const QPoint *center1, const QPoint *ext1, const QPoint *int2, const QPoint *center2, const QPoint *ext2, qint32 extValue)
void fillLine(qint32 *, int, int, int, qint32, qint32)
static int QT_DISTANCEFIELD_DEFAULT_RADIUS
bool qt_fontHasNarrowOutlines(QFontEngine *fontEngine)
int QT_DISTANCEFIELD_RADIUS(bool narrowOutlineFont)
int QT_DISTANCEFIELD_SCALE(bool narrowOutlineFont)
static int QT_DISTANCEFIELD_DEFAULT_SCALE
void fillLine< Clip, LeftToRight >(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd)
static void initialDistanceFieldFactor()
void drawTriangle(qint32 *bits, int width, int height, const QPoint *center, const QPoint *v1, const QPoint *v2, qint32 value)
int QT_DISTANCEFIELD_BASEFONTSIZE(bool narrowOutlineFont)
static bool imageHasNarrowOutlines(const QImage &im)
void fillLine< Clip, RightToLeft >(qint32 *line, int width, int lx, int rx, qint32 d, qint32 dd)