diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ui/text.c | 264 | ||||
-rw-r--r-- | src/ui/text.h | 11 |
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) | |||
25 | struct Impl_Glyph { | 25 | struct 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 | ||
32 | void init_Glyph(iGlyph *d, iChar ch) { | 32 | void 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 | ||
39 | void deinit_Glyph(iGlyph *d) { | 39 | void deinit_Glyph(iGlyph *d) { |
@@ -48,7 +48,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
48 | 48 | ||
49 | /*-----------------------------------------------------------------------------------------------*/ | 49 | /*-----------------------------------------------------------------------------------------------*/ |
50 | 50 | ||
51 | iDeclareType(Font) | 51 | iDeclareType(Font) |
52 | 52 | ||
53 | struct Impl_Font { | 53 | struct 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 | ||
63 | static void init_Font(iFont *d, const iBlock *data, int height, int bloff) { | 64 | static iFont *font_Text_(enum iFontId id); |
65 | |||
66 | static 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 | ||
76 | static void deinit_Font(iFont *d) { | 80 | static 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 | ||
169 | iFont *font_Text_(enum iFontId id) { | ||
170 | return &text_.fonts[id]; | ||
171 | } | ||
172 | |||
153 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch, float xShift) { | 173 | static 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 | ||
164 | static iBool isSpecialChar_(iChar ch) { | ||
165 | return ch >= specialSymbol_Text && ch < 0x20; | ||
166 | } | ||
167 | |||
168 | static float symbolEmWidth_(int symbol) { | ||
169 | return 1.5f; | ||
170 | } | ||
171 | |||
172 | static float symbolAdvance_(int symbol) { | ||
173 | return 1.5f; | ||
174 | } | ||
175 | |||
176 | static int specialChar_(iChar ch) { | ||
177 | return ch - specialSymbol_Text; | ||
178 | } | ||
179 | |||
180 | iLocalDef SDL_Rect sdlRect_(const iRect rect) { | 184 | iLocalDef 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 | ||
185 | static 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 | |||
217 | static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | 188 | static 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 | |||
243 | iLocalDef iBool isEmoji_(iChar ch) { | ||
244 | return ch >= 0x1f000 && ch < 0x20000; | ||
245 | } | ||
246 | |||
247 | iLocalDef 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 | ||
380 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | 254 | static 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 | ||
611 | void drawCentered_Text(int fontId, iRect rect, int color, const char *text, ...) { | 486 | void 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 | |||
37 | enum iSpecialSymbol { | ||
38 | silence_SpecialSymbol, | ||
39 | }; | ||
40 | |||
41 | void init_Text (SDL_Renderer *); | 40 | void init_Text (SDL_Renderer *); |
42 | void deinit_Text (void); | 41 | void deinit_Text (void); |
43 | 42 | ||