summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-26 14:29:31 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-26 14:29:31 +0300
commit5aa20a2cc80543366d2e2c321902570dee1c385c (patch)
tree92e7dc8edcb7903d31609759b798d44b6bc796d6 /src
parent07b10a92a467a8a2ceb045e5eae37a1be440a1ca (diff)
Text: Added a new font for Emoji
Noto Emoji by Google, pretty good but not ideal.
Diffstat (limited to 'src')
-rw-r--r--src/ui/text.c264
-rw-r--r--src/ui/text.h11
2 files changed, 75 insertions, 200 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 26bb9599..3ea046cb 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -25,15 +25,15 @@ iDeclareTypeConstructionArgs(Glyph, iChar ch)
25struct Impl_Glyph { 25struct Impl_Glyph {
26 iHashNode node; 26 iHashNode node;
27 iRect rect[2]; /* zero and half pixel offset */ 27 iRect rect[2]; /* zero and half pixel offset */
28 int advance;
29 iInt2 d[2]; 28 iInt2 d[2];
29 float advance; /* scaled */
30}; 30};
31 31
32void init_Glyph(iGlyph *d, iChar ch) { 32void init_Glyph(iGlyph *d, iChar ch) {
33 d->node.key = ch; 33 d->node.key = ch;
34 d->rect[0] = zero_Rect(); 34 d->rect[0] = zero_Rect();
35 d->rect[1] = zero_Rect(); 35 d->rect[1] = zero_Rect();
36 d->advance = 0; 36 d->advance = 0.0f;
37} 37}
38 38
39void deinit_Glyph(iGlyph *d) { 39void deinit_Glyph(iGlyph *d) {
@@ -48,7 +48,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
48 48
49/*-----------------------------------------------------------------------------------------------*/ 49/*-----------------------------------------------------------------------------------------------*/
50 50
51iDeclareType(Font) 51iDeclareType(Font)
52 52
53struct Impl_Font { 53struct Impl_Font {
54 iBlock * data; 54 iBlock * data;
@@ -58,9 +58,12 @@ struct Impl_Font {
58 int baseline; 58 int baseline;
59 iHash glyphs; 59 iHash glyphs;
60 int baselineOffset; 60 int baselineOffset;
61 enum iFontId emojiFont; /* font to use for emojis */
61}; 62};
62 63
63static void init_Font(iFont *d, const iBlock *data, int height, int bloff) { 64static iFont *font_Text_(enum iFontId id);
65
66static void init_Font(iFont *d, const iBlock *data, int height, int bloff, enum iFontId emojiFont) {
64 init_Hash(&d->glyphs); 67 init_Hash(&d->glyphs);
65 d->baselineOffset = bloff; 68 d->baselineOffset = bloff;
66 d->data = NULL; 69 d->data = NULL;
@@ -69,8 +72,9 @@ static void init_Font(iFont *d, const iBlock *data, int height, int bloff) {
69 stbtt_InitFont(&d->font, constData_Block(data), 0); 72 stbtt_InitFont(&d->font, constData_Block(data), 0);
70 d->scale = stbtt_ScaleForPixelHeight(&d->font, height); 73 d->scale = stbtt_ScaleForPixelHeight(&d->font, height);
71 int ascent; 74 int ascent;
72 stbtt_GetFontVMetrics(&d->font, &ascent, 0, 0); 75 stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL);
73 d->baseline = (int) ascent * d->scale; 76 d->baseline = (int) ascent * d->scale;
77 d->emojiFont = emojiFont;
74} 78}
75 79
76static void deinit_Font(iFont *d) { 80static void deinit_Font(iFont *d) {
@@ -120,21 +124,33 @@ void init_Text(SDL_Renderer *render) {
120 d->cacheRowHeight = 0; 124 d->cacheRowHeight = 0;
121 } 125 }
122 /* Load the fonts. */ { 126 /* Load the fonts. */ {
123 const struct { const iBlock *ttf; int size; } fontData[max_FontId] = { 127 const struct {
124 { &fontSourceSansProRegular_Embedded, fontSize_UI }, 128 const iBlock *ttf;
125 { &fontFiraSansRegular_Embedded, fontSize_UI }, 129 int size;
126 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f }, 130 int emojiFont;
127 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f }, 131 } fontData[max_FontId] = {
128 { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f }, 132 { &fontSourceSansProRegular_Embedded, fontSize_UI, emoji_FontId },
129 { &fontFiraSansLightItalic_Embedded, fontSize_UI }, 133 { &fontFiraSansRegular_Embedded, fontSize_UI, emoji_FontId },
130 { &fontFiraSansBold_Embedded, fontSize_UI }, 134 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId },
131 { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f }, 135 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f, smallEmoji_FontId },
132 { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f }, 136 { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId },
133 { &fontFiraSansBold_Embedded, fontSize_UI * 2.0f }, 137 { &fontFiraSansLightItalic_Embedded, fontSize_UI, emoji_FontId },
138 { &fontFiraSansBold_Embedded, fontSize_UI, emoji_FontId },
139 { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId },
140 { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId },
141 { &fontFiraSansBold_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId },
142 { &fontNotoEmojiRegular_Embedded, fontSize_UI, emoji_FontId },
143 { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.333f, mediumEmoji_FontId },
144 { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, largeEmoji_FontId },
145 { &fontNotoEmojiRegular_Embedded, fontSize_UI * 2.000f, hugeEmoji_FontId },
146 { &fontNotoEmojiRegular_Embedded, fontSize_UI * 0.866f, smallEmoji_FontId },
134 }; 147 };
135 iForIndices(i, fontData) { 148 iForIndices(i, fontData) {
136 init_Font(&d->fonts[i], fontData[i].ttf, fontData[i].size, 149 init_Font(&d->fonts[i],
137 i == 0 ? fontSize_UI / 20 : 0); 150 fontData[i].ttf,
151 fontData[i].size,
152 i == 0 ? fontSize_UI / 18 : 0,
153 fontData[i].emojiFont);
138 } 154 }
139 } 155 }
140} 156}
@@ -150,6 +166,10 @@ void deinit_Text(void) {
150 iRelease(d->ansiEscape); 166 iRelease(d->ansiEscape);
151} 167}
152 168
169iFont *font_Text_(enum iFontId id) {
170 return &text_.fonts[id];
171}
172
153static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) { 173static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) {
154 int w, h; 174 int w, h;
155 uint8_t *bmp = stbtt_GetCodepointBitmapSubpixel( 175 uint8_t *bmp = stbtt_GetCodepointBitmapSubpixel(
@@ -161,59 +181,10 @@ static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift)
161 return surface; 181 return surface;
162} 182}
163 183
164static iBool isSpecialChar_(iChar ch) {
165 return ch >= specialSymbol_Text && ch < 0x20;
166}
167
168static float symbolEmWidth_(int symbol) {
169 return 1.5f;
170}
171
172static float symbolAdvance_(int symbol) {
173 return 1.5f;
174}
175
176static int specialChar_(iChar ch) {
177 return ch - specialSymbol_Text;
178}
179
180iLocalDef SDL_Rect sdlRect_(const iRect rect) { 184iLocalDef SDL_Rect sdlRect_(const iRect rect) {
181 return (SDL_Rect){ rect.pos.x, rect.pos.y, rect.size.x, rect.size.y }; 185 return (SDL_Rect){ rect.pos.x, rect.pos.y, rect.size.x, rect.size.y };
182} 186}
183 187
184#if 0
185static void fillTriangle_(SDL_Surface *surface, const SDL_Rect *rect, int dir) {
186 const uint32_t color = 0xffffffff;
187 SDL_LockSurface(surface);
188 uint32_t *row = surface->pixels;
189 row += rect->x + rect->y * surface->pitch / 4;
190 for (int y = 0; y < rect->h; y++, row += surface->pitch / 4) {
191 float norm = (float) y / (float) (rect->h - 1) * 2.0f;
192 if (norm > 1.0f) norm = 2.0f - norm;
193 const int len = norm * rect->w;
194 const float fract = norm * rect->w - len;
195 const uint32_t fractColor = 0xffffff00 | (int) (fract * 0xff);
196 if (dir > 0) {
197 for (int x = 0; x < len; x++) {
198 row[x] = color;
199 }
200 if (len < rect->w) {
201 row[len] = fractColor;
202 }
203 }
204 else {
205 for (int x = 0; x < len; x++) {
206 row[rect->w - len + x] = color;
207 }
208 if (len < rect->w) {
209 row[rect->w - len - 1] = fractColor;
210 }
211 }
212 }
213 SDL_UnlockSurface(surface);
214}
215#endif
216
217static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { 188static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
218 iText *txt = &text_; 189 iText *txt = &text_;
219 SDL_Renderer *render = txt->render; 190 SDL_Renderer *render = txt->render;
@@ -222,12 +193,12 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
222 const iChar ch = char_Glyph(glyph); 193 const iChar ch = char_Glyph(glyph);
223 iBool fromStb = iFalse; 194 iBool fromStb = iFalse;
224 iRect *glRect = &glyph->rect[hoff]; 195 iRect *glRect = &glyph->rect[hoff];
225 if (!isSpecialChar_(ch)) { 196 /* Rasterize the glyph using stbtt. */ {
226 /* Rasterize the glyph using stbtt. */
227 surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f); 197 surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f);
228 if (hoff == 0) { 198 if (hoff == 0) {
229 int lsb; 199 int adv, lsb;
230 stbtt_GetCodepointHMetrics(&d->font, ch, &glyph->advance, &lsb); 200 stbtt_GetCodepointHMetrics(&d->font, ch, &adv, &lsb);
201 glyph->advance = d->scale * adv;
231 } 202 }
232 stbtt_GetCodepointBitmapBoxSubpixel(&d->font, 203 stbtt_GetCodepointBitmapBoxSubpixel(&d->font,
233 ch, 204 ch,
@@ -244,47 +215,6 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
244 tex = SDL_CreateTextureFromSurface(render, surface); 215 tex = SDL_CreateTextureFromSurface(render, surface);
245 glRect->size = init_I2(surface->w, surface->h); 216 glRect->size = init_I2(surface->w, surface->h);
246 } 217 }
247 else {
248 /* Metrics for special symbols. */
249 int em, lsb;
250 const int symbol = specialChar_(ch);
251 stbtt_GetCodepointHMetrics(&d->font, 'M', &em, &lsb);
252 glyph->d[hoff].x = d->baseline / 10;
253 glyph->d[hoff].y = -d->baseline;
254 glyph->advance = em * symbolAdvance_(symbol);
255 glyph->rect[hoff].size = init_I2(symbolEmWidth_(symbol) * em * d->scale, d->height);
256#if 0
257 if (isRasterizedSymbol_(ch)) {
258 /* Rasterize manually. */
259 surface = SDL_CreateRGBSurfaceWithFormat(
260 0, width_Rect(glyph->rect), height_Rect(glyph->rect), 32, SDL_PIXELFORMAT_RGBA8888);
261 SDL_FillRect(surface, NULL, 0);
262 const uint32_t white = 0xffffffff;
263 switch (specialChar_(ch)) {
264 case play_SpecialSymbol:
265 fillTriangle_(surface, &(SDL_Rect){ 0, 0, surface->w, d->baseline }, 1);
266 break;
267 case pause_SpecialSymbol: {
268 const int w = surface->w * 4 / 11;
269 SDL_FillRect(surface, &(SDL_Rect){ 0, 0, w, d->baseline }, white);
270 SDL_FillRect(surface, &(SDL_Rect){ surface->w - w, 0, w, d->baseline }, white);
271 break;
272 }
273 case rewind_SpecialSymbol: {
274 const int w1 = surface->w / 7;
275 const int w2 = surface->w * 3 / 7;
276 const int h = d->baseline * 4 / 5;
277 const int off = (d->baseline - h) / 2;
278 SDL_FillRect(surface, &(SDL_Rect){ 0, off, w1, h}, white);
279 fillTriangle_(surface, &(SDL_Rect){ w1, off, w2, h }, -1);
280 fillTriangle_(surface, &(SDL_Rect){ surface->w * 4 / 7, off, w2, h }, -1);
281 break;
282 }
283 }
284 tex = SDL_CreateTextureFromSurface(render, surface);
285 }
286#endif
287 }
288 /* Determine placement in the glyph cache texture, advancing in rows. */ 218 /* Determine placement in the glyph cache texture, advancing in rows. */
289 if (txt->cachePos.x + glRect->size.x > txt->cacheSize.x) { 219 if (txt->cachePos.x + glRect->size.x > txt->cacheSize.x) {
290 txt->cachePos.x = 0; 220 txt->cachePos.x = 0;
@@ -294,87 +224,31 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
294 glRect->pos = txt->cachePos; 224 glRect->pos = txt->cachePos;
295 SDL_SetRenderTarget(render, txt->cache); 225 SDL_SetRenderTarget(render, txt->cache);
296 const SDL_Rect dstRect = sdlRect_(*glRect); 226 const SDL_Rect dstRect = sdlRect_(*glRect);
297 if (surface) { 227 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect);
298 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect);
299 }
300 else {
301#if 0
302 /* Draw a special symbol. */
303 SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
304 const iInt2 tl = init_I2(dstRect.x, dstRect.y);
305 const iInt2 br = init_I2(dstRect.x + dstRect.w - 1, dstRect.y + dstRect.h - 1);
306 const int midX = tl.x + dstRect.w / 2;
307 const int midY = tl.y + dstRect.h / 2;
308 const int symH = dstRect.h * 2 / 6;
309 /* Frame. */
310 if (isFramedSymbol_(ch)) {
311 SDL_RenderDrawLines(
312 render,
313 (SDL_Point[]){
314 { tl.x, tl.y }, { br.x, tl.y }, { br.x, br.y }, { tl.x, br.y }, { tl.x, tl.y } },
315 5);
316 }
317 iArray points;
318 init_Array(&points, sizeof(SDL_Point));
319 switch (specialChar_(ch)) {
320 case 0: /* silence */
321 break;
322 case 1: /* sine */
323 for (int i = 0; i < dstRect.w; ++i) {
324 float rad = 2.0f * iMathPif * (float) i / dstRect.w;
325 SDL_Point pt = { tl.x + i, midY + sin(rad) * symH};
326 pushBack_Array(&points, &pt);
327 }
328 SDL_RenderDrawLines(render, constData_Array(&points), size_Array(&points));
329 break;
330 case 2: /* square */
331 SDL_RenderDrawLines(render,
332 (SDL_Point[]){ { tl.x, midY - symH },
333 { midX, midY - symH },
334 { midX, midY + symH },
335 { br.x, midY + symH } },
336 4);
337 break;
338 case 3: /* saw */
339 SDL_RenderDrawLines(render,
340 (SDL_Point[]){ { tl.x, midY },
341 { midX, midY - symH },
342 { midX, midY + symH },
343 { br.x, midY } },
344 4);
345 break;
346 case 4: /* triangle */
347 SDL_RenderDrawLines(render,
348 (SDL_Point[]){ { tl.x, midY },
349 { tl.x + dstRect.w / 4, midY - symH },
350 { br.x - dstRect.w / 4, midY + symH },
351 { br.x, midY } },
352 4);
353 break;
354 case 5: /* noise */
355 for (int i = 0; i < dstRect.w; ++i) {
356 for (int p = 0; p < 2; ++p) {
357 const float val = iRandomf() * 2.0f - 1.0f;
358 pushBack_Array(&points, &(SDL_Point){ tl.x + i, midY - val * symH });
359 }
360 }
361 SDL_RenderDrawPoints(render, constData_Array(&points), size_Array(&points));
362 break;
363 }
364 deinit_Array(&points);
365#endif
366 }
367 SDL_SetRenderTarget(render, NULL); 228 SDL_SetRenderTarget(render, NULL);
368 if (tex) { 229 if (tex) {
369 SDL_DestroyTexture(tex); 230 SDL_DestroyTexture(tex);
370 iAssert(surface); 231 iAssert(surface);
371 if (fromStb) stbtt_FreeBitmap(surface->pixels, NULL); 232 stbtt_FreeBitmap(surface->pixels, NULL);
372 SDL_FreeSurface(surface); 233 SDL_FreeSurface(surface);
373 } 234 }
374 /* Update cache cursor. */ 235 /* Update cache cursor. */
375 txt->cachePos.x += glRect->size.x; 236 txt->cachePos.x += glRect->size.x;
376 txt->cacheRowHeight = iMax(txt->cacheRowHeight, glRect->size.y); 237 txt->cacheRowHeight = iMax(txt->cacheRowHeight, glRect->size.y);
377 iAssert(txt->cachePos.y + txt->cacheRowHeight <= txt->cacheSize.y); 238 iAssert(txt->cachePos.y + txt->cacheRowHeight <= txt->cacheSize.y);
239 /* TODO: Automatically enlarge the cache if running out of space?
240 Maybe make it paged. */
241}
242
243iLocalDef iBool isEmoji_(iChar ch) {
244 return ch >= 0x1f000 && ch < 0x20000;
245}
246
247iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch) {
248 if (isEmoji_(ch) && font_Text_(d->emojiFont) != d) {
249 return font_Text_(d->emojiFont);
250 }
251 return d;
378} 252}
379 253
380static const iGlyph *glyph_Font_(iFont *d, iChar ch) { 254static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
@@ -411,7 +285,6 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
411 int xposLimit, const char **continueFrom_out, int *runAdvance_out) { 285 int xposLimit, const char **continueFrom_out, int *runAdvance_out) {
412 iInt2 size = zero_I2(); 286 iInt2 size = zero_I2();
413 const iInt2 orig = pos; 287 const iInt2 orig = pos;
414 const stbtt_fontinfo *info = &d->font;
415 float xpos = pos.x; 288 float xpos = pos.x;
416 float xposMax = xpos; 289 float xposMax = xpos;
417 iAssert(xposLimit == 0 || mode == measure_RunMode); 290 iAssert(xposLimit == 0 || mode == measure_RunMode);
@@ -454,7 +327,8 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
454 continue; 327 continue;
455 } 328 }
456 } 329 }
457 const iGlyph *glyph = glyph_Font_(d, ch); 330 iFont *font = characterFont_Font_(d, ch); /* may switch to an Emoji font */
331 const iGlyph *glyph = glyph_Font_(font, ch);
458 int x1 = xpos; 332 int x1 = xpos;
459 const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; 333 const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0;
460 int x2 = x1 + glyph->rect[hoff].size.x; 334 int x2 = x1 + glyph->rect[hoff].size.x;
@@ -464,25 +338,26 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
464 break; 338 break;
465 } 339 }
466 size.x = iMax(size.x, x2 - orig.x); 340 size.x = iMax(size.x, x2 - orig.x);
467 size.y = iMax(size.y, pos.y + d->height - orig.y); 341 size.y = iMax(size.y, pos.y + font->height - orig.y);
468 if (mode != measure_RunMode) { 342 if (mode != measure_RunMode) {
469 SDL_Rect dst = { x1 + glyph->d[hoff].x, 343 SDL_Rect dst = { x1 + glyph->d[hoff].x,
470 pos.y + d->baseline + glyph->d[hoff].y, 344 pos.y + font->baseline + glyph->d[hoff].y,
471 glyph->rect[hoff].size.x, 345 glyph->rect[hoff].size.x,
472 glyph->rect[hoff].size.y }; 346 glyph->rect[hoff].size.y };
473 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); 347 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst);
474 } 348 }
475 xpos += d->scale * glyph->advance; 349 xpos += glyph->advance;
476 xposMax = iMax(xposMax, xpos); 350 xposMax = iMax(xposMax, xpos);
477 if (!isSpace_Char(prevCh) && isSpace_Char(ch)) { 351 if (!isSpace_Char(prevCh) && isSpace_Char(ch)) {
478 lastWordEnd = chPos; 352 lastWordEnd = chPos;
479 } 353 }
480 /* Check the next character. */ { 354 /* Check the next character. */
355 if (font == d) {
481 /* TODO: No need to decode the next char twice; check this on the next iteration. */ 356 /* TODO: No need to decode the next char twice; check this on the next iteration. */
482 const char *peek = chPos; 357 const char *peek = chPos;
483 const iChar next = nextChar_(&peek, text.end); 358 const iChar next = nextChar_(&peek, text.end);
484 if (next) { 359 if (next) {
485 xpos += d->scale * stbtt_GetCodepointKernAdvance(info, ch, next); 360 xpos += font->scale * stbtt_GetCodepointKernAdvance(&d->font, ch, next);
486 } 361 }
487 } 362 }
488 prevCh = ch; 363 prevCh = ch;
@@ -608,15 +483,16 @@ void drawString_Text(int fontId, iInt2 pos, int color, const iString *text) {
608 draw_Text_(fontId, pos, color, range_String(text)); 483 draw_Text_(fontId, pos, color, range_String(text));
609} 484}
610 485
611void drawCentered_Text(int fontId, iRect rect, int color, const char *text, ...) { 486void drawCentered_Text(int fontId, iRect rect, int color, const char *format, ...) {
612 iBlock chars; 487 iBlock chars;
613 init_Block(&chars, 0); { 488 init_Block(&chars, 0); {
614 va_list args; 489 va_list args;
615 va_start(args, text); 490 va_start(args, format);
616 vprintf_Block(&chars, text, args); 491 vprintf_Block(&chars, format, args);
617 va_end(args); 492 va_end(args);
618 } 493 }
619 const iInt2 textSize = advance_Text(fontId, cstr_Block(&chars)); 494 const char *text = cstr_Block(&chars);
495 iInt2 textSize = advance_Text(fontId, text);
620 draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color, 496 draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color,
621 (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); 497 (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) });
622 deinit_Block(&chars); 498 deinit_Block(&chars);
diff --git a/src/ui/text.h b/src/ui/text.h
index 982218eb..18718d37 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -16,6 +16,11 @@ enum iFontId {
16 mediumBold_FontId, 16 mediumBold_FontId,
17 largeBold_FontId, 17 largeBold_FontId,
18 hugeBold_FontId, 18 hugeBold_FontId,
19 emoji_FontId,
20 mediumEmoji_FontId,
21 largeEmoji_FontId,
22 hugeEmoji_FontId,
23 smallEmoji_FontId,
19 max_FontId, 24 max_FontId,
20 /* UI fonts: */ 25 /* UI fonts: */
21 uiLabel_FontId = default_FontId, 26 uiLabel_FontId = default_FontId,
@@ -32,12 +37,6 @@ enum iFontId {
32 header3_FontId = medium_FontId, 37 header3_FontId = medium_FontId,
33}; 38};
34 39
35#define specialSymbol_Text 0x10
36
37enum iSpecialSymbol {
38 silence_SpecialSymbol,
39};
40
41void init_Text (SDL_Renderer *); 40void init_Text (SDL_Renderer *);
42void deinit_Text (void); 41void deinit_Text (void);
43 42