Krita Source Code Documentation
Loading...
Searching...
No Matches
KisTofuGlyph.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Alvin Wong <alvin@alvinhc.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "KisTofuGlyph.h"
8
9#include <QPainterPath>
10#include <QPolygon>
11#include <QTransform>
12#include <QVector>
13
14namespace KisTofuGlyph
15{
16
17// These functions build the QPainterPath for each hex char using polygons.
18// Each char glyph is formed by 3x5 grid of squares constructed from polygons
19// wound in the clockwise direction (counterclockwise to subtract).
20
21static inline QPolygon upperHole()
22{
23 return {QVector<QPoint>{{1, 1}, {1, 2}, {2, 2}, {2, 1}}};
24}
25
26static inline QPolygon lowerHole()
27{
28 return {QVector<QPoint>{{1, 3}, {1, 4}, {2, 4}, {2, 3}}};
29}
30
31static inline QPainterPath hexChar0()
32{
33 static const QPainterPath s_path0 = []() {
34 const QPolygon b{QVector<QPoint>{{1, 1}, {1, 4}, {2, 4}, {2, 1}}};
35 QPainterPath p;
36 p.addRect(0, 0, 3, 5);
37 p.addPolygon(b);
38 return p;
39 }();
40 return s_path0;
41}
42
43static inline QPainterPath hexChar1()
44{
45 static const QPainterPath s_path1 = []() {
46 QPainterPath p;
47 p.addRect(1, 0, 1, 5);
48 return p;
49 }();
50 return s_path1;
51}
52
53static inline QPainterPath hexChar2()
54{
55 static const QPainterPath s_path2 = []() {
56 const QPolygon a{QVector<QPoint>{
57 {0, 0},
58 {3, 0},
59 {3, 3},
60 {1, 3},
61 {1, 4},
62 {3, 4},
63 {3, 5},
64 {0, 5},
65 {0, 2},
66 {2, 2},
67 {2, 1},
68 {0, 1},
69 }};
70 QPainterPath p;
71 p.addPolygon(a);
72 return p;
73 }();
74 return s_path2;
75}
76
77static inline QPainterPath hexChar3()
78{
79 static const QPainterPath s_path3 = []() {
80 const QPolygon a{QVector<QPoint>{
81 {0, 0},
82 {3, 0},
83 {3, 5},
84 {0, 5},
85 {0, 4},
86 {2, 4},
87 {2, 3},
88 {0, 3},
89 {0, 2},
90 {2, 2},
91 {2, 1},
92 {0, 1},
93 }};
94 QPainterPath p;
95 p.addPolygon(a);
96 return p;
97 }();
98 return s_path3;
99}
100
101static inline QPainterPath hexChar4()
102{
103 static const QPainterPath s_path4 = []() {
104 const QPolygon a{QVector<QPoint>{
105 {0, 0},
106 {1, 0},
107 {1, 2},
108 {2, 2},
109 {2, 0},
110 {3, 0},
111 {3, 5},
112 {2, 5},
113 {2, 3},
114 {0, 3},
115 }};
116 QPainterPath p;
117 p.addPolygon(a);
118 return p;
119 }();
120 return s_path4;
121}
122
123static inline QPainterPath hexChar5()
124{
125 static const QPainterPath s_path5 = []() {
126 // Just mirror a "2".
127 QPainterPath p = hexChar2();
128 return QTransform::fromScale(-1, 1).map(p).toReversed().translated(3, 0);
129 }();
130 return s_path5;
131}
132
133static inline QPainterPath hexChar6()
134{
135 static const QPainterPath s_path6 = []() {
136 const QPolygon a{QVector<QPoint>{
137 {0, 0},
138 {3, 0},
139 {3, 1},
140 {1, 1},
141 {1, 2},
142 {3, 2},
143 {3, 5},
144 {0, 5},
145 }};
146 QPainterPath p;
147 p.addPolygon(a);
148 p.addPolygon(lowerHole());
149 return p;
150 }();
151 return s_path6;
152}
153
154static inline QPainterPath hexChar7()
155{
156 static const QPainterPath s_path7 = []() {
157 const QPolygon a{QVector<QPoint>{
158 {0, 0},
159 {3, 0},
160 {3, 5},
161 {2, 5},
162 {2, 1},
163 {0, 1},
164 }};
165 QPainterPath p;
166 p.addPolygon(a);
167 return p;
168 }();
169 return s_path7;
170}
171
172static inline QPainterPath hexChar8()
173{
174 static const QPainterPath s_path8 = []() {
175 QPainterPath p;
176 p.addRect(0, 0, 3, 5);
177 p.addPolygon(upperHole());
178 p.addPolygon(lowerHole());
179 return p;
180 }();
181 return s_path8;
182}
183
184static inline QPainterPath hexChar9()
185{
186 static const QPainterPath s_path9 = []() {
187 // Just rotate a "6" upside-down
188 QPainterPath p = hexChar6();
189 return QTransform::fromScale(-1, -1).map(p).translated(3, 5);
190 }();
191 return s_path9;
192}
193
194static inline QPainterPath hexCharA()
195{
196 static const QPainterPath s_pathA = []() {
197 const QPolygon a{QVector<QPoint>{
198 {0, 0},
199 {3, 0},
200 {3, 5},
201 {2, 5},
202 {2, 3},
203 {1, 3},
204 {1, 5},
205 {0, 5},
206 }};
207 QPainterPath p;
208 p.addPolygon(a);
209 p.addPolygon(upperHole());
210 return p;
211 }();
212 return s_pathA;
213}
214
215static inline QPainterPath hexCharB()
216{
217 static const QPainterPath s_pathB = []() {
218 const QPolygon a{QVector<QPoint>{
219 {0, 0},
220 {2, 0},
221 {2, 1},
222 {3, 1},
223 {3, 2},
224 {2, 2},
225 {2, 3},
226 {3, 3},
227 {3, 4},
228 {2, 4},
229 {2, 5},
230 {0, 5},
231 }};
232 QPainterPath p;
233 p.addPolygon(a);
234 p.addPolygon(upperHole());
235 p.addPolygon(lowerHole());
236 return p;
237 }();
238 return s_pathB;
239}
240
241static inline QPainterPath hexCharC()
242{
243 static const QPainterPath s_pathC = []() {
244 const QPolygon a{QVector<QPoint>{
245 {0, 0},
246 {3, 0},
247 {3, 1},
248 {1, 1},
249 {1, 4},
250 {3, 4},
251 {3, 5},
252 {0, 5},
253 }};
254 QPainterPath p;
255 p.addPolygon(a);
256 return p;
257 }();
258 return s_pathC;
259}
260
261static inline QPainterPath hexCharD()
262{
263 static const QPainterPath s_pathD = []() {
264 const QPolygon a{QVector<QPoint>{
265 {0, 0},
266 {2, 0},
267 {2, 1},
268 {1, 1},
269 {1, 4},
270 {2, 4},
271 {2, 5},
272 {0, 5},
273 }};
274 const QPolygon b{QVector<QPoint>{{2, 1}, {3, 1}, {3, 4}, {2, 4}}};
275 QPainterPath p;
276 p.addPolygon(a);
277 p.addPolygon(b);
278 return p;
279 }();
280 return s_pathD;
281}
282
283static inline QPainterPath hexCharE()
284{
285 static const QPainterPath s_pathE = []() {
286 const QPolygon a{QVector<QPoint>{
287 {0, 0},
288 {3, 0},
289 {3, 1},
290 {1, 1},
291 {1, 2},
292 {3, 2},
293 {3, 3},
294 {1, 3},
295 {1, 4},
296 {3, 4},
297 {3, 5},
298 {0, 5},
299 }};
300 QPainterPath p;
301 p.addPolygon(a);
302 return p;
303 }();
304 return s_pathE;
305}
306
307static inline QPainterPath hexCharF()
308{
309 static const QPainterPath s_pathF = []() {
310 const QPolygon a{QVector<QPoint>{
311 {0, 0},
312 {3, 0},
313 {3, 1},
314 {1, 1},
315 {1, 2},
316 {3, 2},
317 {3, 3},
318 {1, 3},
319 {1, 5},
320 {0, 5},
321 }};
322 QPainterPath p;
323 p.addPolygon(a);
324 return p;
325 }();
326 return s_pathF;
327}
328
329static QPainterPath getHexChar(unsigned value)
330{
331 switch (value) {
332 case 0x0:
333 return hexChar0();
334 case 0x1:
335 return hexChar1();
336 case 0x2:
337 return hexChar2();
338 case 0x3:
339 return hexChar3();
340 case 0x4:
341 return hexChar4();
342 case 0x5:
343 return hexChar5();
344 case 0x6:
345 return hexChar6();
346 case 0x7:
347 return hexChar7();
348 case 0x8:
349 return hexChar8();
350 case 0x9:
351 return hexChar9();
352 case 0xA:
353 return hexCharA();
354 case 0xB:
355 return hexCharB();
356 case 0xC:
357 return hexCharC();
358 case 0xD:
359 return hexCharD();
360 case 0xE:
361 return hexCharE();
362 case 0xF:
363 return hexCharF();
364 }
365 return {};
366}
367
371static void addHexChar(QPainterPath &p, unsigned value, int row, int col)
372{
373 QPainterPath glyph = getHexChar(value);
374 glyph.translate(2 + col * 4, 2 + row * 6);
375 p.addPath(glyph);
376}
377
385static constexpr unsigned valueAt(const char32_t codepoint, const unsigned place)
386{
387 return (codepoint >> (place * 4)) & 0xF;
388}
389
393static inline QPainterPath makeFrame(const int width)
394{
395 const int inner = width - 1;
396 const QPolygon a{QVector<QPoint>{{0, 0}, {width, 0}, {width, 15}, {0, 15}}};
397 const QPolygon b{QVector<QPoint>{{1, 1}, {1, 14}, {inner, 14}, {inner, 1}}};
398 QPainterPath p;
399 p.addPolygon(a);
400 p.addPolygon(b);
401 return p;
402}
403
404QPainterPath create(const char32_t codepoint, double height)
405{
406 // We build the glyph as a 15x15 or 11x15 grid of squares.
407 QPainterPath p;
408 if (codepoint > 0xFFFF) {
409 // Codepoints outside the BMP need more than 4 digits to display, so we show 6.
410 // +---+
411 // |01F|
412 // |389| => U+1F389
413 // +---+
414 static const QPainterPath s_outline15 = makeFrame(15);
415 p.addPath(s_outline15);
416 addHexChar(p, valueAt(codepoint, 5), 0, 0);
417 addHexChar(p, valueAt(codepoint, 4), 0, 1);
418 addHexChar(p, valueAt(codepoint, 3), 0, 2);
419 addHexChar(p, valueAt(codepoint, 2), 1, 0);
420 addHexChar(p, valueAt(codepoint, 1), 1, 1);
421 addHexChar(p, valueAt(codepoint, 0), 1, 2);
422 } else {
423 // +--+
424 // |27|
425 // |64| => U+2764
426 // +--+
427 static const QPainterPath s_outline11 = makeFrame(11);
428 p.addPath(s_outline11);
429 addHexChar(p, valueAt(codepoint, 3), 0, 0);
430 addHexChar(p, valueAt(codepoint, 2), 0, 1);
431 addHexChar(p, valueAt(codepoint, 1), 1, 0);
432 addHexChar(p, valueAt(codepoint, 0), 1, 1);
433 }
434 const auto scale = (1. / 15.) * height;
435 return QTransform::fromScale(scale, scale).map(p);
436}
437
438} // namespace KisTofuGlyph
float value(const T *src, size_t ch)
const Params2D p
Tasty tofu for Kiki.
static QPainterPath hexChar9()
static QPainterPath hexChar7()
static QPainterPath hexCharB()
static QPolygon lowerHole()
static QPainterPath hexCharC()
static QPainterPath getHexChar(unsigned value)
static QPainterPath hexChar3()
QPainterPath create(const char32_t codepoint, double height)
Creates a tofu missing glyph indicator representing the provided Unicode codepoint.
static QPainterPath hexChar8()
static QPainterPath hexChar0()
static QPainterPath hexCharF()
static QPainterPath hexChar1()
static QPolygon upperHole()
static QPainterPath hexChar2()
static QPainterPath hexChar6()
static QPainterPath makeFrame(const int width)
Creates the frame of a tofu glyph.
static QPainterPath hexCharA()
static QPainterPath hexChar5()
static constexpr unsigned valueAt(const char32_t codepoint, const unsigned place)
Gets the hex digit at a place.
static void addHexChar(QPainterPath &p, unsigned value, int row, int col)
Adds a hex char at the specified row/column to the QPainterPath.
static QPainterPath hexCharE()
static QPainterPath hexChar4()
static QPainterPath hexCharD()