Refactored lines to contain 'new line'

This commit is contained in:
Krzosa Karol
2024-06-28 15:43:41 +02:00
parent 63adca1fac
commit 2dacc969a3
3 changed files with 179 additions and 271 deletions

View File

@@ -27,6 +27,7 @@ struct Range {
struct Line { struct Line {
int64_t number; int64_t number;
Range range; Range range;
int64_t max_without_new_line;
}; };
struct LineAndColumn { struct LineAndColumn {
@@ -91,6 +92,11 @@ Range MakeRange(int64_t a, int64_t b) {
return result; return result;
} }
Range MakeRange(int64_t a) {
Range result = {a, a};
return result;
}
int64_t GetFront(Cursor cursor) { int64_t GetFront(Cursor cursor) {
int64_t result = cursor.pos[cursor.ifront]; int64_t result = cursor.pos[cursor.ifront];
return result; return result;
@@ -342,7 +348,7 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
int64_t index = 0; int64_t index = 0;
int64_t base_index = 0; int64_t base_index = 0;
while (Seek(string, delimiter, &index)) { while (Seek(string, delimiter, &index)) {
buffer->lines.add({base_index, base_index + index}); buffer->lines.add({base_index, base_index + index + delimiter.len});
base_index += index + delimiter.len; base_index += index + delimiter.len;
string = string.skip(index + delimiter.len); string = string.skip(index + delimiter.len);
} }
@@ -457,25 +463,28 @@ BufferIter Iterate(Buffer &buffer, Range range, int64_t direction = ITERATE_FORW
return result; return result;
} }
Line GetLine(Buffer &buffer, int64_t line) { Line GetLineByIndex(Buffer &buffer, int64_t line) {
Assert(buffer.lines.len); Assert(buffer.lines.len);
line = Clamp(line, (int64_t)0, buffer.lines.len - 1); line = Clamp(line, (int64_t)0, buffer.lines.len - 1);
Range range = buffer.lines[line]; Range range = buffer.lines[line];
Line result = {line, range}; Line result = {line, range, range.max};
if (GetChar(buffer, range.max - 1) == '\n') result.max_without_new_line -= 1;
return result; return result;
} }
Line FindLine(Buffer &buffer, int64_t pos) { Line FindLine(Buffer &buffer, int64_t pos) {
Line result = {};
For(buffer.lines) { For(buffer.lines) {
// The program is doing '<= it.max' so as to include the new line. bool found_last = (pos == it.min && pos == it.max);
// Otherwise this function wouldn't be able to find certain positions. bool found = pos >= it.min && pos < it.max;
if (pos >= it.min && pos <= it.max) { if (found || found_last) {
Line result = {buffer.lines.get_index(it), it}; result = {buffer.lines.get_index(it), it, it.max};
return result; if (GetChar(buffer, it.max - 1) == '\n') result.max_without_new_line -= 1;
break;
} }
} }
return {}; return result;
} }
LineAndColumn FindLineAndColumn(Buffer &buffer, int64_t pos) { LineAndColumn FindLineAndColumn(Buffer &buffer, int64_t pos) {
@@ -493,8 +502,8 @@ LineAndColumn FindLineAndColumn(Buffer &buffer, int64_t pos) {
} }
int64_t FindPos(Buffer &buffer, int64_t line_number, int64_t column) { int64_t FindPos(Buffer &buffer, int64_t line_number, int64_t column) {
Line line = GetLine(buffer, line_number); Line line = GetLineByIndex(buffer, line_number);
int64_t result = line.range.max; int64_t result = line.max_without_new_line;
for (BufferIter iter = Iterate(buffer, line.range); IsValid(iter); Advance(&iter)) { for (BufferIter iter = Iterate(buffer, line.range); IsValid(iter); Advance(&iter)) {
if (iter.codepoint_index == column) { if (iter.codepoint_index == column) {
result = iter.pos; result = iter.pos;
@@ -552,146 +561,3 @@ int64_t Seek(Buffer &buffer, int64_t pos, int64_t direction = ITERATE_FORWARD) {
} }
return result; return result;
} }
void RunBufferTests() {
Scratch scratch;
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
AddEdit(&edits, {0, 0}, "Things and other things");
ApplyEdits(&buffer, edits);
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Things and other things");
Assert(buffer.lines.len == 1);
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things");
edits.clear();
AddEdit(&edits, GetEnd(buffer), " memes");
ApplyEdits(&buffer, edits);
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things memes");
}
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
edits.add({});
ApplyEdits(&buffer, edits);
Assert("" == GetString(buffer));
Assert(buffer.lines.len == 1);
}
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
edits.add({
{0, 0},
"Things and other things"
});
ApplyEdits(&buffer, edits);
edits.clear();
AddEdit(&edits, {0, 6}, "Memes");
AddEdit(&edits, {7, 10}, "dna");
AddEdit(&edits, {11, 16}, "BigOther");
ApplyEdits(&buffer, edits);
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Memes dna BigOther things");
Assert(buffer.lines.len == 1);
Assert(GetString(buffer, buffer.lines[0]) == "Memes dna BigOther things");
}
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
edits.add({
{0, 0},
"Things and other things\n"
"Things and other things\n"
});
ApplyEdits(&buffer, edits);
Assert(buffer.lines.len == 3);
Assert(GetString(buffer, buffer.lines[1]) == "Things and other things");
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things");
Assert(GetString(buffer, buffer.lines[2]) == "");
{
Array<char> s = {scratch};
for (BufferIter iter = Iterate(buffer, {0, 6}); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255);
s.add((char)iter.item);
}
String str = {s.data, s.len};
Assert(str == "Things");
}
{
Array<char> s = {scratch};
for (BufferIter iter = Iterate(buffer, {0, 6}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255);
s.add((char)iter.item);
}
String str = {s.data, s.len};
Assert(str == "sgnihT");
}
{
Array<char> s = {scratch};
for (BufferIter iter = Iterate(buffer, {0, buffer.len}); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255);
s.add((char)iter.item);
}
String str = {s.data, s.len};
String b = {GetCharP(buffer, 0), buffer.len};
Assert(str == b);
}
{
Array<char> s = {scratch};
for (BufferIter iter = Iterate(buffer, {0, buffer.len}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255);
s.add((char)iter.item);
}
String str = {s.data, s.len};
String b = {GetCharP(buffer, 0), buffer.len};
Assert(str.len == b.len);
}
}
{
Arena *arena = AllocArena();
Buffer buffer = {*arena};
Array<Edit> edits = {*arena};
edits.add({
{0, 0},
"Things and other things\n"
"Things and other things\n"
});
int iters = 100;
for (int i = 0; i < iters; i += 1) {
ApplyEdits(&buffer, edits);
for (int64_t j = 0; j < i; j += 1) {
String string = GetString(buffer, {edits[0].string.len * j, edits[0].string.len * (j + 1)});
Assert(string == edits[0].string);
}
}
Assert(edits[0].string.len * iters == buffer.len);
Assert(buffer.lines.len == iters * 2 + 1);
Line l0 = FindLine(buffer, 4);
Assert(l0.number == 0);
Assert(l0.range.min == 0);
Assert(l0.range.max < 30);
Line l1 = FindLine(buffer, 30);
Assert(l1.number == 1);
Assert(l1.range.min > 20);
Assert(l1.range.max < 50);
Assert(l1.range.max == GetLine(buffer, 1).range.max);
// Make sure there are no gaps
for (int64_t i = 100; i < 600; i += 1) {
Line l2 = FindLine(buffer, i);
Assert(l2.number > 0);
}
}
}

View File

@@ -40,50 +40,130 @@ Layout CalculateLayout(Arena *arena, Buffer &buffer, Font font, float font_size,
Layout layout = {}; Layout layout = {};
layout.rows.allocator = *arena; layout.rows.allocator = *arena;
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor Range *last_range = buffer.lines.last();
float text_offset_y = 0; float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
float text_offset_y = 0;
float text_offset_x = 0;
float line_spacing = font_size;
ForItem(line_range, buffer.lines) { ForItem(line_range, buffer.lines) {
float textOffsetX = 0.0f; text_offset_x = 0.0f;
if (&line_range == last_range && GetRangeSize(line_range) == 0) break; // end of buffer line
LayoutRow *row = layout.rows.alloc(); LayoutRow *row = layout.rows.alloc();
row->columns.allocator = *arena; row->columns.allocator = *arena;
row->rect.min = {textOffsetX, text_offset_y}; row->rect.min = {text_offset_x, text_offset_y};
BufferIter iter = Iterate(buffer, line_range); for (BufferIter iter = Iterate(buffer, line_range); IsValid(iter); Advance(&iter)) {
for (;; Advance(&iter)) { int index = GetGlyphIndex(font, (int)iter.item);
bool end_of_buffer = iter.pos == buffer.len;
bool new_line = iter.pos == line_range.max;
bool in_range = IsValid(iter);
bool continue_looping = end_of_buffer || new_line || in_range;
if (!continue_looping) break;
int codepoint = '\n'; // @todo: questionable choice
if (in_range) codepoint = iter.item;
int index = GetGlyphIndex(font, codepoint);
GlyphInfo *glyph = font.glyphs + index; GlyphInfo *glyph = font.glyphs + index;
Vec2 glyph_position = {textOffsetX, text_offset_y}; Vec2 glyph_position = {text_offset_x, text_offset_y};
float x_to_offset_by = ((float)glyph->advanceX * scaleFactor + font_spacing); float x_to_offset_by = ((float)glyph->advanceX * scaleFactor + font_spacing);
if (glyph->advanceX == 0) x_to_offset_by = ((float)font.recs[index].width * scaleFactor + font_spacing); if (glyph->advanceX == 0) x_to_offset_by = ((float)font.recs[index].width * scaleFactor + font_spacing);
Vec2 cell_size = {x_to_offset_by, font_size}; Vec2 cell_size = {x_to_offset_by, font_size};
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)}; Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
row->columns.add({cell_rect, iter.pos, codepoint}); row->columns.add({cell_rect, iter.pos, (int)iter.item});
row->rect.max = cell_rect.max; row->rect.max = cell_rect.max;
textOffsetX += x_to_offset_by; text_offset_x += x_to_offset_by;
if (end_of_buffer || new_line) break;
} }
layout.buffer_world_pixel_size.x = Max(layout.buffer_world_pixel_size.x, textOffsetX); layout.buffer_world_pixel_size.x = Max(layout.buffer_world_pixel_size.x, text_offset_x);
text_offset_y += font_size; text_offset_y += line_spacing;
} }
// Add end of buffer as layout cell at the end
{
LayoutRow *row = layout.rows.alloc();
row->columns.allocator = *arena;
if (last_range->min == last_range->max) {
text_offset_x = 0;
} else {
text_offset_y -= line_spacing;
}
int index = GetGlyphIndex(font, ' ');
GlyphInfo *glyph = font.glyphs + index;
Vec2 glyph_position = {text_offset_x, text_offset_y};
float x_to_offset_by = ((float)glyph->advanceX * scaleFactor + font_spacing);
if (glyph->advanceX == 0) x_to_offset_by = ((float)font.recs[index].width * scaleFactor + font_spacing);
Vec2 cell_size = {x_to_offset_by, line_spacing};
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
row->columns.add({cell_rect, buffer.len, '\0'});
row->rect = {glyph_position, cell_rect.max};
}
layout.buffer_world_pixel_size.y = text_offset_y; layout.buffer_world_pixel_size.y = text_offset_y;
return layout; return layout;
} }
void DrawString(Font font, String text, Vector2 position, float fontSize, float spacing, Color tint) {
if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize / font.baseSize; // Character quad scaling factor
for (int i = 0; i < text.len;) {
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
if ((codepoint != ' ') && (codepoint != '\t')) {
DrawTextCodepoint(font, codepoint, {position.x + textOffsetX, position.y}, fontSize, tint);
}
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width * scaleFactor + spacing);
else textOffsetX += ((float)font.glyphs[index].advanceX * scaleFactor + spacing);
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
Vector2 MeasureString(Font font, String text, float fontSize, float spacing) {
Vector2 textSize = {0};
if ((font.texture.id == 0) || (text.data == NULL)) return textSize; // Security check
int size = (int)text.len; // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars
int byteCounter = 0;
float textWidth = 0.0f;
float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = fontSize;
float scaleFactor = fontSize / (float)font.baseSize;
int letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < size;) {
byteCounter++;
int next = 0;
letter = GetCodepointNext(&text.data[i], &next);
index = GetGlyphIndex(font, letter);
i += next;
if (font.glyphs[index].advanceX != 0) textWidth += font.glyphs[index].advanceX;
else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
}
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
textSize.x = tempTextWidth * scaleFactor + (float)((tempByteCounter - 1) * spacing);
textSize.y = textHeight;
return textSize;
}
LayoutRow *GetLayoutRow(Window &window, float ypos_window_buffer_world_units) { LayoutRow *GetLayoutRow(Window &window, float ypos_window_buffer_world_units) {
float line_spacing = window.font_size; float line_spacing = window.font_size;
int64_t line = (int64_t)floorf(ypos_window_buffer_world_units / line_spacing); int64_t line = (int64_t)floorf(ypos_window_buffer_world_units / line_spacing);

View File

@@ -17,70 +17,6 @@ Rect2 GetScreenRect() {
return result; return result;
} }
void DrawString(Font font, String text, Vector2 position, float fontSize, float spacing, Color tint) {
if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize / font.baseSize; // Character quad scaling factor
for (int i = 0; i < text.len;) {
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
if ((codepoint != ' ') && (codepoint != '\t')) {
DrawTextCodepoint(font, codepoint, {position.x + textOffsetX, position.y}, fontSize, tint);
}
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width * scaleFactor + spacing);
else textOffsetX += ((float)font.glyphs[index].advanceX * scaleFactor + spacing);
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
Vector2 MeasureString(Font font, String text, float fontSize, float spacing) {
Vector2 textSize = {0};
if ((font.texture.id == 0) || (text.data == NULL)) return textSize; // Security check
int size = (int)text.len; // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars
int byteCounter = 0;
float textWidth = 0.0f;
float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = fontSize;
float scaleFactor = fontSize / (float)font.baseSize;
int letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < size;) {
byteCounter++;
int next = 0;
letter = GetCodepointNext(&text.data[i], &next);
index = GetGlyphIndex(font, letter);
i += next;
if (font.glyphs[index].advanceX != 0) textWidth += font.glyphs[index].advanceX;
else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
}
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
textSize.x = tempTextWidth * scaleFactor + (float)((tempByteCounter - 1) * spacing);
textSize.y = textHeight;
return textSize;
}
int64_t MoveRight(Buffer &buffer, int64_t pos) { int64_t MoveRight(Buffer &buffer, int64_t pos) {
pos = pos + 1; pos = pos + 1;
pos = AdjustUTF8Pos(buffer, pos); pos = AdjustUTF8Pos(buffer, pos);
@@ -192,8 +128,6 @@ void Dbg_Draw() {
int main() { int main() {
InitScratch(); InitScratch();
RunBufferTests();
InitWindow(800, 600, "Hello"); InitWindow(800, 600, "Hello");
SetTargetFPS(60); SetTargetFPS(60);
@@ -218,11 +152,14 @@ int main() {
InitBuffer(&window.buffer); InitBuffer(&window.buffer);
if (1) { if (1) {
for (int i = 0; i < 50; i += 1) { Array<Edit> edits = {FrameArena};
Array<Edit> edits = {FrameArena}; AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: 1"));
AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number line number line number line number: %d\n", i)); ApplyEdits(&window.buffer, edits);
ApplyEdits(&window.buffer, edits); // for (int i = 0; i < 5; i += 1) {
} // Array<Edit> edits = {FrameArena};
// AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: %d\n", i));
// ApplyEdits(&window.buffer, edits);
// }
} }
window.cursors.add({}); window.cursors.add({});
@@ -321,8 +258,8 @@ int main() {
For(focused_window->cursors) { For(focused_window->cursors) {
int64_t front = GetFront(it); int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front); Line line = FindLine(focused_window->buffer, front);
String string = GetString(focused_window->buffer, {line.range.min, line.range.max + 1}); String string = GetString(focused_window->buffer, line.range);
AddEdit(&edits, {line.range.max + 1, line.range.max + 1}, string); AddEdit(&edits, {line.range.max, line.range.max}, string);
} }
ApplyEdits(&focused_window->buffer, edits); ApplyEdits(&focused_window->buffer, edits);
AfterEdit(focused_window, edits); AfterEdit(focused_window, edits);
@@ -355,7 +292,7 @@ int main() {
For(focused_window->cursors) { For(focused_window->cursors) {
int64_t front = GetFront(it); int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front); Line line = FindLine(focused_window->buffer, front);
String string = GetString(focused_window->buffer, {line.range.min, line.range.max + 1}); String string = GetString(focused_window->buffer, line.range);
AddEdit(&edits, {line.range.min, line.range.min}, string); AddEdit(&edits, {line.range.min, line.range.min}, string);
} }
ApplyEdits(&focused_window->buffer, edits); ApplyEdits(&focused_window->buffer, edits);
@@ -376,6 +313,32 @@ int main() {
} }
} }
For(focused_window->cursors) {
if (IsKeyPressed(KEY_HOME) || IsKeyPressedRepeat(KEY_HOME)) {
if (IsKeyDown(KEY_LEFT_SHIFT)) {
int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front);
it = ChangeFront(it, line.range.min);
} else {
int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front);
it.range.min = it.range.max = line.range.min;
}
}
if (IsKeyPressed(KEY_END) || IsKeyPressedRepeat(KEY_END)) {
if (IsKeyDown(KEY_LEFT_SHIFT)) {
int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front);
it = ChangeFront(it, line.max_without_new_line);
} else {
int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front);
it.range.min = it.range.max = line.max_without_new_line;
}
}
}
// @todo: improve behaviour of all copy pasting // @todo: improve behaviour of all copy pasting
if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) { if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) {
Array<String> strings = {FrameArena}; Array<String> strings = {FrameArena};
@@ -402,7 +365,7 @@ int main() {
For(focused_window->cursors) { For(focused_window->cursors) {
if (GetRangeSize(it.range) == 0) { if (GetRangeSize(it.range) == 0) {
Line line = FindLine(focused_window->buffer, it.range.min); Line line = FindLine(focused_window->buffer, it.range.min);
it.range = {line.range.min, line.range.max + 1}; it.range = line.range;
} }
} }
@@ -420,24 +383,6 @@ int main() {
AfterEdit(focused_window, edits); AfterEdit(focused_window, edits);
} }
if (IsKeyPressed(KEY_ENTER)) {
if (IsKeyDown(KEY_LEFT_CONTROL)) {
} else {
}
}
if (IsKeyPressed(KEY_HOME) || IsKeyPressedRepeat(KEY_HOME)) {
if (IsKeyDown(KEY_LEFT_SHIFT)) {
} else {
}
}
if (IsKeyPressed(KEY_END) || IsKeyPressedRepeat(KEY_END)) {
if (IsKeyDown(KEY_LEFT_SHIFT)) {
} else {
}
}
if (IsKeyDown(KEY_LEFT_CONTROL) && (IsKeyPressed(KEY_D) || IsKeyPressedRepeat(KEY_D))) { if (IsKeyDown(KEY_LEFT_CONTROL) && (IsKeyPressed(KEY_D) || IsKeyPressedRepeat(KEY_D))) {
} }
@@ -460,6 +405,22 @@ int main() {
} }
} }
if (IsKeyPressed(KEY_ENTER)) {
if (IsKeyDown(KEY_LEFT_CONTROL)) {
For(focused_window->cursors) {
int64_t front = GetFront(it);
Line line = FindLine(focused_window->buffer, front);
it.range = MakeRange(line.max_without_new_line);
}
}
BeforeEdit(focused_window);
Array<Edit> edits = {FrameArena};
For(focused_window->cursors) AddEdit(&edits, it.range, "\n");
ApplyEdits(&focused_window->buffer, edits);
AfterEdit(focused_window, edits);
}
// Handle user input // Handle user input
for (;;) { for (;;) {
int c = GetCharPressed(); int c = GetCharPressed();
@@ -584,8 +545,9 @@ int main() {
} }
if (col.codepoint == '\n') { if (col.codepoint == '\n') {
Vec2 mid = GetMid(rect); DrawTextEx(font, "\\n", rect.min, font_size, font_spacing, GRAY);
DrawCircle((int)mid.x, (int)mid.y, font_size / 8, {0, 0, 0, 120}); } else if (col.codepoint == '\0') {
DrawTextEx(font, "\\0", rect.min, font_size, font_spacing, {255, 0, 0, 150});
} else if ((col.codepoint != ' ') && (col.codepoint != '\t')) { } else if ((col.codepoint != ' ') && (col.codepoint != '\t')) {
DrawTextCodepoint(font, col.codepoint, rect.min, font_size, BLACK); DrawTextCodepoint(font, col.codepoint, rect.min, font_size, BLACK);
} }