summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--res/LICENSE_OFL.txt92
-rw-r--r--res/NotoEmoji-Regular.ttfbin0 -> 418804 bytes
-rw-r--r--src/ui/text.c264
-rw-r--r--src/ui/text.h11
5 files changed, 168 insertions, 200 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0aaa2bb5..a7e38f5f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ set (EMBED_RESOURCES
19 res/FiraSans-Bold.ttf 19 res/FiraSans-Bold.ttf
20 res/FiraSans-LightItalic.ttf 20 res/FiraSans-LightItalic.ttf
21 res/FiraMono-Regular.ttf 21 res/FiraMono-Regular.ttf
22 res/NotoEmoji-Regular.ttf
22 res/SourceSansPro-Regular.ttf 23 res/SourceSansPro-Regular.ttf
23) 24)
24# if (UNIX AND NOT APPLE) 25# if (UNIX AND NOT APPLE)
diff --git a/res/LICENSE_OFL.txt b/res/LICENSE_OFL.txt
new file mode 100644
index 00000000..d952d62c
--- /dev/null
+++ b/res/LICENSE_OFL.txt
@@ -0,0 +1,92 @@
1This Font Software is licensed under the SIL Open Font License,
2Version 1.1.
3
4This license is copied below, and is also available with a FAQ at:
5http://scripts.sil.org/OFL
6
7-----------------------------------------------------------
8SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9-----------------------------------------------------------
10
11PREAMBLE
12The goals of the Open Font License (OFL) are to stimulate worldwide
13development of collaborative font projects, to support the font
14creation efforts of academic and linguistic communities, and to
15provide a free and open framework in which fonts may be shared and
16improved in partnership with others.
17
18The OFL allows the licensed fonts to be used, studied, modified and
19redistributed freely as long as they are not sold by themselves. The
20fonts, including any derivative works, can be bundled, embedded,
21redistributed and/or sold with any software provided that any reserved
22names are not used by derivative works. The fonts and derivatives,
23however, cannot be released under any other type of license. The
24requirement for fonts to remain under this license does not apply to
25any document created using the fonts or their derivatives.
26
27DEFINITIONS
28"Font Software" refers to the set of files released by the Copyright
29Holder(s) under this license and clearly marked as such. This may
30include source files, build scripts and documentation.
31
32"Reserved Font Name" refers to any names specified as such after the
33copyright statement(s).
34
35"Original Version" refers to the collection of Font Software
36components as distributed by the Copyright Holder(s).
37
38"Modified Version" refers to any derivative made by adding to,
39deleting, or substituting -- in part or in whole -- any of the
40components of the Original Version, by changing formats or by porting
41the Font Software to a new environment.
42
43"Author" refers to any designer, engineer, programmer, technical
44writer or other person who contributed to the Font Software.
45
46PERMISSION & CONDITIONS
47Permission is hereby granted, free of charge, to any person obtaining
48a copy of the Font Software, to use, study, copy, merge, embed,
49modify, redistribute, and sell modified and unmodified copies of the
50Font Software, subject to the following conditions:
51
521) Neither the Font Software nor any of its individual components, in
53Original or Modified Versions, may be sold by itself.
54
552) Original or Modified Versions of the Font Software may be bundled,
56redistributed and/or sold with any software, provided that each copy
57contains the above copyright notice and this license. These can be
58included either as stand-alone text files, human-readable headers or
59in the appropriate machine-readable metadata fields within text or
60binary files as long as those fields can be easily viewed by the user.
61
623) No Modified Version of the Font Software may use the Reserved Font
63Name(s) unless explicit written permission is granted by the
64corresponding Copyright Holder. This restriction only applies to the
65primary font name as presented to the users.
66
674) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68Software shall not be used to promote, endorse or advertise any
69Modified Version, except to acknowledge the contribution(s) of the
70Copyright Holder(s) and the Author(s) or with their explicit written
71permission.
72
735) The Font Software, modified or unmodified, in part or in whole,
74must be distributed entirely under this license, and must not be
75distributed under any other license. The requirement for fonts to
76remain under this license does not apply to any document created using
77the Font Software.
78
79TERMINATION
80This license becomes null and void if any of the above conditions are
81not met.
82
83DISCLAIMER
84THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/res/NotoEmoji-Regular.ttf b/res/NotoEmoji-Regular.ttf
new file mode 100644
index 00000000..19b7badf
--- /dev/null
+++ b/res/NotoEmoji-Regular.ttf
Binary files differ
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