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