Skip to content

Instantly share code, notes, and snippets.

@SasLuca
Created June 15, 2020 09:21
Show Gist options
  • Save SasLuca/2115826b6f6369a05e9f5115179a96a0 to your computer and use it in GitHub Desktop.
Save SasLuca/2115826b6f6369a05e9f5115179a96a0 to your computer and use it in GitHub Desktop.
typedef struct rf_sizef
{
float width, height;
} rf_sizef;
RF_API rf_sizef rf_measure_text_rec(rf_font font, const char* text, int text_len, rf_rec rec, float font_size, float extra_spacing, bool wrap)
{
rf_sizef result = {0};
if (font.valid)
{
int text_offset_x = 0; // Offset between characters
int text_offset_y = 0; // Required for line break!
float scale_factor = 0.0f;
int letter = 0; // Current character
int index = 0; // Index position in sprite font
scale_factor = font_size / font.base_size;
enum
{
MEASURE_WRAP_STATE = 0,
MEASURE_REGULAR_STATE = 1
};
int state = wrap ? MEASURE_WRAP_STATE : MEASURE_REGULAR_STATE;
int start_line = -1; // Index where to begin drawing (where a line begins)
int end_line = -1; // Index where to stop drawing (where a line ends)
int lastk = -1; // Holds last value of the character position
int max_y = 0;
int first_y = 0;
bool first_y_set = false;
for (int i = 0, k = 0; i < text_len; i++, k++)
{
int glyph_width = 0;
rf_decoded_rune decoded_rune = rf_decode_utf8_char(&text[i], text_len - i);
letter = decoded_rune.codepoint;
index = rf_get_glyph_index(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad unsigned char is found (and return 0x3f)
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) decoded_rune.bytes_processed = 1;
i += decoded_rune.bytes_processed - 1;
if (letter != '\n')
{
glyph_width = (font.glyphs[index].advance_x == 0) ?
(int)(font.glyphs[index].width * scale_factor + extra_spacing) :
(int)(font.glyphs[index].advance_x * scale_factor + extra_spacing);
}
// NOTE: When word_wrap is ON we first measure how much of the text we can draw before going outside of the rec container
// We store this info in start_line and end_line, then we change states, draw the text between those two variables
// and change states again and again recursively until the end of the text (or until we get outside of the container).
// When word_wrap is OFF we don't need the measure state so we go to the drawing state immediately
// and begin drawing on the next line before we can get outside the container.
if (state == MEASURE_WRAP_STATE)
{
// TODO: there are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
// See: http://jkorpela.fi/chars/spaces.html
if ((letter == ' ') || (letter == '\t') || (letter == '\n')) { end_line = i; }
if ((text_offset_x + glyph_width + 1) >= rec.width)
{
end_line = (end_line < 1) ? i : end_line;
if (i == end_line) { end_line -= decoded_rune.bytes_processed; }
if ((start_line + decoded_rune.bytes_processed) == end_line) { end_line = i - decoded_rune.bytes_processed; }
state = !state;
}
else if ((i + 1) == text_len)
{
end_line = i;
state = !state;
}
else if (letter == '\n')
{
state = !state;
}
if (state == MEASURE_REGULAR_STATE)
{
text_offset_x = 0;
i = start_line;
glyph_width = 0;
// Save character position when we switch states
int tmp = lastk;
lastk = k - 1;
k = tmp;
}
}
else
{
if (letter == '\n')
{
if (!wrap)
{
text_offset_y += (int)((font.base_size + font.base_size/2)*scale_factor);
text_offset_x = 0;
}
}
else
{
if (!wrap && (text_offset_x + glyph_width + 1) >= rec.width)
{
text_offset_y += (int)((font.base_size + font.base_size/2)*scale_factor);
text_offset_x = 0;
}
if ((text_offset_y + (int)(font.base_size*scale_factor)) > rec.height) break;
// The right side expression is the offset of the latest character plus its width (so the end of the line)
// We want the highest value of that expression by the end of the function
result.width = rf_max_f(result.width, rec.x + text_offset_x - 1 + glyph_width);
if (!first_y_set)
{
first_y = rec.y + text_offset_y;
first_y_set = true;
}
max_y = rf_max_i(max_y, rec.y + text_offset_y + font.base_size * scale_factor);
}
if (wrap && i == end_line)
{
text_offset_y += (int)((font.base_size + font.base_size/2)*scale_factor);
text_offset_x = 0;
start_line = end_line;
end_line = -1;
glyph_width = 0;
k = lastk;
state = !state;
}
}
text_offset_x += glyph_width;
}
result.height = max_y - first_y;
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment