diff options
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 337 |
1 files changed, 194 insertions, 143 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index e3f7f7c4..86fdc084 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -123,40 +123,57 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
123 | 123 | ||
124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); | 124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); |
125 | 125 | ||
126 | struct Impl_Font { | 126 | iDeclareType(GlyphTable) |
127 | iBlock * data; | 127 | |
128 | enum iTextFont family; | 128 | struct Impl_GlyphTable { |
129 | stbtt_fontinfo font; | 129 | iHash glyphs; /* key is glyph index in the font */ |
130 | float xScale, yScale; | 130 | /* TODO: `glyphs` does not need to be a Hash. |
131 | int vertOffset; /* offset due to scaling */ | 131 | We could lazily allocate an array with glyphCount elements instead. */ |
132 | int height; | ||
133 | int baseline; | ||
134 | iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */ | ||
135 | iBool isMonospaced; | ||
136 | float emAdvance; | ||
137 | enum iFontSize sizeId; /* used to look up different fonts of matching size */ | ||
138 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ | 132 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ |
139 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | ||
140 | hb_blob_t * hbBlob; /* raw TrueType data */ | ||
141 | hb_face_t * hbFace; | ||
142 | hb_font_t * hbMainFont; | ||
143 | hb_font_t * hbFont; /* may be a sub-font with customized font metrics */ | ||
144 | #endif | ||
145 | }; | 133 | }; |
146 | 134 | ||
147 | static iFont *font_Text_(enum iFontId id); | 135 | static void clearGlyphs_Font_(iGlyphTable *d) { |
148 | 136 | iForEach(Hash, i, &d->glyphs) { | |
149 | #if 0 | 137 | delete_Glyph((iGlyph *) i.value); |
150 | static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, | 138 | } |
151 | hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, | 139 | clear_Hash(&d->glyphs); |
152 | void *userData) { | ||
153 | return 100; | ||
154 | } | 140 | } |
155 | #endif | ||
156 | 141 | ||
157 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, | 142 | static void init_GlyphTable(iGlyphTable *d) { |
158 | enum iFontSize sizeId, iBool isMonospaced) { | ||
159 | init_Hash(&d->glyphs); | 143 | init_Hash(&d->glyphs); |
144 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); | ||
145 | } | ||
146 | |||
147 | static void deinit_GlyphTable(iGlyphTable *d) { | ||
148 | clearGlyphs_Font_(d); | ||
149 | deinit_Hash(&d->glyphs); | ||
150 | } | ||
151 | |||
152 | iDefineTypeConstruction(GlyphTable) | ||
153 | |||
154 | struct Impl_Font { | ||
155 | const iFontSpec *fontSpec; | ||
156 | const iFontFile *fontFile; | ||
157 | int height; | ||
158 | int baseline; | ||
159 | int vertOffset; /* offset due to glyph scaling */ | ||
160 | float xScale, yScale; | ||
161 | float emAdvance; | ||
162 | iGlyphTable * table; | ||
163 | }; | ||
164 | |||
165 | iLocalDef iBool isMonospaced_Font(const iFont *d) { | ||
166 | return (d->fontSpec->flags & monospace_FontSpecFlag) != 0; | ||
167 | } | ||
168 | |||
169 | static iFont *font_Text_(enum iFontId id); | ||
170 | |||
171 | static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile, | ||
172 | enum iFontSize sizeId, int height) { | ||
173 | d->fontSpec = fontSpec; | ||
174 | d->fontFile = fontFile; | ||
175 | /* TODO: Nunito kerning fixes need to be a font parameter of its own. */ | ||
176 | #if 0 | ||
160 | d->data = NULL; | 177 | d->data = NULL; |
161 | d->family = undefined_TextFont; | 178 | d->family = undefined_TextFont; |
162 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ | 179 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ |
@@ -177,95 +194,49 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
177 | data == &fontSmolEmojiRegular_Embedded) { | 194 | data == &fontSmolEmojiRegular_Embedded) { |
178 | d->family = emojiAndSymbols_TextFont; | 195 | d->family = emojiAndSymbols_TextFont; |
179 | } | 196 | } |
180 | d->isMonospaced = isMonospaced; | 197 | #endif |
198 | // d->isMonospaced = (fontSpec->flags & monospace_FontSpecFlag) != 0; | ||
181 | d->height = height; | 199 | d->height = height; |
182 | iZap(d->font); | 200 | //iZap(d->font); |
183 | stbtt_InitFont(&d->font, constData_Block(data), 0); | 201 | // stbtt_InitFont(&d->font, constData_Block(data), 0); |
184 | int ascent, descent, emAdv; | 202 | // int ascent, descent, emAdv; |
185 | stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); | 203 | // stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); |
186 | stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); | 204 | // stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); |
187 | d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; | 205 | const float scale = fontSpec->scaling; |
188 | if (d->isMonospaced) { | 206 | d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, height) * scale; |
207 | if (isMonospaced_Font(d)) { | ||
189 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that | 208 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that |
190 | box-drawing characters don't have partially occupied edge pixels, leading to seams | 209 | box-drawing characters don't have partially occupied edge pixels, leading to seams |
191 | between adjacent glyphs. */ | 210 | between adjacent glyphs. */ |
192 | const float advance = (float) emAdv * d->xScale; | 211 | const float advance = (float) fontFile->emAdvance * d->xScale; |
193 | if (advance > 4) { /* not too tiny */ | 212 | if (advance > 4) { /* not too tiny */ |
194 | d->xScale *= floorf(advance) / advance; | 213 | d->xScale *= floorf(advance) / advance; |
195 | } | 214 | } |
196 | } | 215 | } |
197 | d->emAdvance = emAdv * d->xScale; | 216 | d->emAdvance = fontFile->emAdvance * d->xScale; |
198 | d->baseline = ascent * d->yScale; | 217 | d->baseline = fontFile->ascent * d->yScale; |
199 | d->vertOffset = height * (1.0f - scale) / 2; | 218 | d->vertOffset = height * (1.0f - scale) / 2 * fontSpec->vertOffset; |
200 | /* Custom tweaks. */ | 219 | d->table = NULL; |
201 | if (data == &fontNotoSansSymbolsRegular_Embedded) { | ||
202 | d->vertOffset *= 1.2f; | ||
203 | } | ||
204 | else if (data == &fontNotoSansSymbols2Regular_Embedded) { | ||
205 | d->vertOffset /= 2; | ||
206 | } | ||
207 | else if (data == &fontNotoEmojiRegular_Embedded) { | ||
208 | //d->vertOffset -= height / 30; | ||
209 | } | ||
210 | d->sizeId = sizeId; | ||
211 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); | ||
212 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) | ||
213 | /* HarfBuzz will read the font data. */ { | ||
214 | d->hbBlob = hb_blob_create(constData_Block(data), size_Block(data), | ||
215 | HB_MEMORY_MODE_READONLY, NULL, NULL); | ||
216 | d->hbFace = hb_face_create(d->hbBlob, 0); | ||
217 | d->hbMainFont = hb_font_create(d->hbFace); | ||
218 | #if 0 | ||
219 | /* TODO: The custom kerning function doesn't get called? | ||
220 | Maybe HarfBuzz needs FreeType to do kerning? */ | ||
221 | if (d->family == nunito_TextFont) { | ||
222 | /* Customize the kerning of Nunito. */ | ||
223 | d->hbFont = hb_font_create_sub_font(d->hbMainFont); | ||
224 | hb_font_funcs_t *ffs = hb_font_funcs_create(); | ||
225 | hb_font_funcs_set_glyph_h_kerning_func(ffs, hbGlyphHKernForNunito_, d, NULL); | ||
226 | hb_font_set_funcs(d->hbFont, ffs, NULL, NULL); | ||
227 | hb_font_funcs_destroy(ffs); | ||
228 | } | ||
229 | else | ||
230 | #endif | ||
231 | { | ||
232 | d->hbFont = hb_font_reference(d->hbMainFont); | ||
233 | } | ||
234 | } | ||
235 | #endif | ||
236 | } | ||
237 | |||
238 | static void clearGlyphs_Font_(iFont *d) { | ||
239 | iForEach(Hash, i, &d->glyphs) { | ||
240 | delete_Glyph((iGlyph *) i.value); | ||
241 | } | ||
242 | clear_Hash(&d->glyphs); | ||
243 | } | 220 | } |
244 | 221 | ||
245 | static void deinit_Font(iFont *d) { | 222 | static void deinit_Font(iFont *d) { |
246 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) | 223 | delete_GlyphTable(d->table); |
247 | /* HarfBuzz objects. */ { | ||
248 | hb_font_destroy(d->hbFont); | ||
249 | hb_font_destroy(d->hbMainFont); | ||
250 | hb_face_destroy(d->hbFace); | ||
251 | hb_blob_destroy(d->hbBlob); | ||
252 | } | ||
253 | #endif | ||
254 | clearGlyphs_Font_(d); | ||
255 | deinit_Hash(&d->glyphs); | ||
256 | delete_Block(d->data); | ||
257 | } | 224 | } |
258 | 225 | ||
259 | static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { | 226 | static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { |
260 | /* TODO: Add a small cache of ~5 most recently found indices. */ | 227 | /* TODO: Add a small cache of ~5 most recently found indices. */ |
261 | const size_t entry = ch - 32; | 228 | const size_t entry = ch - 32; |
262 | if (entry < iElemCount(d->indexTable)) { | 229 | if (!d->table) { |
263 | if (d->indexTable[entry] == ~0u) { | 230 | d->table = new_GlyphTable(); |
264 | d->indexTable[entry] = stbtt_FindGlyphIndex(&d->font, ch); | 231 | } |
232 | iGlyphTable *table = d->table; | ||
233 | if (entry < iElemCount(table->indexTable)) { | ||
234 | if (table->indexTable[entry] == ~0u) { | ||
235 | table->indexTable[entry] = findGlyphIndex_FontFile(d->fontFile, ch); | ||
265 | } | 236 | } |
266 | return d->indexTable[entry]; | 237 | return table->indexTable[entry]; |
267 | } | 238 | } |
268 | return stbtt_FindGlyphIndex(&d->font, ch); | 239 | return findGlyphIndex_FontFile(d->fontFile, ch); |
269 | } | 240 | } |
270 | 241 | ||
271 | /*----------------------------------------------------------------------------------------------*/ | 242 | /*----------------------------------------------------------------------------------------------*/ |
@@ -282,7 +253,8 @@ struct Impl_Text { | |||
282 | enum iTextFont contentFont; | 253 | enum iTextFont contentFont; |
283 | enum iTextFont headingFont; | 254 | enum iTextFont headingFont; |
284 | float contentFontSize; | 255 | float contentFontSize; |
285 | iFont fonts[max_FontId]; | 256 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
257 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | ||
286 | SDL_Renderer * render; | 258 | SDL_Renderer * render; |
287 | SDL_Texture * cache; | 259 | SDL_Texture * cache; |
288 | iInt2 cacheSize; | 260 | iInt2 cacheSize; |
@@ -296,12 +268,49 @@ struct Impl_Text { | |||
296 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 268 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
297 | 269 | ||
298 | static iText *activeText_; | 270 | static iText *activeText_; |
299 | static iBlock *userFont_; | 271 | |
272 | static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) { | ||
273 | #if defined (iPlatformMobile) | ||
274 | const float uiSize = fontSize_UI * 1.1f; | ||
275 | #else | ||
276 | const float uiSize = fontSize_UI; | ||
277 | #endif | ||
278 | const float textSize = fontSize_UI * d->contentFontSize; | ||
279 | // const float monoSize = textSize * 0.71f; | ||
280 | // const float smallMonoSize = monoSize * 0.8f; | ||
281 | if (spec->flags & override_FontSpecFlag && d->overrideFontId < 0) { | ||
282 | /* This is the highest priority override font. */ | ||
283 | d->overrideFontId = baseId; | ||
284 | } | ||
285 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { | ||
286 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { | ||
287 | init_Font(font_Text_(FONT_ID(baseId, style, sizeId)), | ||
288 | spec, | ||
289 | spec->styles[style], | ||
290 | sizeId, | ||
291 | (sizeId < contentRegular_FontSize ? uiSize : textSize) * scale_FontSize(sizeId)); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
297 | return at_Array(&activeText_->fonts, id & mask_FontId); | ||
298 | } | ||
299 | |||
300 | static enum iFontId fontId_Text_(const iFont *font) { | ||
301 | return (enum iFontId) (font - (const iFont *) constData_Array(&activeText_->fonts)); | ||
302 | } | ||
303 | |||
304 | iLocalDef enum iFontSize sizeId_Text_(const iFont *d) { | ||
305 | return fontId_Text_(d) % max_FontSize; | ||
306 | } | ||
307 | |||
308 | iLocalDef enum iFontStyle styleId_Text_(const iFont *d) { | ||
309 | return (fontId_Text_(d) / max_FontSize) % max_FontStyle; | ||
310 | } | ||
300 | 311 | ||
301 | static void initFonts_Text_(iText *d) { | 312 | static void initFonts_Text_(iText *d) { |
302 | const float textSize = fontSize_UI * d->contentFontSize; | 313 | #if 0 |
303 | const float monoSize = textSize * 0.71f; | ||
304 | const float smallMonoSize = monoSize * 0.8f; | ||
305 | const iBlock *regularFont = &fontNunitoRegular_Embedded; | 314 | const iBlock *regularFont = &fontNunitoRegular_Embedded; |
306 | const iBlock *boldFont = &fontNunitoBold_Embedded; | 315 | const iBlock *boldFont = &fontNunitoBold_Embedded; |
307 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; | 316 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; |
@@ -367,15 +376,10 @@ static void initFonts_Text_(iText *d) { | |||
367 | h12Font = &fontIosevkaTermExtended_Embedded; | 376 | h12Font = &fontIosevkaTermExtended_Embedded; |
368 | h3Font = &fontIosevkaTermExtended_Embedded; | 377 | h3Font = &fontIosevkaTermExtended_Embedded; |
369 | } | 378 | } |
370 | #if defined (iPlatformMobile) | ||
371 | const float uiSize = fontSize_UI * 1.1f; | ||
372 | #else | ||
373 | const float uiSize = fontSize_UI; | ||
374 | #endif | ||
375 | const struct { | 379 | const struct { |
376 | const iBlock *ttf; | 380 | const iFontFile *fontFile; |
377 | int size; | 381 | int size; /* pixels */ |
378 | float scaling; | 382 | // float scaling; |
379 | enum iFontSize sizeId; | 383 | enum iFontSize sizeId; |
380 | /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ | 384 | /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ |
381 | /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ | 385 | /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ |
@@ -433,8 +437,34 @@ static void initFonts_Text_(iText *d) { | |||
433 | DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), | 437 | DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), |
434 | // DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), | 438 | // DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), |
435 | }; | 439 | }; |
440 | #endif | ||
441 | /* The `fonts` array has precomputed scaling factors and other parameters in all sizes | ||
442 | and styles for each available font. Indices to `fonts` act as font runtime IDs. */ | ||
443 | /* First the mandatory fonts. */ | ||
444 | d->overrideFontId = -1; | ||
445 | resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */ | ||
446 | iAssert(auxiliary_FontId == documentHeading_FontId + maxVariants_Fonts); | ||
447 | setupFontVariants_Text_(d, findSpec_Fonts("default"), default_FontId); | ||
448 | setupFontVariants_Text_(d, findSpec_Fonts("iosevka"), monospace_FontId); | ||
449 | setupFontVariants_Text_(d, findSpec_Fonts("default"), documentBody_FontId); | ||
450 | setupFontVariants_Text_(d, findSpec_Fonts("default"), documentHeading_FontId); | ||
451 | /* Check if there are auxiliary fonts available and set those up, too. */ | ||
452 | iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) { | ||
453 | const iFontSpec *spec = s.ptr; | ||
454 | if (spec->flags & auxiliary_FontSpecFlag) { | ||
455 | const int fontId = size_Array(&d->fonts); | ||
456 | resize_Array(&d->fonts, fontId + maxVariants_Fonts); | ||
457 | setupFontVariants_Text_(d, spec, fontId); | ||
458 | } | ||
459 | } | ||
460 | /* test */ { | ||
461 | const iFont *h = font_Text_(preformatted_FontId); // FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize)); | ||
462 | printf("{%s} %d sz:%d st:%d\n", cstr_String(&h->fontSpec->name), h->height, sizeId_Text_(h), | ||
463 | styleId_Text_(h)); | ||
464 | } | ||
465 | #if 0 | ||
436 | iForIndices(i, fontData) { | 466 | iForIndices(i, fontData) { |
437 | iFont *font = &d->fonts[i]; | 467 | iFont *font = font_Text_(i); |
438 | init_Font(font, | 468 | init_Font(font, |
439 | fontData[i].ttf, | 469 | fontData[i].ttf, |
440 | fontData[i].size, | 470 | fontData[i].size, |
@@ -442,13 +472,15 @@ static void initFonts_Text_(iText *d) { | |||
442 | fontData[i].sizeId, | 472 | fontData[i].sizeId, |
443 | fontData[i].ttf == &fontIosevkaTermExtended_Embedded); | 473 | fontData[i].ttf == &fontIosevkaTermExtended_Embedded); |
444 | } | 474 | } |
475 | #endif | ||
445 | gap_Text = iRound(gap_UI * d->contentFontSize); | 476 | gap_Text = iRound(gap_UI * d->contentFontSize); |
446 | } | 477 | } |
447 | 478 | ||
448 | static void deinitFonts_Text_(iText *d) { | 479 | static void deinitFonts_Text_(iText *d) { |
449 | iForIndices(i, d->fonts) { | 480 | iForEach(Array, i, &d->fonts) { |
450 | deinit_Font(&d->fonts[i]); | 481 | deinit_Font(i.value); |
451 | } | 482 | } |
483 | clear_Array(&d->fonts); | ||
452 | } | 484 | } |
453 | 485 | ||
454 | static int maxGlyphHeight_Text_(const iText *d) { | 486 | static int maxGlyphHeight_Text_(const iText *d) { |
@@ -490,6 +522,7 @@ static void deinitCache_Text_(iText *d) { | |||
490 | SDL_DestroyTexture(d->cache); | 522 | SDL_DestroyTexture(d->cache); |
491 | } | 523 | } |
492 | 524 | ||
525 | #if 0 | ||
493 | void loadUserFonts_Text(void) { | 526 | void loadUserFonts_Text(void) { |
494 | if (userFont_) { | 527 | if (userFont_) { |
495 | delete_Block(userFont_); | 528 | delete_Block(userFont_); |
@@ -508,9 +541,12 @@ void loadUserFonts_Text(void) { | |||
508 | iRelease(f); | 541 | iRelease(f); |
509 | } | 542 | } |
510 | } | 543 | } |
544 | #endif | ||
511 | 545 | ||
512 | void init_Text(iText *d, SDL_Renderer *render) { | 546 | void init_Text(iText *d, SDL_Renderer *render) { |
513 | loadUserFonts_Text(); | 547 | iText *oldActive = activeText_; |
548 | activeText_ = d; | ||
549 | init_Array(&d->fonts, sizeof(iFont)); | ||
514 | d->contentFont = nunito_TextFont; | 550 | d->contentFont = nunito_TextFont; |
515 | d->headingFont = nunito_TextFont; | 551 | d->headingFont = nunito_TextFont; |
516 | d->contentFontSize = contentScale_Text_; | 552 | d->contentFontSize = contentScale_Text_; |
@@ -526,6 +562,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
526 | } | 562 | } |
527 | initCache_Text_(d); | 563 | initCache_Text_(d); |
528 | initFonts_Text_(d); | 564 | initFonts_Text_(d); |
565 | activeText_ = oldActive; | ||
529 | } | 566 | } |
530 | 567 | ||
531 | void deinit_Text(iText *d) { | 568 | void deinit_Text(iText *d) { |
@@ -534,6 +571,7 @@ void deinit_Text(iText *d) { | |||
534 | deinitCache_Text_(d); | 571 | deinitCache_Text_(d); |
535 | d->render = NULL; | 572 | d->render = NULL; |
536 | iRelease(d->ansiEscape); | 573 | iRelease(d->ansiEscape); |
574 | deinit_Array(&d->fonts); | ||
537 | } | 575 | } |
538 | 576 | ||
539 | void setCurrent_Text(iText *d) { | 577 | void setCurrent_Text(iText *d) { |
@@ -569,8 +607,8 @@ void setContentFontSize_Text(iText *d, float fontSizeFactor) { | |||
569 | 607 | ||
570 | static void resetCache_Text_(iText *d) { | 608 | static void resetCache_Text_(iText *d) { |
571 | deinitCache_Text_(d); | 609 | deinitCache_Text_(d); |
572 | for (int i = 0; i < max_FontId; i++) { | 610 | iForEach(Array, i, &d->fonts) { |
573 | clearGlyphs_Font_(&d->fonts[i]); | 611 | clearGlyphs_Font_(i.value); |
574 | } | 612 | } |
575 | initCache_Text_(d); | 613 | initCache_Text_(d); |
576 | } | 614 | } |
@@ -582,14 +620,10 @@ void resetFonts_Text(iText *d) { | |||
582 | initFonts_Text_(d); | 620 | initFonts_Text_(d); |
583 | } | 621 | } |
584 | 622 | ||
585 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
586 | return &activeText_->fonts[id & mask_FontId]; | ||
587 | } | ||
588 | |||
589 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { | 623 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { |
590 | int w, h; | 624 | int w, h; |
591 | uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( | 625 | uint8_t *bmp = rasterizeGlyph_FontFile(d->fontFile, d->xScale, d->yScale, xShift, glyphIndex, |
592 | &d->font, d->xScale, d->yScale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); | 626 | &w, &h); |
593 | SDL_Surface *surface8 = | 627 | SDL_Surface *surface8 = |
594 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); | 628 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); |
595 | SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); | 629 | SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); |
@@ -636,8 +670,8 @@ static iInt2 assignCachePos_Text_(iText *d, iInt2 size) { | |||
636 | static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | 670 | static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { |
637 | iRect *glRect = &glyph->rect[hoff]; | 671 | iRect *glRect = &glyph->rect[hoff]; |
638 | int x0, y0, x1, y1; | 672 | int x0, y0, x1, y1; |
639 | stbtt_GetGlyphBitmapBoxSubpixel( | 673 | measureGlyph_FontFile(d->fontFile, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, |
640 | &d->font, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1); | 674 | &x0, &y0, &x1, &y1); |
641 | glRect->size = init_I2(x1 - x0, y1 - y0); | 675 | glRect->size = init_I2(x1 - x0, y1 - y0); |
642 | /* Determine placement in the glyph cache texture, advancing in rows. */ | 676 | /* Determine placement in the glyph cache texture, advancing in rows. */ |
643 | glRect->pos = assignCachePos_Text_(activeText_, glRect->size); | 677 | glRect->pos = assignCachePos_Text_(activeText_, glRect->size); |
@@ -645,7 +679,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
645 | glyph->d[hoff].y += d->vertOffset; | 679 | glyph->d[hoff].y += d->vertOffset; |
646 | if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ | 680 | if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ |
647 | int adv; | 681 | int adv; |
648 | stbtt_GetGlyphHMetrics(&d->font, index_Glyph_(glyph), &adv, NULL); | 682 | stbtt_GetGlyphHMetrics(&d->fontFile->stbInfo, index_Glyph_(glyph), &adv, NULL); |
649 | glyph->advance = d->xScale * adv; | 683 | glyph->advance = d->xScale * adv; |
650 | } | 684 | } |
651 | } | 685 | } |
@@ -654,13 +688,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
654 | if (isVariationSelector_Char(ch)) { | 688 | if (isVariationSelector_Char(ch)) { |
655 | return d; | 689 | return d; |
656 | } | 690 | } |
657 | /* Smol Emoji overrides all other fonts. */ | 691 | const enum iFontStyle styleId = styleId_Text_(d); |
658 | if (ch != 0x20) { | 692 | const enum iFontSize sizeId = sizeId_Text_(d); |
659 | iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId); | 693 | iFont *overrideFont = NULL; |
660 | if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) { | 694 | if (ch != 0x20 && activeText_->overrideFontId >= 0) { |
661 | return smol; | 695 | /* Override font is checked first. */ |
696 | overrideFont = font_Text_(FONT_ID(activeText_->overrideFontId, styleId, sizeId)); | ||
697 | if (overrideFont != d && (*glyphIndex = glyphIndex_Font_(overrideFont, ch)) != 0) { | ||
698 | return overrideFont; | ||
662 | } | 699 | } |
663 | } | 700 | } |
701 | #if 0 | ||
702 | /* TODO: Put arrows in Smol Emoji. */ | ||
664 | /* Manual exceptions. */ { | 703 | /* Manual exceptions. */ { |
665 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { | 704 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { |
666 | d = font_Text_(iosevka_FontId + d->sizeId); | 705 | d = font_Text_(iosevka_FontId + d->sizeId); |
@@ -668,9 +707,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
668 | return d; | 707 | return d; |
669 | } | 708 | } |
670 | } | 709 | } |
710 | #endif | ||
711 | /* The font's own version of the glyph. */ | ||
671 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { | 712 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { |
672 | return d; | 713 | return d; |
673 | } | 714 | } |
715 | /* As a fallback, check all other available fonts of this size. */ | ||
716 | for (iFont *font = font_Text_(FONT_ID(0, styleId, sizeId)); | ||
717 | font < (iFont *) end_Array(&activeText_->fonts); | ||
718 | font += maxVariants_Fonts) { | ||
719 | if (font == d || font == overrideFont) { | ||
720 | continue; /* already checked this one */ | ||
721 | } | ||
722 | if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) { | ||
723 | return font; | ||
724 | } | ||
725 | } | ||
726 | #if 0 | ||
674 | const int fallbacks[] = { | 727 | const int fallbacks[] = { |
675 | notoEmoji_FontId, | 728 | notoEmoji_FontId, |
676 | symbols2_FontId, | 729 | symbols2_FontId, |
@@ -732,6 +785,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
732 | if (d != font) { | 785 | if (d != font) { |
733 | *glyphIndex = glyphIndex_Font_(font, ch); | 786 | *glyphIndex = glyphIndex_Font_(font, ch); |
734 | } | 787 | } |
788 | #endif // 0 | ||
735 | if (!*glyphIndex) { | 789 | if (!*glyphIndex) { |
736 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 790 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); |
737 | } | 791 | } |
@@ -739,8 +793,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
739 | } | 793 | } |
740 | 794 | ||
741 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | 795 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { |
796 | iAssert(d->table); | ||
742 | iGlyph* glyph = NULL; | 797 | iGlyph* glyph = NULL; |
743 | void * node = value_Hash(&d->glyphs, glyphIndex); | 798 | void * node = value_Hash(&d->table->glyphs, glyphIndex); |
744 | if (node) { | 799 | if (node) { |
745 | glyph = node; | 800 | glyph = node; |
746 | } | 801 | } |
@@ -758,7 +813,7 @@ static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | |||
758 | and updates the glyph metrics. */ | 813 | and updates the glyph metrics. */ |
759 | allocate_Font_(d, glyph, 0); | 814 | allocate_Font_(d, glyph, 0); |
760 | allocate_Font_(d, glyph, 1); | 815 | allocate_Font_(d, glyph, 1); |
761 | insert_Hash(&d->glyphs, &glyph->node); | 816 | insert_Hash(&d->table->glyphs, &glyph->node); |
762 | } | 817 | } |
763 | return glyph; | 818 | return glyph; |
764 | } | 819 | } |
@@ -866,10 +921,6 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i | |||
866 | run->logical.start = endAt; | 921 | run->logical.start = endAt; |
867 | } | 922 | } |
868 | 923 | ||
869 | static enum iFontId fontId_Text_(const iFont *font) { | ||
870 | return (enum iFontId) (font - activeText_->fonts); | ||
871 | } | ||
872 | |||
873 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { | 924 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { |
874 | iAssert(isEmpty_Array(&d->runs)); | 925 | iAssert(isEmpty_Array(&d->runs)); |
875 | size_t length = 0; | 926 | size_t length = 0; |
@@ -997,14 +1048,14 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
997 | continue; | 1048 | continue; |
998 | } | 1049 | } |
999 | if (ch == 0x20) { | 1050 | if (ch == 0x20) { |
1000 | if (run.font->family == emojiAndSymbols_TextFont) { | 1051 | if (run.font->fontSpec->flags & auxiliary_FontSpecFlag) { |
1001 | finishRun_AttributedText_(d, &run, pos); | 1052 | finishRun_AttributedText_(d, &run, pos); |
1002 | run.font = d->font; /* never use space from the symbols font, it's too wide */ | 1053 | run.font = d->font; /* never use space from the symbols font, it's too wide */ |
1003 | } | 1054 | } |
1004 | continue; | 1055 | continue; |
1005 | } | 1056 | } |
1006 | iFont *currentFont = d->font; | 1057 | iFont *currentFont = d->font; |
1007 | if (run.font->family == arabic_TextFont && isPunct_Char(ch)) { | 1058 | if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) { |
1008 | currentFont = run.font; /* remain as Arabic for whitespace */ | 1059 | currentFont = run.font; /* remain as Arabic for whitespace */ |
1009 | } | 1060 | } |
1010 | const iGlyph *glyph = glyph_Font_(currentFont, ch); | 1061 | const iGlyph *glyph = glyph_Font_(currentFont, ch); |
@@ -1283,7 +1334,7 @@ static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int | |||
1283 | 1334 | ||
1284 | float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { | 1335 | float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { |
1285 | #if defined (LAGRANGE_ENABLE_KERNING) | 1336 | #if defined (LAGRANGE_ENABLE_KERNING) |
1286 | if (!enableKerning_Text || d->family != nunito_TextFont) { | 1337 | if (!enableKerning_Text || ~d->fontSpec->flags & fixNunitoKerning_FontSpecFlag) { |
1287 | return 0.0f; | 1338 | return 0.0f; |
1288 | } | 1339 | } |
1289 | if (glyph1 && glyph2) { | 1340 | if (glyph1 && glyph2) { |
@@ -1335,7 +1386,7 @@ static void deinit_GlyphBuffer_(iGlyphBuffer *d) { | |||
1335 | 1386 | ||
1336 | static void shape_GlyphBuffer_(iGlyphBuffer *d) { | 1387 | static void shape_GlyphBuffer_(iGlyphBuffer *d) { |
1337 | if (!d->glyphInfo) { | 1388 | if (!d->glyphInfo) { |
1338 | hb_shape(d->font->hbFont, d->hb, NULL, 0); | 1389 | hb_shape(d->font->fontFile->hbFont, d->hb, NULL, 0); |
1339 | d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); | 1390 | d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); |
1340 | d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); | 1391 | d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); |
1341 | } | 1392 | } |
@@ -1389,7 +1440,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1389 | float xCursor = 0.0f; | 1440 | float xCursor = 0.0f; |
1390 | float yCursor = 0.0f; | 1441 | float yCursor = 0.0f; |
1391 | float xCursorMax = 0.0f; | 1442 | float xCursorMax = 0.0f; |
1392 | const iBool isMonospaced = d->isMonospaced; | 1443 | const iBool isMonospaced = isMonospaced_Font(d); |
1393 | iWrapText *wrap = args->wrap; | 1444 | iWrapText *wrap = args->wrap; |
1394 | iAssert(args->text.end >= args->text.start); | 1445 | iAssert(args->text.end >= args->text.start); |
1395 | /* Split the text into a number of attributed runs that specify exactly which | 1446 | /* Split the text into a number of attributed runs that specify exactly which |