summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-27 07:30:04 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-27 07:30:04 +0300
commit9b078897b581e7a9108d5ffab83f3c0a4ec720ee (patch)
tree6ae16fca7dd2bfae5e60163f117078859334dd57 /src/ui
parent6d8d188237324fc1379859251ca66f1ba4247589 (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.c89
-rw-r--r--src/ui/text.h7
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
22iDeclareType(Font)
22iDeclareType(Glyph) 23iDeclareType(Glyph)
23iDeclareTypeConstructionArgs(Glyph, iChar ch) 24iDeclareTypeConstructionArgs(Glyph, iChar ch)
24 25
25struct Impl_Glyph { 26struct 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
32void init_Glyph(iGlyph *d, iChar ch) { 34void 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
51iDeclareType(Font)
52
53struct Impl_Font { 54struct 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
64static iFont *font_Text_(enum iFontId id); 65static iFont *font_Text_(enum iFontId id);
65 66
66static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum iFontId emojiFont) { 67static 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
80static void deinit_Font(iFont *d) { 81static 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
243iLocalDef iBool isEmoji_(iChar ch) {
244 return ch >= 0x1f000 && ch < 0x20000;
245}
246
247iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch) { 249iLocalDef 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
254static const iGlyph *glyph_Font_(iFont *d, iChar ch) { 262static 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,