summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-21 13:21:11 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-21 13:21:11 +0300
commit17d7cc86578eb0a9e97e3a51bad0e10a9da1482e (patch)
tree9f95e7fd9f91bea0590959d656f4a2e98f0d86de /src/ui
parent648ddbf3d118d28a20027b8c5dd074494c53b90a (diff)
Experimenting with text art
Convert glyph bitmaps to Unicode block characters
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/text.c110
-rw-r--r--src/ui/text.h5
2 files changed, 113 insertions, 2 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 0bf9b294..60e26692 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -11,6 +11,7 @@
11#include <the_Foundation/file.h> 11#include <the_Foundation/file.h>
12#include <the_Foundation/hash.h> 12#include <the_Foundation/hash.h>
13#include <the_Foundation/math.h> 13#include <the_Foundation/math.h>
14#include <the_Foundation/stringlist.h>
14#include <the_Foundation/regexp.h> 15#include <the_Foundation/regexp.h>
15#include <the_Foundation/path.h> 16#include <the_Foundation/path.h>
16#include <the_Foundation/vec2.h> 17#include <the_Foundation/vec2.h>
@@ -287,8 +288,8 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) {
287 /* Rasterize the glyph using stbtt. */ { 288 /* Rasterize the glyph using stbtt. */ {
288 surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f); 289 surface = rasterizeGlyph_Font_(d, ch, hoff * 0.5f);
289 if (hoff == 0) { 290 if (hoff == 0) {
290 int adv, lsb; 291 int adv;
291 stbtt_GetCodepointHMetrics(&d->font, ch, &adv, &lsb); 292 stbtt_GetCodepointHMetrics(&d->font, ch, &adv, NULL);
292 glyph->advance = d->scale * adv; 293 glyph->advance = d->scale * adv;
293 } 294 }
294 stbtt_GetCodepointBitmapBoxSubpixel(&d->font, 295 stbtt_GetCodepointBitmapBoxSubpixel(&d->font,
@@ -658,6 +659,111 @@ SDL_Texture *glyphCache_Text(void) {
658 return text_.cache; 659 return text_.cache;
659} 660}
660 661
662static void freeBitmap_(void *ptr) {
663 stbtt_FreeBitmap(ptr, NULL);
664}
665
666iString *renderBlockChars_Text(const iBlock *fontData, int height, enum iTextBlockMode mode,
667 const iString *text) {
668 iBeginCollect();
669 stbtt_fontinfo font;
670 iZap(font);
671 stbtt_InitFont(&font, constData_Block(fontData), 0);
672 int ascent;
673 stbtt_GetFontVMetrics(&font, &ascent, NULL, NULL);
674 iDeclareType(CharBuf);
675 struct Impl_CharBuf {
676 uint8_t *pixels;
677 iInt2 size;
678 int dy;
679 int advance;
680 };
681 iArray * chars = collectNew_Array(sizeof(iCharBuf));
682 int pxRatio = (mode == quadrants_TextBlockMode ? 2 : 1);
683 int pxHeight = height * pxRatio;
684 const float scale = stbtt_ScaleForPixelHeight(&font, pxHeight);
685 const float xScale = scale * 2; /* character aspect ratio */
686 const int baseline = ascent * scale;
687 int width = 0;
688 size_t strRemain = length_String(text);
689 iConstForEach(String, i, text) {
690 if (!strRemain) break;
691 if (i.value == variationSelectorEmoji_Char) {
692 strRemain--;
693 continue;
694 }
695 iCharBuf buf;
696 buf.pixels = stbtt_GetCodepointBitmap(
697 &font, xScale, scale, i.value, &buf.size.x, &buf.size.y, 0, &buf.dy);
698 stbtt_GetCodepointHMetrics(&font, i.value, &buf.advance, NULL);
699 buf.advance *= xScale;
700 if (!isSpace_Char(i.value)) {
701 if (mode == quadrants_TextBlockMode) {
702 buf.advance = (buf.size.x - 1) / 2 * 2 + 2;
703 }
704 else {
705 buf.advance = buf.size.x + 1;
706 }
707 }
708 pushBack_Array(chars, &buf);
709 collect_Garbage(buf.pixels, freeBitmap_);
710 width += buf.advance;
711 strRemain--;
712 }
713 const size_t len = (mode == quadrants_TextBlockMode ? height * ((width + 1) / 2 + 1)
714 : (height * (width + 1)));
715 iChar *outBuf = iCollectMem(malloc(sizeof(iChar) * len));
716 for (size_t i = 0; i < len; ++i) {
717 outBuf[i] = 0x20;
718 }
719 iChar *outPos = outBuf;
720 for (int y = 0; y < pxHeight; y += pxRatio) {
721 const iCharBuf *ch = constData_Array(chars);
722 int lx = 0;
723 for (int x = 0; x < width; x += pxRatio, lx += pxRatio) {
724 if (lx >= ch->advance) {
725 ch++;
726 lx = 0;
727 }
728 const int ly = y - baseline - ch->dy;
729 if (mode == quadrants_TextBlockMode) {
730 #define checkPixel_(offx, offy) \
731 (lx + offx < ch->size.x && ly + offy < ch->size.y && ly + offy >= 0 ? \
732 ch->pixels[(lx + offx) + (ly + offy) * ch->size.x] > 155 \
733 : iFalse)
734 const int mask = (checkPixel_(0, 0) ? 1 : 0) |
735 (checkPixel_(1, 0) ? 2 : 0) |
736 (checkPixel_(0, 1) ? 4 : 0) |
737 (checkPixel_(1, 1) ? 8 : 0);
738 #undef checkPixel_
739 static const iChar blocks[16] = { 0x0020, 0x2598, 0x259D, 0x2580, 0x2596, 0x258C,
740 0x259E, 0x259B, 0x2597, 0x259A, 0x2590, 0x259C,
741 0x2584, 0x2599, 0x259F, 0x2588 };
742 *outPos++ = blocks[mask];
743 }
744 else {
745 static const iChar shades[5] = { 0x0020, 0x2591, 0x2592, 0x2593, 0x2588 };
746 *outPos++ = shades[lx < ch->size.x && ly < ch->size.y && ly >= 0 ?
747 ch->pixels[lx + ly * ch->size.x] * 5 / 256 : 0];
748 }
749 }
750 *outPos++ = '\n';
751 }
752 /* We could compose the lines separately, but we'd still need to convert them to Strings
753 individually to trim them. */
754 iStringList *lines = split_String(collect_String(newUnicodeN_String(outBuf, len)), "\n");
755 while (!isEmpty_StringList(lines) &&
756 isEmpty_String(collect_String(trimmed_String(at_StringList(lines, 0))))) {
757 popFront_StringList(lines);
758 }
759 while (!isEmpty_StringList(lines) && isEmpty_String(collect_String(trimmed_String(
760 at_StringList(lines, size_StringList(lines) - 1))))) {
761 popBack_StringList(lines);
762 }
763 iEndCollect();
764 return joinCStr_StringList(iClob(lines), "\n");
765}
766
661/*-----------------------------------------------------------------------------------------------*/ 767/*-----------------------------------------------------------------------------------------------*/
662 768
663iDefineTypeConstructionArgs(TextBuf, (int font, const char *text), font, text) 769iDefineTypeConstructionArgs(TextBuf, (int font, const char *text), font, text)
diff --git a/src/ui/text.h b/src/ui/text.h
index 2d49a1a6..239fae51 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -90,6 +90,11 @@ void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text);
90 90
91SDL_Texture * glyphCache_Text (void); 91SDL_Texture * glyphCache_Text (void);
92 92
93enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode };
94
95iString * renderBlockChars_Text (const iBlock *fontData, int height, enum iTextBlockMode,
96 const iString *text);
97
93/*-----------------------------------------------------------------------------------------------*/ 98/*-----------------------------------------------------------------------------------------------*/
94 99
95iDeclareType(TextBuf) 100iDeclareType(TextBuf)