1
0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-06-02 19:13:31 +02:00

Move wordwrapping functions and len to Font.cpp/font:: namespace

The following functions were moved directly:
- next_wrap
- next_wrap_s
- string_wordwrap
- string_wordwrap_balanced
- string_unwordwrap

These ones will probably still need get a flags argument, except for
string_unwordwrap (since they need to know what font we're talking
about.

The implementation of graphics.len has also been moved to Font.cpp,
but graphics.len still exists for now and is deprecated.
This commit is contained in:
Dav999-v 2023-01-06 19:17:50 +01:00 committed by Misa Elizabeth Kai
parent 1d8494db8d
commit 159c70dade
9 changed files with 262 additions and 251 deletions

View File

@ -11,6 +11,7 @@
#include "DeferCallbacks.h"
#include "Entity.h"
#include "Enums.h"
#include "Font.h"
#include "Game.h"
#include "Graphics.h"
#include "GraphicsUtil.h"
@ -1154,7 +1155,7 @@ void editorrender(void)
}
short lines;
message = graphics.string_wordwrap(message, 312, &lines);
message = font::string_wordwrap(message, 312, &lines);
short textheight = 8*lines;
graphics.fill_rect(0,238-textheight,320,240, graphics.getRGB(32,32,32));
@ -1256,7 +1257,7 @@ void editorrender(void)
else if (ed.textmod)
{
short lines;
std::string wrapped = graphics.string_wordwrap(ed.textdesc, 312, &lines);
std::string wrapped = font::string_wordwrap(ed.textdesc, 312, &lines);
short textheight = 8*lines+8;
graphics.fill_rect(0, 238-textheight, 320, 240, graphics.getRGB(32, 32, 32));
@ -1590,7 +1591,7 @@ void editorrender(void)
if(ed.notedelay>0 || ed.oldnotedelay>0)
{
short lines;
std::string wrapped = graphics.string_wordwrap(ed.note, 304, &lines);
std::string wrapped = font::string_wordwrap(ed.note, 304, &lines);
short textheight = 8+(lines-1)*10;
short banner_y = 120 - textheight/2 - 5;

View File

@ -107,6 +107,18 @@ static GlyphInfo* find_glyphinfo(const Font* f, const uint32_t codepoint)
return NULL;
}
int get_advance(const Font* f, const uint32_t codepoint)
{
// Get the width of a single character in a font
GlyphInfo* glyph = find_glyphinfo(f, codepoint);
if (glyph == NULL)
{
return f->glyph_w;
}
return glyph->advance;
}
static bool decode_xml_range(tinyxml2::XMLElement* elem, unsigned* start, unsigned* end)
{
// We do support hexadecimal start/end like "0x10FFFF"
@ -319,16 +331,219 @@ void destroy(void)
}
}
int get_advance(const Font* f, const uint32_t codepoint)
{
/* Get the width of a single character in a font */
GlyphInfo* glyph = find_glyphinfo(f, codepoint);
if (glyph == NULL)
static bool 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 f->glyph_w;
return false;
}
return glyph->advance;
while (true)
{
/* FIXME: This only checks one byte, not multiple! */
if ((str[idx] & 0xC0) == 0x80)
{
/* Skip continuation byte. */
goto next;
}
linewidth += get_advance(&font::temp_bfont, str[idx]);
switch (str[idx])
{
case ' ':
if (loc::get_langmeta()->autowordwrap)
{
lenfromlastspace = idx;
lastspace = *start;
}
break;
case '\n':
case '|':
*start += 1;
SDL_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;
}
}
static bool 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 = SDL_min(buffer_size - 1, len);
SDL_memcpy(buffer, &str[prev_start], length);
buffer[length] = '\0';
}
return retval;
}
std::string string_wordwrap(const std::string& s, int maxwidth, short *lines /*= NULL*/)
{
/* Return a string wordwrapped to a maximum limit by adding newlines.
* CJK will need to have autowordwrap disabled and have manually inserted newlines. */
if (lines != NULL)
{
*lines = 1;
}
const char* orig = s.c_str();
std::string result;
size_t start = 0;
bool first = true;
while (true)
{
size_t len = 0;
const char* part = &orig[start];
const bool retval = next_wrap(&start, &len, part, maxwidth);
if (!retval)
{
return result;
}
if (first)
{
first = false;
}
else
{
result.push_back('\n');
if (lines != NULL)
{
(*lines)++;
}
}
result.append(part, len);
}
}
std::string string_wordwrap_balanced(const std::string& s, int maxwidth)
{
/* Return a string wordwrapped to a limit of maxwidth by adding newlines.
* Try to fill the lines as far as possible, and return result where lines are most filled.
* Goal is to have all lines in textboxes be about as long and to avoid wrapping just one word to a new line.
* CJK will need to have autowordwrap disabled and have manually inserted newlines. */
if (!loc::get_langmeta()->autowordwrap)
{
return s;
}
short lines;
string_wordwrap(s, maxwidth, &lines);
int bestwidth = maxwidth;
if (lines > 1)
{
for (int curlimit = maxwidth; curlimit > 1; curlimit -= 8)
{
short try_lines;
string_wordwrap(s, curlimit, &try_lines);
if (try_lines > lines)
{
bestwidth = curlimit + 8;
break;
}
}
}
return string_wordwrap(s, bestwidth);
}
std::string string_unwordwrap(const std::string& s)
{
/* Takes a string wordwrapped by newlines, and turns it into a single line, undoing the wrapping.
* Also trims any leading/trailing whitespace and collapses multiple spaces into one (to undo manual centering)
* Only applied to English, so langmeta.autowordwrap isn't used here (it'd break looking up strings) */
std::string result;
std::back_insert_iterator<std::string> inserter = std::back_inserter(result);
std::string::const_iterator iter = s.begin();
bool latest_was_space = true; // last character was a space (or the beginning, don't want leading whitespace)
int consecutive_newlines = 0; // number of newlines currently encountered in a row (multiple newlines should stay!)
while (iter != s.end())
{
uint32_t ch = utf8::unchecked::next(iter);
if (ch == '\n')
{
if (consecutive_newlines == 0)
{
ch = ' ';
}
else if (consecutive_newlines == 1)
{
// The last character was already a newline, so change it back from the space we thought it should have become.
result[result.size()-1] = '\n';
}
consecutive_newlines++;
}
else
{
consecutive_newlines = 0;
}
if (ch != ' ' || !latest_was_space)
{
utf8::unchecked::append(ch, inserter);
}
latest_was_space = (ch == ' ' || ch == '\n');
}
// We could have one trailing space
if (!result.empty() && result[result.size()-1] == ' ')
{
result.erase(result.end()-1);
}
return result;
}
static int print_char(
@ -390,6 +605,21 @@ static PrintFlags decode_print_flags(uint32_t flags)
}
#undef FLAG_PART
int len(const uint32_t flags, const std::string& t)
{
PrintFlags pf = decode_print_flags(flags);
// TODO flags!
int text_len = 0;
std::string::const_iterator iter = t.begin();
while (iter != t.end())
{
int cur = utf8::unchecked::next(iter);
text_len += get_advance(&font::temp_bfont, cur);
}
return text_len;
}
void print(
const uint32_t flags,
int x,
@ -493,7 +723,7 @@ int print_wrap(
{
// Correct for the height of the resulting print.
size_t len = 0;
while (graphics.next_wrap(&start, &len, &str[start], maxwidth))
while (next_wrap(&start, &len, &str[start], maxwidth))
{
y += linespacing;
}
@ -501,7 +731,7 @@ int print_wrap(
start = 0;
}
while (graphics.next_wrap_s(buffer, sizeof(buffer), &start, str, maxwidth))
while (next_wrap_s(buffer, sizeof(buffer), &start, str, maxwidth))
{
print(flags, x, y, buffer, r, g, b);

View File

@ -104,7 +104,11 @@ void load_custom(void);
void unload_custom(void);
void destroy(void);
int get_advance(const Font* f, uint32_t codepoint); // TODO de-api
std::string string_wordwrap(const std::string& s, int maxwidth, short *lines = NULL);
std::string string_wordwrap_balanced(const std::string& s, int maxwidth);
std::string string_unwordwrap(const std::string& s);
int len(uint32_t flags, const std::string& t);
void print(
uint32_t flags,

View File

@ -672,7 +672,7 @@ void Game::crewmate_textbox(const int r, const int g, const int b)
/* This is a special case for wrapping, we MUST have two lines.
* So just make sure it can't fit in one line. */
const char* text = loc::gettext("You have rescued a crew member!");
std::string wrapped = graphics.string_wordwrap_balanced(text, graphics.len(text)-1);
std::string wrapped = font::string_wordwrap_balanced(text, graphics.len(text)-1);
size_t startline = 0;
size_t newline;

View File

@ -354,92 +354,6 @@ void Graphics::PrintAlpha( int x, int y, const std::string& text, int r, int g,
font::print(PR_ALPHA(a), x, y, text, r, g, 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 += font::get_advance(&font::temp_bfont, str[idx]);
switch (str[idx])
{
case ' ':
if (loc::get_langmeta()->autowordwrap)
{
lenfromlastspace = idx;
lastspace = *start;
}
break;
case '\n':
case '|':
*start += 1;
SDL_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 = SDL_min(buffer_size - 1, len);
SDL_memcpy(buffer, &str[prev_start], length);
buffer[length] = '\0';
}
return retval;
}
int Graphics::PrintWrap(
const int x,
int y,
@ -481,142 +395,8 @@ void Graphics::bigbprint(int x, int y, const std::string& text, int r, int g, in
int Graphics::len(const std::string& t)
{
int text_len = 0;
std::string::const_iterator iter = t.begin();
while (iter != t.end()) {
int cur = utf8::unchecked::next(iter);
text_len += font::get_advance(&font::temp_bfont, cur);
}
return text_len;
}
std::string Graphics::string_wordwrap(const std::string& s, int maxwidth, short *lines /*= NULL*/)
{
// Return a string wordwrapped to a maximum limit by adding newlines.
// CJK will need to have autowordwrap disabled and have manually inserted newlines.
if (lines != NULL)
{
*lines = 1;
}
const char* orig = s.c_str();
std::string result;
size_t start = 0;
bool first = true;
while (true)
{
size_t len = 0;
const char* part = &orig[start];
const bool retval = next_wrap(&start, &len, part, maxwidth);
if (!retval)
{
return result;
}
if (first)
{
first = false;
}
else
{
result.push_back('\n');
if (lines != NULL)
{
(*lines)++;
}
}
result.append(part, len);
}
}
std::string Graphics::string_wordwrap_balanced(const std::string& s, int maxwidth)
{
// Return a string wordwrapped to a limit of maxwidth by adding newlines.
// Try to fill the lines as far as possible, and return result where lines are most filled.
// Goal is to have all lines in textboxes be about as long and to avoid wrapping just one word to a new line.
// CJK will need to have autowordwrap disabled and have manually inserted newlines.
if (!loc::get_langmeta()->autowordwrap)
{
return s;
}
short lines;
string_wordwrap(s, maxwidth, &lines);
int bestwidth = maxwidth;
if (lines > 1)
{
for (int curlimit = maxwidth; curlimit > 1; curlimit -= 8)
{
short try_lines;
string_wordwrap(s, curlimit, &try_lines);
if (try_lines > lines)
{
bestwidth = curlimit + 8;
break;
}
}
}
return string_wordwrap(s, bestwidth);
}
std::string Graphics::string_unwordwrap(const std::string& s)
{
/* Takes a string wordwrapped by newlines, and turns it into a single line, undoing the wrapping.
* Also trims any leading/trailing whitespace and collapses multiple spaces into one (to undo manual centering)
* Only applied to English, so langmeta.autowordwrap isn't used here (it'd break looking up strings) */
std::string result;
std::back_insert_iterator<std::string> inserter = std::back_inserter(result);
std::string::const_iterator iter = s.begin();
bool latest_was_space = true; // last character was a space (or the beginning, don't want leading whitespace)
int consecutive_newlines = 0; // number of newlines currently encountered in a row (multiple newlines should stay!)
while (iter != s.end())
{
uint32_t ch = utf8::unchecked::next(iter);
if (ch == '\n')
{
if (consecutive_newlines == 0)
{
ch = ' ';
}
else if (consecutive_newlines == 1)
{
// The last character was already a newline, so change it back from the space we thought it should have become.
result[result.size()-1] = '\n';
}
consecutive_newlines++;
}
else
{
consecutive_newlines = 0;
}
if (ch != ' ' || !latest_was_space)
{
utf8::unchecked::append(ch, inserter);
}
latest_was_space = (ch == ' ' || ch == '\n');
}
// We could have one trailing space
if (!result.empty() && result[result.size()-1] == ' ')
{
result.erase(result.end()-1);
}
return result;
// DEPRECATED
return font::len(0, t);
}
void Graphics::bprint( int x, int y, const std::string& text, int r, int g, int b, bool cen /*= false*/ ) {
@ -2058,12 +1838,12 @@ void Graphics::drawtrophytext(void)
short lines;
if (top_text != NULL)
{
string_wordwrap(top_text, 304, &lines);
font::string_wordwrap(top_text, 304, &lines);
PrintWrap(-1, 11-(lines-1)*5, top_text, temp, temp2, temp3, true);
}
if (bottom_text != NULL)
{
string_wordwrap(bottom_text, 304, &lines);
font::string_wordwrap(bottom_text, 304, &lines);
PrintWrap(-1, 221-(lines-1)*5, bottom_text, temp, temp2, temp3, true);
}
}
@ -3337,7 +3117,7 @@ int Graphics::textboxwrap(int pad)
vlog_error("textboxwrap() has no first line!");
return 16;
}
std::string wrapped = string_wordwrap_balanced(textboxes[m].lines[0], 36*8 - pad*8);
std::string wrapped = font::string_wordwrap_balanced(textboxes[m].lines[0], 36*8 - pad*8);
textboxes[m].lines.clear();
size_t startline = 0;

View File

@ -206,10 +206,6 @@ public:
void PrintAlpha(int _x, int _y, const 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);
int PrintWrap(int x, int y, const std::string& s, int r, int g, int b, bool cen = false, int linespacing = -1, int maxwidth = -1);
void bprint(int x, int y, const std::string& t, int r, int g, int b, bool cen = false);
@ -217,9 +213,6 @@ public:
void bprintalpha(int x, int y, const std::string& t, int r, int g, int b, int a, bool cen = false);
int len(const std::string& t);
std::string string_wordwrap(const std::string& s, int maxwidth, short *lines = NULL);
std::string string_wordwrap_balanced(const std::string& s, int maxwidth);
std::string string_unwordwrap(const std::string& s);
void bigprint( int _x, int _y, const std::string& _s, int r, int g, int b, bool cen = false, int sc = 2 );
void bigbprint(int x, int y, const std::string& s, int r, int g, int b, bool cen = false, int sc = 2);

View File

@ -6,6 +6,7 @@
#include "Alloc.h"
#include "FileSystemUtils.h"
#include "Font.h"
#include "Graphics.h"
#include "Script.h"
#include "Vlogging.h"
@ -198,7 +199,7 @@ static void sync_lang_file(const std::string& langcode)
}
size_t alloc_len;
const std::string eng_unwrapped = graphics.string_unwordwrap(eng);
const std::string eng_unwrapped = font::string_unwordwrap(eng);
char* eng_prefixed = add_disambiguator(subElem->UnsignedAttribute("case", 1), eng_unwrapped.c_str(), &alloc_len);
if (eng_prefixed == NULL)
{

View File

@ -6,6 +6,7 @@
#include "Constants.h"
#include "CustomLevels.h"
#include "FileSystemUtils.h"
#include "Font.h"
#include "Graphics.h"
#include "Unused.h"
#include "UtilityClass.h"
@ -309,7 +310,7 @@ static bool max_check_string(const char* str, const char* max)
else
{
short lines;
graphics.string_wordwrap(str, max_w_px, &lines);
font::string_wordwrap(str, max_w_px, &lines);
does_overflow = lines > (short) max_h;
}
@ -651,7 +652,7 @@ static void loadtext_cutscenes(bool custom_level)
{
continue;
}
const std::string eng_unwrapped = graphics.string_unwordwrap(eng);
const std::string eng_unwrapped = font::string_unwordwrap(eng);
char* eng_prefixed = add_disambiguator(subElem->UnsignedAttribute("case", 1), eng_unwrapped.c_str(), NULL);
if (eng_prefixed == NULL)
{

View File

@ -10,6 +10,7 @@
#include "Entity.h"
#include "Enums.h"
#include "Exit.h"
#include "Font.h"
#include "GlitchrunnerMode.h"
#include "Graphics.h"
#include "KeyPoll.h"
@ -2466,7 +2467,7 @@ void scriptclass::translate_dialogue(void)
eng.append(txt[i]);
}
eng = graphics.string_unwordwrap(eng);
eng = font::string_unwordwrap(eng);
const loc::TextboxFormat* format = loc::gettext_cutscene(scriptname, eng, tc);
if (format == NULL || format->text == NULL || format->text[0] == '\0')
{
@ -2489,7 +2490,7 @@ void scriptclass::translate_dialogue(void)
}
else
{
tra = graphics.string_wordwrap_balanced(format->text, format->wraplimit);
tra = font::string_wordwrap_balanced(format->text, format->wraplimit);
}
textcentertext = format->centertext;