diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-27 07:30:04 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-27 07:30:04 +0300 |
commit | 9b078897b581e7a9108d5ffab83f3c0a4ec720ee (patch) | |
tree | 6ae16fca7dd2bfae5e60163f117078859334dd57 /src/ui | |
parent | 6d8d188237324fc1379859251ca66f1ba4247589 (diff) |
Added a subset of Symbola for more symbols
Non-ugly emoticons, box drawing, math symbols, etc.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/text.c | 89 | ||||
-rw-r--r-- | src/ui/text.h | 7 |
2 files changed, 57 insertions, 39 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 3ea046cb..c8e8c787 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -19,11 +19,13 @@ | |||
19 | #include <SDL_hints.h> | 19 | #include <SDL_hints.h> |
20 | #include <stdarg.h> | 20 | #include <stdarg.h> |
21 | 21 | ||
22 | iDeclareType(Font) | ||
22 | iDeclareType(Glyph) | 23 | iDeclareType(Glyph) |
23 | iDeclareTypeConstructionArgs(Glyph, iChar ch) | 24 | iDeclareTypeConstructionArgs(Glyph, iChar ch) |
24 | 25 | ||
25 | struct Impl_Glyph { | 26 | struct Impl_Glyph { |
26 | iHashNode node; | 27 | iHashNode node; |
28 | const iFont *font; /* may come from symbols/emoji */ | ||
27 | iRect rect[2]; /* zero and half pixel offset */ | 29 | iRect rect[2]; /* zero and half pixel offset */ |
28 | iInt2 d[2]; | 30 | iInt2 d[2]; |
29 | float advance; /* scaled */ | 31 | float advance; /* scaled */ |
@@ -31,6 +33,7 @@ struct Impl_Glyph { | |||
31 | 33 | ||
32 | void init_Glyph(iGlyph *d, iChar ch) { | 34 | void init_Glyph(iGlyph *d, iChar ch) { |
33 | d->node.key = ch; | 35 | d->node.key = ch; |
36 | d->font = NULL; | ||
34 | d->rect[0] = zero_Rect(); | 37 | d->rect[0] = zero_Rect(); |
35 | d->rect[1] = zero_Rect(); | 38 | d->rect[1] = zero_Rect(); |
36 | d->advance = 0.0f; | 39 | d->advance = 0.0f; |
@@ -48,8 +51,6 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
48 | 51 | ||
49 | /*-----------------------------------------------------------------------------------------------*/ | 52 | /*-----------------------------------------------------------------------------------------------*/ |
50 | 53 | ||
51 | iDeclareType(Font) | ||
52 | |||
53 | struct Impl_Font { | 54 | struct Impl_Font { |
54 | iBlock * data; | 55 | iBlock * data; |
55 | stbtt_fontinfo font; | 56 | stbtt_fontinfo font; |
@@ -58,12 +59,12 @@ struct Impl_Font { | |||
58 | int baseline; | 59 | int baseline; |
59 | iHash glyphs; | 60 | iHash glyphs; |
60 | int baselineOffset; | 61 | int baselineOffset; |
61 | enum iFontId emojiFont; /* font to use for emojis */ | 62 | enum iFontId symbolsFont; /* font to use for symbols */ |
62 | }; | 63 | }; |
63 | 64 | ||
64 | static iFont *font_Text_(enum iFontId id); | 65 | static iFont *font_Text_(enum iFontId id); |
65 | 66 | ||
66 | static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum iFontId emojiFont) { | 67 | static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum iFontId symbolsFont) { |
67 | init_Hash(&d->glyphs); | 68 | init_Hash(&d->glyphs); |
68 | d->baselineOffset = bloff; | 69 | d->baselineOffset = bloff; |
69 | d->data = NULL; | 70 | d->data = NULL; |
@@ -74,7 +75,7 @@ static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum | |||
74 | int ascent; | 75 | int ascent; |
75 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); | 76 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); |
76 | d->baseline = (int) ascent * d->scale; | 77 | d->baseline = (int) ascent * d->scale; |
77 | d->emojiFont = emojiFont; | 78 | d->symbolsFont = symbolsFont; |
78 | } | 79 | } |
79 | 80 | ||
80 | static void deinit_Font(iFont *d) { | 81 | static void deinit_Font(iFont *d) { |
@@ -127,30 +128,35 @@ void init_Text(SDL_Renderer *render) { | |||
127 | const struct { | 128 | const struct { |
128 | const iBlock *ttf; | 129 | const iBlock *ttf; |
129 | int size; | 130 | int size; |
130 | int emojiFont; | 131 | int symbolsFont; |
131 | } fontData[max_FontId] = { | 132 | } fontData[max_FontId] = { |
132 | { &fontSourceSansProRegular_Embedded, fontSize_UI, emoji_FontId }, | 133 | { &fontSourceSansProRegular_Embedded, fontSize_UI, symbols_FontId }, |
133 | { &fontFiraSansRegular_Embedded, fontSize_UI, emoji_FontId }, | 134 | { &fontFiraSansRegular_Embedded, fontSize_UI, symbols_FontId }, |
134 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId }, | 135 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, |
135 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f, smallEmoji_FontId }, | 136 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f, smallSymbols_FontId }, |
136 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, | 137 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, |
137 | { &fontFiraSansLightItalic_Embedded, fontSize_UI, emoji_FontId }, | 138 | { &fontFiraSansLightItalic_Embedded, fontSize_UI, symbols_FontId }, |
138 | { &fontFiraSansBold_Embedded, fontSize_UI, emoji_FontId }, | 139 | { &fontFiraSansBold_Embedded, fontSize_UI, symbols_FontId }, |
139 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, | 140 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, |
140 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId }, | 141 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, |
141 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId }, | 142 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, |
142 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, emoji_FontId }, | 143 | { &fontSymbolaSubset_Embedded, fontSize_UI, symbols_FontId }, |
143 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId }, | 144 | { &fontSymbolaSubset_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, |
144 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId }, | 145 | { &fontSymbolaSubset_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, |
145 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId }, | 146 | { &fontSymbolaSubset_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, |
146 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId }, | 147 | { &fontSymbolaSubset_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, |
148 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, symbols_FontId }, | ||
149 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, | ||
150 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, | ||
151 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, | ||
152 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, | ||
147 | }; | 153 | }; |
148 | iForIndices(i, fontData) { | 154 | iForIndices(i, fontData) { |
149 | init_Font(&d->fonts[i], | 155 | init_Font(&d->fonts[i], |
150 | fontData[i].ttf, | 156 | fontData[i].ttf, |
151 | fontData[i].size, | 157 | fontData[i].size, |
152 | i == 0 ? fontSize_UI / 18 : 0, | 158 | i == 0 ? fontSize_UI / 18 : 0, |
153 | fontData[i].emojiFont); | 159 | fontData[i].symbolsFont); |
154 | } | 160 | } |
155 | } | 161 | } |
156 | } | 162 | } |
@@ -240,26 +246,31 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
240 | Maybe make it paged. */ | 246 | Maybe make it paged. */ |
241 | } | 247 | } |
242 | 248 | ||
243 | iLocalDef iBool isEmoji_(iChar ch) { | ||
244 | return ch >= 0x1f000 && ch < 0x20000; | ||
245 | } | ||
246 | |||
247 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch) { | 249 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch) { |
248 | if (isEmoji_(ch) && font_Text_(d->emojiFont) != d) { | 250 | if (stbtt_FindGlyphIndex(&d->font, ch) != 0) { |
249 | return font_Text_(d->emojiFont); | 251 | return d; |
252 | } | ||
253 | /* Not defined in current font, try symbols. */ | ||
254 | iFont *symbols = font_Text_(d->symbolsFont); | ||
255 | if (symbols != d && stbtt_FindGlyphIndex(&symbols->font, ch)) { | ||
256 | return symbols; | ||
250 | } | 257 | } |
251 | return d; | 258 | /* Perhaps it's Emoji. */ |
259 | return font_Text_(d->symbolsFont + fromSymbolsToEmojiOffset_FontId); | ||
252 | } | 260 | } |
253 | 261 | ||
254 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | 262 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { |
255 | const void *node = value_Hash(&d->glyphs, ch); | 263 | /* It may actually come from a different font. */ |
264 | iFont *font = characterFont_Font_(d, ch); | ||
265 | const void *node = value_Hash(&font->glyphs, ch); | ||
256 | if (node) { | 266 | if (node) { |
257 | return node; | 267 | return node; |
258 | } | 268 | } |
259 | iGlyph *glyph = new_Glyph(ch); | 269 | iGlyph *glyph = new_Glyph(ch); |
260 | cache_Font_(d, glyph, 0); | 270 | glyph->font = font; |
261 | cache_Font_(d, glyph, 1); /* half-pixel offset */ | 271 | cache_Font_(font, glyph, 0); |
262 | insert_Hash(&d->glyphs, &glyph->node); | 272 | cache_Font_(font, glyph, 1); /* half-pixel offset */ |
273 | insert_Hash(&font->glyphs, &glyph->node); | ||
263 | return glyph; | 274 | return glyph; |
264 | } | 275 | } |
265 | 276 | ||
@@ -327,8 +338,8 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
327 | continue; | 338 | continue; |
328 | } | 339 | } |
329 | } | 340 | } |
330 | iFont *font = characterFont_Font_(d, ch); /* may switch to an Emoji font */ | 341 | /* TODO: Remember the glyph's font, no need to look it up constantly. */ |
331 | const iGlyph *glyph = glyph_Font_(font, ch); | 342 | const iGlyph *glyph = glyph_Font_(d, ch); |
332 | int x1 = xpos; | 343 | int x1 = xpos; |
333 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; | 344 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; |
334 | int x2 = x1 + glyph->rect[hoff].size.x; | 345 | int x2 = x1 + glyph->rect[hoff].size.x; |
@@ -338,10 +349,10 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
338 | break; | 349 | break; |
339 | } | 350 | } |
340 | size.x = iMax(size.x, x2 - orig.x); | 351 | size.x = iMax(size.x, x2 - orig.x); |
341 | size.y = iMax(size.y, pos.y + font->height - orig.y); | 352 | size.y = iMax(size.y, pos.y + glyph->font->height - orig.y); |
342 | if (mode != measure_RunMode) { | 353 | if (mode != measure_RunMode) { |
343 | SDL_Rect dst = { x1 + glyph->d[hoff].x, | 354 | SDL_Rect dst = { x1 + glyph->d[hoff].x, |
344 | pos.y + font->baseline + glyph->d[hoff].y, | 355 | pos.y + glyph->font->baseline + glyph->d[hoff].y, |
345 | glyph->rect[hoff].size.x, | 356 | glyph->rect[hoff].size.x, |
346 | glyph->rect[hoff].size.y }; | 357 | glyph->rect[hoff].size.y }; |
347 | SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); | 358 | SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); |
@@ -352,12 +363,12 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
352 | lastWordEnd = chPos; | 363 | lastWordEnd = chPos; |
353 | } | 364 | } |
354 | /* Check the next character. */ | 365 | /* Check the next character. */ |
355 | if (font == d) { | 366 | if (glyph->font == d) { |
356 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ | 367 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ |
357 | const char *peek = chPos; | 368 | const char *peek = chPos; |
358 | const iChar next = nextChar_(&peek, text.end); | 369 | const iChar next = nextChar_(&peek, text.end); |
359 | if (next) { | 370 | if (next) { |
360 | xpos += font->scale * stbtt_GetCodepointKernAdvance(&d->font, ch, next); | 371 | xpos += d->scale * stbtt_GetCodepointKernAdvance(&d->font, ch, next); |
361 | } | 372 | } |
362 | } | 373 | } |
363 | prevCh = ch; | 374 | prevCh = ch; |
diff --git a/src/ui/text.h b/src/ui/text.h index 18718d37..0e432043 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -16,12 +16,19 @@ enum iFontId { | |||
16 | mediumBold_FontId, | 16 | mediumBold_FontId, |
17 | largeBold_FontId, | 17 | largeBold_FontId, |
18 | hugeBold_FontId, | 18 | hugeBold_FontId, |
19 | symbols_FontId, | ||
20 | mediumSymbols_FontId, | ||
21 | largeSymbols_FontId, | ||
22 | hugeSymbols_FontId, | ||
23 | smallSymbols_FontId, | ||
19 | emoji_FontId, | 24 | emoji_FontId, |
20 | mediumEmoji_FontId, | 25 | mediumEmoji_FontId, |
21 | largeEmoji_FontId, | 26 | largeEmoji_FontId, |
22 | hugeEmoji_FontId, | 27 | hugeEmoji_FontId, |
23 | smallEmoji_FontId, | 28 | smallEmoji_FontId, |
24 | max_FontId, | 29 | max_FontId, |
30 | /* Meta: */ | ||
31 | fromSymbolsToEmojiOffset_FontId = 5, | ||
25 | /* UI fonts: */ | 32 | /* UI fonts: */ |
26 | uiLabel_FontId = default_FontId, | 33 | uiLabel_FontId = default_FontId, |
27 | uiShortcuts_FontId = default_FontId, | 34 | uiShortcuts_FontId = default_FontId, |