summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ui/text.c127
1 files changed, 82 insertions, 45 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index cf974366..218e7565 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -52,8 +52,16 @@ int gap_Text; /* cf. gap_UI in metrics.h */
52int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ 52int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */
53int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ 53int enableKerning_Text = iTrue; /* looking up kern pairs is slow */
54 54
55static iBool enableRaster_Text_ = iTrue;
56
57enum iGlyphFlag {
58 rasterized0_GlyphFlag = iBit(1), /* zero offset */
59 rasterized1_GlyphFlag = iBit(2), /* half-pixel offset */
60};
61
55struct Impl_Glyph { 62struct Impl_Glyph {
56 iHashNode node; 63 iHashNode node;
64 int flags;
57 uint32_t glyphIndex; 65 uint32_t glyphIndex;
58 const iFont *font; /* may come from symbols/emoji */ 66 const iFont *font; /* may come from symbols/emoji */
59 iRect rect[2]; /* zero and half pixel offset */ 67 iRect rect[2]; /* zero and half pixel offset */
@@ -63,6 +71,7 @@ struct Impl_Glyph {
63 71
64void init_Glyph(iGlyph *d, iChar ch) { 72void init_Glyph(iGlyph *d, iChar ch) {
65 d->node.key = ch; 73 d->node.key = ch;
74 d->flags = 0;
66 d->glyphIndex = 0; 75 d->glyphIndex = 0;
67 d->font = NULL; 76 d->font = NULL;
68 d->rect[0] = zero_Rect(); 77 d->rect[0] = zero_Rect();
@@ -74,10 +83,23 @@ void deinit_Glyph(iGlyph *d) {
74 iUnused(d); 83 iUnused(d);
75} 84}
76 85
77iChar codepoint_Glyph(const iGlyph *d) { 86static iChar codepoint_Glyph_(const iGlyph *d) {
78 return d->node.key; 87 return d->node.key;
79} 88}
80 89
90iLocalDef iBool isRasterized_Glyph_(const iGlyph *d, int hoff) {
91 return (d->flags & (rasterized0_GlyphFlag << hoff)) != 0;
92}
93
94iLocalDef iBool isFullyRasterized_Glyph_(const iGlyph *d) {
95 return (d->flags & (rasterized0_GlyphFlag | rasterized1_GlyphFlag)) ==
96 (rasterized0_GlyphFlag | rasterized1_GlyphFlag);
97}
98
99iLocalDef void setRasterized_Glyph_(iGlyph *d, int hoff) {
100 d->flags |= rasterized0_GlyphFlag << hoff;
101}
102
81iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) 103iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
82 104
83/*-----------------------------------------------------------------------------------------------*/ 105/*-----------------------------------------------------------------------------------------------*/
@@ -496,41 +518,41 @@ static iInt2 assignCachePos_Text_(iText *d, iInt2 size) {
496 return assigned; 518 return assigned;
497} 519}
498 520
499static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { 521static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
500 iText *txt = &text_;
501 SDL_Renderer *render = txt->render;
502 SDL_Texture *tex = NULL;
503 SDL_Surface *surface = NULL;
504 iRect *glRect = &glyph->rect[hoff]; 522 iRect *glRect = &glyph->rect[hoff];
505 /* Rasterize the glyph using stbtt. */ { 523 int x0, y0, x1, y1;
506 surface = rasterizeGlyph_Font_(d, glyph->glyphIndex, hoff * 0.5f); 524 stbtt_GetGlyphBitmapBoxSubpixel(
507 if (hoff == 0) { /* hoff==1 uses same `glyph` */ 525 &d->font, glyph->glyphIndex, d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1);
508 int adv; 526 glRect->size = init_I2(x1 - x0, y1 - y0);
509 const uint32_t gIndex = glyph->glyphIndex; 527 /* Determine placement in the glyph cache texture, advancing in rows. */
510 stbtt_GetGlyphHMetrics(&d->font, gIndex, &adv, NULL); 528 glRect->pos = assignCachePos_Text_(&text_, glRect->size);
511 glyph->advance = d->xScale * adv; 529 glyph->d[hoff] = init_I2(x0, y0);
512 } 530 glyph->d[hoff].y += d->vertOffset;
513 stbtt_GetGlyphBitmapBoxSubpixel(&d->font, 531 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */
514 glyph->glyphIndex, 532 int adv;
515 d->xScale, 533 const uint32_t gIndex = glyph->glyphIndex;
516 d->yScale, 534 stbtt_GetGlyphHMetrics(&d->font, gIndex, &adv, NULL);
517 hoff * 0.5f, 535 glyph->advance = d->xScale * adv;
518 0.0f,
519 &glyph->d[hoff].x,
520 &glyph->d[hoff].y,
521 NULL,
522 NULL);
523 glyph->d[hoff].y += d->vertOffset;
524 tex = SDL_CreateTextureFromSurface(render, surface);
525 SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_NONE);
526 glRect->size = init_I2(surface->w, surface->h);
527 } 536 }
537}
538
539static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
540 iText * txt = &text_;
541 SDL_Renderer *render = txt->render;
542 SDL_Texture * tex = NULL;
543 SDL_Surface * surface = NULL;
544 iRect * glRect = &glyph->rect[hoff];
545 /* Rasterize the glyph using stbtt. */
546 iAssert(!isRasterized_Glyph_(glyph, hoff));
547 surface = rasterizeGlyph_Font_(d, glyph->glyphIndex, hoff * 0.5f);
548 tex = SDL_CreateTextureFromSurface(render, surface);
549 iAssert(isEqual_I2(glRect->size, init_I2(surface->w, surface->h)));
528 if (tex) { 550 if (tex) {
529 /* Determine placement in the glyph cache texture, advancing in rows. */ 551 SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_NONE);
530 glRect->pos = assignCachePos_Text_(txt, glRect->size);
531 const SDL_Rect dstRect = sdlRect_(*glRect); 552 const SDL_Rect dstRect = sdlRect_(*glRect);
532 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); 553 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect);
533 SDL_DestroyTexture(tex); 554 SDL_DestroyTexture(tex);
555 setRasterized_Glyph_(glyph, hoff);
534 } 556 }
535 if (surface) { 557 if (surface) {
536 SDL_FreeSurface(surface); 558 SDL_FreeSurface(surface);
@@ -580,29 +602,42 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
580} 602}
581 603
582static const iGlyph *glyph_Font_(iFont *d, iChar ch) { 604static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
605 iGlyph * glyph;
583 uint32_t glyphIndex = 0; 606 uint32_t glyphIndex = 0;
584 /* The glyph may actually come from a different font; look up the right font. */ 607 /* The glyph may actually come from a different font; look up the right font. */
585 iFont *font = characterFont_Font_(d, ch, &glyphIndex); 608 iFont *font = characterFont_Font_(d, ch, &glyphIndex);
586 const void *node = value_Hash(&font->glyphs, ch); 609 void * node = value_Hash(&font->glyphs, ch);
587 if (node) { 610 if (node) {
588 return node; 611 glyph = node;
589 } 612 }
590 iGlyph *glyph = new_Glyph(ch); 613 else {
591 glyph->glyphIndex = glyphIndex; 614 /* If the cache is running out of space, clear it and we'll recache what's needed currently. */
592 glyph->font = font; 615 if (text_.cacheBottom > text_.cacheSize.y - maxGlyphHeight_Text_(&text_)) {
593 /* If the cache is running out of space, clear it and we'll recache what's needed currently. */
594 if (text_.cacheBottom > text_.cacheSize.y - maxGlyphHeight_Text_(&text_)) {
595#if !defined (NDEBUG) 616#if !defined (NDEBUG)
596 printf("[Text] glyph cache is full, clearing!\n"); fflush(stdout); 617 printf("[Text] glyph cache is full, clearing!\n"); fflush(stdout);
597#endif 618#endif
598 resetCache_Text_(&text_); 619 resetCache_Text_(&text_);
620 }
621 glyph = new_Glyph(ch);
622 glyph->glyphIndex = glyphIndex;
623 glyph->font = font;
624 /* New glyphs are always allocated at least. This reserves a position in the cache
625 and updates the glyph metrics. */
626 allocate_Font_(font, glyph, 0);
627 allocate_Font_(font, glyph, 1);
628 insert_Hash(&font->glyphs, &glyph->node);
629 }
630 if (enableRaster_Text_ && !isFullyRasterized_Glyph_(glyph)) {
631 SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render);
632 SDL_SetRenderTarget(text_.render, text_.cache);
633 if (!isRasterized_Glyph_(glyph, 0)) {
634 cache_Font_(font, glyph, 0);
635 }
636 if (!isRasterized_Glyph_(glyph, 1)) {
637 cache_Font_(font, glyph, 1); /* half-pixel offset */
638 }
639 SDL_SetRenderTarget(text_.render, oldTarget);
599 } 640 }
600 SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render);
601 SDL_SetRenderTarget(text_.render, text_.cache);
602 cache_Font_(font, glyph, 0);
603 cache_Font_(font, glyph, 1); /* half-pixel offset */
604 SDL_SetRenderTarget(text_.render, oldTarget);
605 insert_Hash(&font->glyphs, &glyph->node);
606 return glyph; 641 return glyph;
607} 642}
608 643
@@ -693,6 +728,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
693 if (isMonospaced) { 728 if (isMonospaced) {
694 monoAdvance = glyph_Font_(d, 'M')->advance; 729 monoAdvance = glyph_Font_(d, 'M')->advance;
695 } 730 }
731 /* Global flag that allows glyph rasterization. */
732 enableRaster_Text_ = !isMeasuring_(mode);
696 for (const char *chPos = args->text.start; chPos != args->text.end; ) { 733 for (const char *chPos = args->text.start; chPos != args->text.end; ) {
697 iAssert(chPos < args->text.end); 734 iAssert(chPos < args->text.end);
698 const char *currentPos = chPos; 735 const char *currentPos = chPos;