From ed9cb4ca6dcd65c3344d80b84cb5537564263b04 Mon Sep 17 00:00:00 2001 From: Misa Date: Fri, 6 Aug 2021 20:55:09 -0700 Subject: [PATCH] Add graphics wrapping functions This will wrap text on-the-fly, since I will be introducing text that needs to be wrapped whose length we can't know in advance. (Or we can, but, that'd be stupid.) I took the algorithm from Dav999's localization branch, but it's not like it's a complicated algorithm in the first place. Plus I think it actually handles words that get too long to fit on a single line better than his localization branch. The only difference is that I removed all the STL, and made it more memory efficient (unlike his localization branch, it does not copy the entire string to make a version with newline separator characters). --- desktop_version/src/Graphics.cpp | 113 +++++++++++++++++++++++++++++++ desktop_version/src/Graphics.h | 6 ++ 2 files changed, 119 insertions(+) diff --git a/desktop_version/src/Graphics.cpp b/desktop_version/src/Graphics.cpp index 02c38b8d..63927fe1 100644 --- a/desktop_version/src/Graphics.cpp +++ b/desktop_version/src/Graphics.cpp @@ -515,6 +515,119 @@ void Graphics::PrintAlpha( int _x, int _y, std::string _s, int r, int g, int b, } } +bool Graphics::next_wrap( + size_t* start, + size_t* len, + const char* str, + const int maxwidth +) { + /* This function is UTF-8 aware. But start/len still are bytes. */ + size_t idx = 0; + size_t lenfromlastspace = 0; + size_t lastspace = 0; + int linewidth = 0; + *len = 0; + + if (str[idx] == '\0') + { + return false; + } + + while (true) + { + /* FIXME: This only checks one byte, not multiple! */ + if ((str[idx] & 0xC0) == 0x80) + { + /* Skip continuation byte. */ + goto next; + } + + linewidth += bfontlen(str[idx]); + + switch (str[idx]) + { + case ' ': + lenfromlastspace = idx; + lastspace = *start; + break; + case '\n': + *start += 1; + VVV_fallthrough; + case '\0': + return true; + } + + if (linewidth > maxwidth) + { + if (lenfromlastspace != 0) + { + *len = lenfromlastspace; + *start = lastspace + 1; + } + return true; + } + +next: + idx += 1; + *start += 1; + *len += 1; + } +} + +bool Graphics::next_wrap_s( + char buffer[], + const size_t buffer_size, + size_t* start, + const char* str, + const int maxwidth +) { + size_t len = 0; + const size_t prev_start = *start; + + const bool retval = next_wrap(start, &len, &str[*start], maxwidth); + + if (retval) + { + /* Like next_split_s(), don't use SDL_strlcpy() here. */ + const size_t length = VVV_min(buffer_size - 1, len); + SDL_memcpy(buffer, &str[prev_start], length); + buffer[length] = '\0'; + } + + return retval; +} + +void Graphics::PrintWrap( + const int x, + int y, + const char* str, + const int r, + const int g, + const int b, + const bool cen, + const int linespacing, + const int maxwidth +) { + /* Screen width is 320 pixels. The shortest a char can be is 6 pixels wide. + * 320 / 6 is 54, rounded up. 4 bytes per char. */ + char buffer[54*4 + 1]; + size_t start = 0; + + while (next_wrap_s(buffer, sizeof(buffer), &start, str, maxwidth)) + { + Print(x, y, buffer, r, g, b, cen); + + if (flipmode) + { + y -= linespacing; + } + else + { + y += linespacing; + } + } +} + void Graphics::bigprint( int _x, int _y, std::string _s, int r, int g, int b, bool cen, int sc ) { diff --git a/desktop_version/src/Graphics.h b/desktop_version/src/Graphics.h index 29ebdb99..6e64e5d0 100644 --- a/desktop_version/src/Graphics.h +++ b/desktop_version/src/Graphics.h @@ -136,6 +136,12 @@ public: void PrintAlpha(int _x, int _y, std::string _s, int r, int g, int b, int a, bool cen = false); + bool next_wrap(size_t* start, size_t* len, const char* str, int maxwidth); + + bool next_wrap_s(char buffer[], size_t buffer_size, size_t* start, const char* str, int maxwidth); + + void PrintWrap(int x, int y, const char* str, int r, int g, int b, bool cen, int linespacing, int maxwidth); + void PrintOffAlpha(int _x, int _y, std::string _s, int r, int g, int b, int a, bool cen = false); void bprint(int x, int y, std::string t, int r, int g, int b, bool cen = false);