aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-11-15 08:20:13 +0100
committerdec05eba <dec05eba@protonmail.com>2021-11-15 08:20:13 +0100
commita3c6774f211ee765f910df76837548bdadd4e959 (patch)
tree499b29166c04fa62cb946c7d395f8a5299c78fbf /src
parentc4f84e1969f4c856a5bf0352e99fcb73a4cf56cf (diff)
Add dynamic font atlas creation (not finished)
Diffstat (limited to 'src')
-rw-r--r--src/gl.c5
-rw-r--r--src/graphics/font.c283
-rw-r--r--src/graphics/font_char_map.c214
-rw-r--r--src/graphics/sprite.c17
-rw-r--r--src/graphics/text.c10
-rw-r--r--src/graphics/texture.c117
-rw-r--r--src/graphics/vertex.c4
-rw-r--r--src/graphics/vertex_buffer.c4
-rw-r--r--src/window/window.c5
9 files changed, 531 insertions, 128 deletions
diff --git a/src/gl.c b/src/gl.c
index 44572af..5999b80 100644
--- a/src/gl.c
+++ b/src/gl.c
@@ -19,7 +19,7 @@ static void* dlsym_print_fail(void *handle, const char *name) {
}
int mgl_gl_load(mgl_gl *self) {
- const char *glx_path = "/usr/lib/libGL.so.1";
+ const char *glx_path = "libGL.so.1";
self->handle = dlopen(glx_path, RTLD_LAZY);
if(!self->handle) {
fprintf(stderr, "dlopen(\"%s\", RTLD_LAZY) failed\n", glx_path);
@@ -41,6 +41,7 @@ int mgl_gl_load(mgl_gl *self) {
{ &self->glGenTextures, "glGenTextures" },
{ &self->glDeleteTextures, "glDeleteTextures" },
{ &self->glTexImage2D, "glTexImage2D" },
+ { &self->glTexSubImage2D, "glTexSubImage2D" },
{ &self->glBindTexture, "glBindTexture" },
{ &self->glTexParameteri, "glTexParameteri" },
{ &self->glHint, "glHint" },
@@ -54,6 +55,7 @@ int mgl_gl_load(mgl_gl *self) {
{ &self->glPushMatrix, "glPushMatrix" },
{ &self->glPopMatrix, "glPopMatrix" },
{ &self->glLoadIdentity, "glLoadIdentity" },
+ { &self->glLoadMatrixf, "glLoadMatrixf" },
{ &self->glTranslatef, "glTranslatef" },
{ &self->glGenBuffers, "glGenBuffers" },
{ &self->glBindBuffer, "glBindBuffer" },
@@ -83,6 +85,7 @@ int mgl_gl_load(mgl_gl *self) {
{ &self->glUniform2f, "glUniform2f" },
{ &self->glGetError, "glGetError" },
{ &self->glGetString, "glGetString" },
+ { &self->glGetIntegerv, "glGetIntegerv" },
{ NULL, NULL }
};
diff --git a/src/graphics/font.c b/src/graphics/font.c
index dfffc7e..0044d96 100644
--- a/src/graphics/font.c
+++ b/src/graphics/font.c
@@ -1,141 +1,228 @@
#include "../../include/mgl/graphics/font.h"
-#include "../../include/mgl/system/fileutils.h"
#include <stdio.h>
-#define STB_RECT_PACK_IMPLEMENTATION
-#include "../../external/stb_rect_pack.h"
-
#define STB_TRUETYPE_IMPLEMENTATION
#include "../../external/stb_truetype.h"
-/* TODO: Test and fix .tcc files */
+/* TODO: Use correct atlas padding after resize. Its incorrect because padding from previous size doesn't accumulate */
-static unsigned int to_div2_ceil(unsigned int value) {
- const uint32_t v = value;
- return v + (v & 1);
-}
+/* Need padding so filtering doesn't touch pixels in another glyphs area */
+#define GLYPH_PADDING 2
+#define GLYPH_UPSAMPLE 1
-int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int character_size) {
+int mgl_font_load_from_file(mgl_font *self, const mgl_memory_mapped_file *mapped_file, unsigned int character_size) {
self->texture.id = 0;
- self->font_atlas.atlas = NULL;
self->font_atlas.width = 0;
self->font_atlas.height = 0;
+ self->font_atlas.prev_width = 0;
+ self->font_atlas.prev_height = 0;
+ self->font_atlas.pointer_position = (mgl_vec2i){ GLYPH_PADDING, GLYPH_PADDING };
+ self->font_atlas.render_section = MGL_ATLAS_SECTION_INITIAL;
self->character_size = character_size;
- self->packed_chars = NULL;
- self->num_packed_chars = 0;
+ mgl_font_char_map_init(&self->char_map);
+ self->current_line_max_height = 0;
+ self->font_info = NULL;
- mgl_memory_mapped_file mapped_file;
- if(mgl_mapped_file_load(filepath, &mapped_file, &(mgl_memory_mapped_file_load_options){ .readable = true, .writable = false }) != 0) {
- fprintf(stderr, "Error: failed to load font %s, error: mgl_load_file failed\n", filepath);
+ self->font_info = malloc(sizeof(stbtt_fontinfo));
+ if(!self->font_info) {
+ fprintf(stderr, "Error: failed to load font, error: out of memory\n");
return -1;
}
- stbtt_fontinfo font;
- if(!stbtt_InitFont(&font, mapped_file.data, stbtt_GetFontOffsetForIndex(mapped_file.data, 0))) {
- fprintf(stderr, "Error: failed to load font %s, error: stbtt_InitFont failed\n", filepath);
- mgl_mapped_file_unload(&mapped_file);
+ /* TODO: Handle font offset with ttc */
+ if(!stbtt_InitFont(self->font_info, mapped_file->data, stbtt_GetFontOffsetForIndex(mapped_file->data, 0))) {
+ fprintf(stderr, "Error: failed to load font, error: stbtt_InitFont failed\n");
+ mgl_font_unload(self);
return -1;
}
- self->num_packed_chars = 256;
- self->packed_chars = malloc(self->num_packed_chars * sizeof(stbtt_packedchar));
- if(!self->packed_chars) {
- fprintf(stderr, "Error: failed to load font %s, error: out of memory\n", filepath);
- goto error;
+ if(mgl_texture_init(&self->texture) != 0) {
+ mgl_font_unload(self);
+ return -1;
}
- bool atlas_created = false;
- /* TODO: Optimize */
- /* Find optimal size for atlas, starting from small to large */
- for(int i = 0; i < 4; ++i) {
- /*
- This to_div2_ceil is needed because otherwise for character sizes such as 33 which are not divisable by 2
- causes the font texture to be skewed. I dont know why this happens. Maybe a bug in stbtt?
- TODO: Figure out why it happens.
- */
- self->font_atlas.width = (14 + (8 * i)) * to_div2_ceil(self->character_size);
- self->font_atlas.height = (14 + (8 * i)) * to_div2_ceil(self->character_size);
- unsigned char *new_atlas = realloc(self->font_atlas.atlas, self->font_atlas.width * self->font_atlas.height);
- if(!new_atlas) {
- fprintf(stderr, "Error: failed to load font %s, error: out of memory\n", filepath);
- goto error;
- }
- self->font_atlas.atlas = new_atlas;
+ /* TODO: Use stbtt_GetCodepointSDF */
+ return 0;
+}
- stbtt_pack_context pc;
+void mgl_font_unload(mgl_font *self) {
+ mgl_texture_unload(&self->texture);
- if(!stbtt_PackBegin(&pc, self->font_atlas.atlas, self->font_atlas.width, self->font_atlas.height, self->font_atlas.width, 1, NULL)) {
- fprintf(stderr, "Error: failed to load font %s, error: stbtt_PackBegin failed\n", filepath);
- goto error;
- }
+ mgl_font_char_map_deinit(&self->char_map);
+ self->current_line_max_height = 0;
- stbtt_PackSetOversampling(&pc, 2, 2);
+ free(self->font_info);
+ self->font_info = NULL;
+}
- if(!stbtt_PackFontRange(&pc, mapped_file.data, 0, self->character_size, 0, self->num_packed_chars, self->packed_chars)) {
- stbtt_PackEnd(&pc);
- continue;
+static void mgl_font_handle_new_render_position(mgl_font *self, int glyph_width) {
+ if(self->font_atlas.pointer_position.x + glyph_width + GLYPH_PADDING >= self->font_atlas.width) {
+ if(self->font_atlas.pointer_position.y + self->current_line_max_height + GLYPH_PADDING >= self->font_atlas.height && self->font_atlas.render_section != MGL_ATLAS_SECTION_RIGHT) {
+ /*
+ Texture atlas looks like this with glyphs filling the texture:
+ |----|
+ |ABCD|
+ |EFGH|
+ |----|
+
+ And then texture atlas gets resized:
+ |----|----| |
+ |ABCD| | <-| 1. The right side gets filled in with new glyphs.
+ |EDFG| | <-|
+ |----|----| |
+ | | | |
+ | | | |
+ | | | |
+ |----|----| |
+ |
+ ^^^^^^^^^^^ |
+ ---------------|
+ 2. The bottom side gets filled in with new glyphs after the right side is filled with glyphs.
+ */
+
+ /* Texture overflow, enlargen the font atlas! */
+ /* TODO: Create a new texture and copy the texture content instead of rendering all glyphs again */
+ if(mgl_texture_resize(&self->texture, self->font_atlas.width * 2, self->font_atlas.height * 2, NULL) == 0) {
+ self->font_atlas.prev_width = self->font_atlas.width;
+ self->font_atlas.prev_height = self->font_atlas.height;
+ self->font_atlas.width *= 2;
+ self->font_atlas.height *= 2;
+
+ self->font_atlas.render_section = MGL_ATLAS_SECTION_RIGHT;
+ self->font_atlas.pointer_position.x = self->font_atlas.prev_width;
+ self->font_atlas.pointer_position.y = GLYPH_PADDING;
+
+ mgl_font_char_map_clear_rendered(&self->char_map);
+
+ mgl_font_glyph glyph_dummy;
+ mgl_font_char_iterator font_char_it = mgl_font_char_map_begin(&self->char_map);
+ while(font_char_it.value) {
+ mgl_font_get_glyph(self, font_char_it.value->key, &glyph_dummy);
+ mgl_font_char_iterator_next(&font_char_it);
+ }
+ } else {
+ fprintf(stderr, "Error: failed to resize font atlas\n");
+ }
+ } else if(self->font_atlas.render_section == MGL_ATLAS_SECTION_RIGHT && self->font_atlas.pointer_position.y + self->current_line_max_height + GLYPH_PADDING >= self->font_atlas.prev_height) {
+ self->font_atlas.render_section = MGL_ATLAS_SECTION_BOTTOM;
+ self->font_atlas.pointer_position.x = GLYPH_PADDING;
+ self->font_atlas.pointer_position.y = self->font_atlas.prev_height;
+ } else {
+ if(self->font_atlas.render_section != MGL_ATLAS_SECTION_RIGHT) {
+ self->font_atlas.pointer_position.x = GLYPH_PADDING;
+ } else {
+ self->font_atlas.pointer_position.x = self->font_atlas.prev_width;
+ }
+ self->font_atlas.pointer_position.y += self->current_line_max_height + GLYPH_PADDING;
}
-
- /*if(!stbtt_PackFontRange(&pc, mapped_file.data, 0, self->character_size, 0x00004E00, self->num_packed_chars, self->packed_chars)) {
- stbtt_PackEnd(&pc);
- continue;
- }*/
-
- stbtt_PackEnd(&pc);
- atlas_created = true;
- break;
+ self->current_line_max_height = 0;
}
+}
- if(!atlas_created) {
- fprintf(stderr, "Error: failed to load font %s, error: failed to create atlas\n", filepath);
- goto error;
+int mgl_font_get_glyph(mgl_font *self, uint32_t codepoint, mgl_font_glyph *glyph) {
+ if(self->font_atlas.width == 0) {
+ const int initial_atlas_size = 128;
+ /* TODO: Scale by character size */
+ mgl_texture_load_options load_options = {
+ .compressed = false,
+ .pixel_coordinates = true
+ };
+ if(mgl_texture_load_from_memory(&self->texture, NULL, initial_atlas_size, initial_atlas_size, MGL_IMAGE_FORMAT_ALPHA, &load_options) == 0) {
+ /*fprintf(stderr, "Error: failed to create font atlas texture, error: mgl_texture_load_from_memory failed\n");*/
+ self->font_atlas.width = initial_atlas_size;
+ self->font_atlas.height = initial_atlas_size;
+ }
}
- if(mgl_texture_load_from_memory(&self->texture, self->font_atlas.atlas, self->font_atlas.width, self->font_atlas.height, MGL_IMAGE_FORMAT_ALPHA, NULL) != 0) {
- fprintf(stderr, "Error: failed to load font %s, error: mgl_texture_load_from_memory failed\n", filepath);
- goto error;
+ mgl_font_char_entry *existing_char_entry = mgl_font_char_map_get(&self->char_map, codepoint);
+ if(existing_char_entry) {
+ *glyph = existing_char_entry->value;
+ if(existing_char_entry->rendered)
+ return 0;
}
- /* TODO: Use stbtt_GetCodepointSDF */
-
- mgl_mapped_file_unload(&mapped_file);
- return 0;
+ /*
+ TODO: Maybe somehow use multiple textures? that would be needed when using a lot of glyphs (such as when using chinese fonts).
+ TODO: Map texture buffer and rasterize the glyph directly into the texture.
+ */
- error:
- mgl_mapped_file_unload(&mapped_file);
- mgl_font_unload(self);
- return -1;
-}
+ const int glyph_index = stbtt_FindGlyphIndex(self->font_info, codepoint);
+ if(glyph_index == 0) {
+ memset(glyph, 0, sizeof(mgl_font_glyph));
+ return -1;
+ }
+ const float font_scale = stbtt_ScaleForPixelHeight(self->font_info, self->character_size*GLYPH_UPSAMPLE);
+
+ int advance = 0;
+ int lsb;
+ stbtt_GetGlyphHMetrics(self->font_info, glyph_index, &advance, &lsb);
+ const float glyph_advance = font_scale * advance;
+
+ int x0, y0, x1, y1;
+ stbtt_GetGlyphBitmapBox(self->font_info, glyph_index, font_scale, font_scale, &x0, &y0, &x1, &y1);
+
+ int width = x1 - x0;
+ int height = y1 - y0;
+ int xoff = x0;
+ int yoff = y0;
+
+ /* TODO: Use stbtt_MakeGlyphBitmapSubpixelPrefilter instead for better text quality */
+ const size_t pixels_size = (width + GLYPH_PADDING * 2) * (height + GLYPH_PADDING * 2);
+ unsigned char *pixels = malloc(pixels_size);
+ if(pixels) {
+ /* TODO: Is this needed? */
+ /*memset(pixels, 0, pixels_size);*/
+ stbtt_MakeGlyphBitmapSubpixel(self->font_info, pixels, width, height, width, font_scale, font_scale, GLYPH_PADDING, GLYPH_PADDING, glyph_index);
+ }
-void mgl_font_unload(mgl_font *self) {
- mgl_texture_unload(&self->texture);
+ mgl_vec2i render_offset;
+ if(existing_char_entry) {
+ render_offset = existing_char_entry->render_offset;
+ } else {
+ mgl_font_handle_new_render_position(self, width);
+ render_offset.x = self->font_atlas.pointer_position.x;
+ render_offset.y = self->font_atlas.pointer_position.y;
+
+ mgl_font_glyph new_glyph;
+ new_glyph.position = (mgl_vec2i){ xoff/GLYPH_UPSAMPLE, yoff/GLYPH_UPSAMPLE };
+ new_glyph.size = (mgl_vec2i){ width/GLYPH_UPSAMPLE, height/GLYPH_UPSAMPLE };
+ new_glyph.texture_position = (mgl_vec2i){ render_offset.x, render_offset.y };
+ new_glyph.texture_size = (mgl_vec2i){ width, height };
+ new_glyph.advance = glyph_advance/GLYPH_UPSAMPLE;
+ *glyph = new_glyph;
+
+ if(mgl_font_char_map_insert(&self->char_map, codepoint, &new_glyph, &existing_char_entry) != 0) {
+ free(pixels);
+ memset(glyph, 0, sizeof(mgl_font_glyph));
+ return 0;
+ }
+ existing_char_entry->render_offset = render_offset;
- free(self->font_atlas.atlas);
- self->font_atlas.atlas = NULL;
- self->font_atlas.width = 0;
- self->font_atlas.height = 0;
+ if(height > self->current_line_max_height)
+ self->current_line_max_height = height;
- free(self->packed_chars);
- self->packed_chars = NULL;
- self->num_packed_chars = 0;
-}
+ self->font_atlas.pointer_position.x += width + GLYPH_PADDING;
+ }
-int mgl_font_get_glyph(const mgl_font *self, uint32_t codepoint, mgl_font_glyph *glyph) {
- stbtt_packedchar *packed_chars = self->packed_chars;
- if(codepoint < 0 || codepoint >= 0 + self->num_packed_chars)
- return -1;
+ existing_char_entry->rendered = true;
+ if(!pixels) {
+ if(codepoint == ' ' || codepoint == '\t') {
+ return 0;
+ } else {
+ memset(glyph, 0, sizeof(mgl_font_glyph));
+ return -1;
+ }
+ }
- float x = 0.0f;
- float y = 0.0f;
- stbtt_aligned_quad quad;
- stbtt_GetPackedQuad(packed_chars, self->font_atlas.width, self->font_atlas.height, codepoint, &x, &y, &quad, 0);
+ const int res = mgl_texture_update(&self->texture, pixels, render_offset.x, render_offset.y, width, height, MGL_IMAGE_FORMAT_ALPHA);
+ free(pixels);
+ return res;
+}
- glyph->position = (mgl_vec2f){ quad.x0, quad.y0 };
- glyph->size = (mgl_vec2f){ quad.x1 - quad.x0, quad.y1 - quad.y0 };
- glyph->texture_position = (mgl_vec2f){ quad.s0, quad.t0 };
- glyph->texture_size = (mgl_vec2f){ quad.s1 - quad.s0, quad.t1 - quad.t0 };
- glyph->advance = x;
+int mgl_font_get_kerning(const mgl_font *self, uint32_t prev_codepoint, uint32_t codepoint) {
return 0;
+ /* TODO: */
+ /*return stbtt_GetCodepointKernAdvance(self->font_info, prev_codepoint, codepoint);*/
}
diff --git a/src/graphics/font_char_map.c b/src/graphics/font_char_map.c
new file mode 100644
index 0000000..abaa56a
--- /dev/null
+++ b/src/graphics/font_char_map.c
@@ -0,0 +1,214 @@
+#include "../../include/mgl/graphics/font_char_map.h"
+#include <stdlib.h>
+
+/* TODO: Optimize % to & */
+
+/* Optimized div by sizeof(mgl_font_char_entry*) */
+#if INTPTR_MAX == INT64_MAX
+#define CAP_NUM_ENTRIES(cap) ((cap) >> 3)
+#elif INTPTR_MAX == INT32_MAX
+#define CAP_NUM_ENTRIES(cap) ((cap) >> 2)
+#else
+#error Unsupported system. Only systems where a pointer is 32-bits or 64-bits are supported.
+#endif
+
+static size_t align_to(size_t value, size_t align) {
+ const size_t is_aligned = (value & (align - 1));
+ if(is_aligned == 0) {
+ return value;
+ } else {
+ return (value & ~(align - 1)) + align;
+ }
+}
+
+static void mgl_font_char_entry_deinit(mgl_font_char_entry *self) {
+ mgl_font_char_entry *next = self->next;
+ while(next) {
+ mgl_font_char_entry *next_next = next->next;
+ free(next);
+ next = next_next;
+ }
+}
+
+static void mgl_font_char_map_insert_entry(mgl_font_char_map *self, size_t index, uint32_t key, const mgl_font_glyph *value, mgl_font_char_entry *entry) {
+ entry->key = key;
+ entry->value = *value;
+ entry->next = self->entries[index];
+ self->entries[index] = entry;
+}
+
+static void mgl_font_char_map_reorder_nodes(mgl_font_char_map *self, size_t prev_capacity) {
+ for(size_t i = 0; i < CAP_NUM_ENTRIES(prev_capacity); ++i) {
+ mgl_font_char_entry *entry = self->entries[i];
+ if(!entry)
+ continue;
+
+ mgl_font_char_entry *prev_entry = NULL;
+ while(entry) {
+ const uint32_t hash = entry->key;
+ const size_t index = hash % CAP_NUM_ENTRIES(self->capacity);
+ mgl_font_char_entry *next_entry = entry->next; /* store next here so the next insert below doesn't cause an infinite loop */
+
+ if(index != i) {
+ /* Remove entry by replacing this entry with the next entry */
+ if(prev_entry)
+ prev_entry->next = next_entry;
+ else
+ self->entries[i] = next_entry;
+
+ /* Insert the entry at the new index */
+ mgl_font_char_map_insert_entry(self, index, entry->key, &entry->value, entry);
+ } else {
+ prev_entry = entry;
+ }
+
+ entry = next_entry;
+ }
+ }
+}
+
+static int mgl_font_char_map_ensure_capacity(mgl_font_char_map *self, size_t target_capacity) {
+ if(self->capacity >= target_capacity)
+ return 0;
+
+ size_t capacity = self->capacity;
+ if(capacity == 0)
+ capacity = 8;
+
+ while(capacity < target_capacity) {
+ capacity = capacity + (capacity >> 1); /* *= 1.5 = 1 + 0.5 */
+ }
+
+ capacity = align_to(capacity, sizeof(mgl_font_char_entry*));
+ void *new_data = realloc(self->entries, capacity);
+ if(!new_data)
+ return -1;
+
+ const size_t prev_capacity = self->capacity;
+ self->entries = new_data;
+ self->capacity = capacity;
+
+ for(size_t i = CAP_NUM_ENTRIES(prev_capacity); i < CAP_NUM_ENTRIES(self->capacity); ++i) {
+ self->entries[i] = NULL;
+ }
+
+ mgl_font_char_map_reorder_nodes(self, prev_capacity);
+ return 0;
+}
+
+void mgl_font_char_map_init(mgl_font_char_map *self) {
+ self->entries = NULL;
+ self->size = 0;
+ self->capacity = 0;
+}
+
+void mgl_font_char_map_deinit(mgl_font_char_map *self) {
+ if(self->entries) {
+ for(size_t i = 0; i < CAP_NUM_ENTRIES(self->capacity); ++i) {
+ if(self->entries[i]) {
+ mgl_font_char_entry_deinit(self->entries[i]);
+ free(self->entries[i]);
+ }
+ }
+ free(self->entries);
+ self->entries = NULL;
+ }
+ self->size = 0;
+ self->capacity = 0;
+}
+
+int mgl_font_char_map_insert(mgl_font_char_map *self, uint32_t key, const mgl_font_glyph *value, mgl_font_char_entry **inserted_entry) {
+ if(mgl_font_char_map_ensure_capacity(self, (self->size + 1) * sizeof(mgl_font_char_entry*)) != 0)
+ return -1;
+
+ ++self->size;
+ const uint32_t hash = key;
+ const size_t index = hash % CAP_NUM_ENTRIES(self->capacity);
+
+ mgl_font_char_entry *entry = malloc(sizeof(mgl_font_char_entry));
+ if(!entry)
+ return -1;
+
+ mgl_font_char_map_insert_entry(self, index, key, value, entry);
+ entry->render_offset = (mgl_vec2i){ 0, 0 };
+ entry->rendered = false;
+ if(inserted_entry)
+ *inserted_entry = entry;
+
+ return 0;
+}
+
+mgl_font_char_entry* mgl_font_char_map_get(const mgl_font_char_map *self, uint32_t key) {
+ if(self->capacity == 0)
+ return NULL;
+
+ const uint32_t hash = key;
+ const size_t index = hash % CAP_NUM_ENTRIES(self->capacity);
+
+ mgl_font_char_entry *entry = self->entries[index];
+ while(entry) {
+ if(entry->key == key)
+ return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+void mgl_font_char_map_clear_rendered(mgl_font_char_map *self) {
+ if(!self->entries)
+ return;
+
+ for(size_t i = 0; i < CAP_NUM_ENTRIES(self->capacity); ++i) {
+ mgl_font_char_entry *entry = self->entries[i];
+ if(!entry)
+ continue;
+
+ while(entry) {
+ entry->rendered = false;
+ entry = entry->next;
+ }
+ }
+}
+
+mgl_font_char_iterator mgl_font_char_map_begin(mgl_font_char_map *self) {
+ mgl_font_char_iterator it;
+ it.char_map = self;
+ it.index = 0;
+ it.value = NULL;
+
+ if(!self->entries)
+ return it;
+
+ for(size_t i = 0; i < CAP_NUM_ENTRIES(self->capacity); ++i) {
+ mgl_font_char_entry *entry = self->entries[i];
+ if(entry) {
+ it.index = i;
+ it.value = entry;
+ break;
+ }
+ }
+
+ return it;
+}
+
+void mgl_font_char_iterator_next(mgl_font_char_iterator *self) {
+ if(!self->value)
+ return;
+
+ if(self->value->next) {
+ self->value = self->value->next;
+ return;
+ }
+
+ for(size_t i = self->index + 1; i < CAP_NUM_ENTRIES(self->char_map->capacity); ++i) {
+ mgl_font_char_entry *entry = self->char_map->entries[i];
+ if(entry) {
+ self->index = i;
+ self->value = entry;
+ return;
+ }
+ }
+
+ self->value = NULL;
+}
diff --git a/src/graphics/sprite.c b/src/graphics/sprite.c
index 231b554..2aecdd1 100644
--- a/src/graphics/sprite.c
+++ b/src/graphics/sprite.c
@@ -26,20 +26,27 @@ void mgl_sprite_draw(mgl_context *context, mgl_sprite *sprite) {
if(!sprite->texture)
return;
+ float texture_right = 1.0f;
+ float texture_bottom = 1.0f;
+ if(sprite->texture->pixel_coordinates) {
+ texture_right = sprite->texture->width;
+ texture_bottom = sprite->texture->height;
+ }
+
context->gl.glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a);
- context->gl.glBindTexture(GL_TEXTURE_2D, sprite->texture->id);
+ mgl_texture_use(sprite->texture);
context->gl.glBegin(GL_QUADS);
context->gl.glTexCoord2f(0.0f, 0.0f);
context->gl.glVertex3f(sprite->position.x, sprite->position.y, 0.0f);
- context->gl.glTexCoord2f(1.0f, 0.0f);
+ context->gl.glTexCoord2f(texture_right, 0.0f);
context->gl.glVertex3f(sprite->position.x + sprite->texture->width * sprite->scale.x, sprite->position.y, 0.0f);
- context->gl.glTexCoord2f(1.0f, 1.0f);
+ context->gl.glTexCoord2f(texture_right, texture_bottom);
context->gl.glVertex3f(sprite->position.x + sprite->texture->width * sprite->scale.x, sprite->position.y + sprite->texture->height * sprite->scale.y, 0.0f);
- context->gl.glTexCoord2f(0.0f, 1.0f);
+ context->gl.glTexCoord2f(0.0f, texture_bottom);
context->gl.glVertex3f(sprite->position.x, sprite->position.y + sprite->texture->height * sprite->scale.y, 0.0f);
context->gl.glEnd();
- context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ mgl_texture_use(NULL);
}
diff --git a/src/graphics/text.c b/src/graphics/text.c
index d330dc0..ea60b6a 100644
--- a/src/graphics/text.c
+++ b/src/graphics/text.c
@@ -4,6 +4,8 @@
#include "../../include/mgl/mgl.h"
#include <stdio.h>
+/* TODO: Cache mgl_font_get_glyph */
+
#define TAB_WIDTH 4
static float max_float(float a, float b) {
@@ -95,7 +97,7 @@ mgl_vec2f mgl_text_get_bounds(const mgl_text *self) {
return self->bounds;
}
-static void mgl_text_draw_glyph(mgl_context *context, mgl_font_glyph *glyph, mgl_vec2f position) {
+static void mgl_text_draw_glyph(mgl_context *context, mgl_font_glyph *glyph, mgl_vec2i position) {
context->gl.glTexCoord2f(glyph->texture_position.x, glyph->texture_position.y);
context->gl.glVertex3f(position.x + glyph->position.x, position.y + glyph->position.y, 0.0f);
@@ -116,11 +118,11 @@ void mgl_text_draw(mgl_context *context, mgl_text *text) {
return;
mgl_font_glyph glyph;
- mgl_vec2f position = text->position;
+ mgl_vec2i position = (mgl_vec2i){ text->position.x, text->position.y };
position.y += text->font->character_size;
context->gl.glColor4ub(text->color.r, text->color.g, text->color.b, text->color.a);
- context->gl.glBindTexture(GL_TEXTURE_2D, text->font->texture.id);
+ mgl_texture_use(&text->font->texture);
context->gl.glBegin(GL_QUADS);
for(size_t i = 0; i < text->text_size;) {
unsigned char *cp = (unsigned char*)&text->text[i];
@@ -147,5 +149,5 @@ void mgl_text_draw(mgl_context *context, mgl_text *text) {
i += clen;
}
context->gl.glEnd();
- context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ mgl_texture_use(NULL);
}
diff --git a/src/graphics/texture.c b/src/graphics/texture.c
index f6549f1..2387fa7 100644
--- a/src/graphics/texture.c
+++ b/src/graphics/texture.c
@@ -3,7 +3,7 @@
#include "../../include/mgl/mgl.h"
#include <stdio.h>
-/* TODO: Check for glTexImage2D failure */
+/* TODO: Check for glTexImage2D/glTexSubImage2D failure */
static int mgl_texture_format_to_opengl_format(mgl_texture_format format) {
switch(format) {
@@ -49,11 +49,31 @@ static mgl_texture_format mgl_image_format_to_mgl_texture_format(mgl_image_forma
return 0;
}
-int mgl_texture_load_from_file(mgl_texture *self, const char *filepath, mgl_texture_load_options *load_options) {
+int mgl_texture_init(mgl_texture *self) {
self->id = 0;
self->width = 0;
self->height = 0;
-
+ self->format = 0;
+ self->max_width = 0;
+ self->max_height = 0;
+ self->pixel_coordinates = false;
+
+ mgl_context *context = mgl_get_context();
+ context->gl.glGenTextures(1, &self->id);
+ if(self->id == 0) {
+ fprintf(stderr, "Error: failed to init texture (glGenTextures failed)\n");
+ return -1;
+ }
+
+ int max_texture_size = 0;
+ context->gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+ self->max_width = max_texture_size;
+ self->max_height = max_texture_size;
+
+ return 0;
+}
+
+int mgl_texture_load_from_file(mgl_texture *self, const char *filepath, mgl_texture_load_options *load_options) {
mgl_image image;
if(mgl_image_load_from_file(&image, filepath) != 0)
return -1;
@@ -68,21 +88,22 @@ int mgl_texture_load_from_image(mgl_texture *self, const mgl_image *image, mgl_t
}
int mgl_texture_load_from_memory(mgl_texture *self, const unsigned char *data, int width, int height, mgl_image_format format, mgl_texture_load_options *load_options) {
- self->id = 0;
+ if(width < 0 || height < 0)
+ return -1;
+
+ if(width > self->max_width || height > self->max_height)
+ return -1;
+
self->width = width;
self->height = height;
self->format = mgl_image_format_to_mgl_texture_format(format);
+ self->pixel_coordinates = load_options ? load_options->pixel_coordinates : false;
- mgl_context *context = mgl_get_context();
- context->gl.glGenTextures(1, &self->id);
- if(self->id == 0) {
- fprintf(stderr, "Error: failed to load image from memory (glGenTextures failed)\n");
- mgl_texture_unload(self);
- return -1;
- }
-
- const int opengl_texture_format = load_options && load_options->compressed ? mgl_texture_format_to_compressed_opengl_format(self->format) : mgl_texture_format_to_opengl_format(self->format);
+ int opengl_texture_format = mgl_texture_format_to_opengl_format(self->format);
+ if(load_options && load_options->compressed)
+ opengl_texture_format = mgl_texture_format_to_compressed_opengl_format(self->format);
+ mgl_context *context = mgl_get_context();
context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, data);
context->gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -94,13 +115,77 @@ int mgl_texture_load_from_memory(mgl_texture *self, const unsigned char *data, i
return 0;
}
+int mgl_texture_update(mgl_texture *self, const unsigned char *data, int offset_x, int offset_y, int width, int height, mgl_image_format format) {
+ if(offset_x + width > self->width || offset_y + height > self->height)
+ return -1;
+
+ mgl_context *context = mgl_get_context();
+ context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
+ const mgl_texture_format texture_format = mgl_image_format_to_mgl_texture_format(format);
+ /* TODO: TODO: Only do one glTexSubImage2D */
+#if 1
+ for(int i = 0; i < height; ++i) {
+ context->gl.glTexSubImage2D(GL_TEXTURE_2D, 0, offset_x, offset_y + i, width, 1, mgl_texture_format_to_source_opengl_format(texture_format), GL_UNSIGNED_BYTE, data + (i * width));
+ }
+#else
+ context->gl.glTexSubImage2D(GL_TEXTURE_2D, 0, offset_x, offset_y, width, height, mgl_texture_format_to_source_opengl_format(texture_format), GL_UNSIGNED_BYTE, data);
+#endif
+ context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ return 0;
+}
+
+int mgl_texture_resize(mgl_texture *self, int new_width, int new_height, mgl_texture_load_options *load_options) {
+ if(new_width == self->width && new_height == self->height)
+ return 0;
+
+ if(new_width < 0 || new_height < 0)
+ return -1;
+
+ if(new_width > self->max_width || new_height > self->max_height)
+ return -1;
+
+ self->width = new_width;
+ self->height = new_height;
+
+ const int opengl_texture_format = load_options && load_options->compressed ? mgl_texture_format_to_compressed_opengl_format(self->format) : mgl_texture_format_to_opengl_format(self->format);
+
+ mgl_context *context = mgl_get_context();
+ context->gl.glBindTexture(GL_TEXTURE_2D, self->id);
+ context->gl.glTexImage2D(GL_TEXTURE_2D, 0, opengl_texture_format, self->width, self->height, 0, mgl_texture_format_to_source_opengl_format(self->format), GL_UNSIGNED_BYTE, NULL);
+ context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ return 0;
+}
+
+void mgl_texture_use(const mgl_texture *texture) {
+ mgl_context *context = mgl_get_context();
+
+ if(texture) {
+ float matrix[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ };
+
+ if(texture->pixel_coordinates) {
+ matrix[0] = 1.0f / (float)texture->width;
+ matrix[5] = 1.0f / (float)texture->height;
+ }
+
+ context->gl.glMatrixMode(GL_TEXTURE);
+ context->gl.glLoadMatrixf(matrix);
+ context->gl.glMatrixMode(GL_MODELVIEW);
+
+ context->gl.glBindTexture(GL_TEXTURE_2D, texture->id);
+ } else {
+ context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ }
+}
+
void mgl_texture_unload(mgl_texture *self) {
mgl_context *context = mgl_get_context();
if(self->id) {
context->gl.glDeleteTextures(1, &self->id);
self->id = 0;
}
- self->width = 0;
- self->height = 0;
- self->format = 0;
}
diff --git a/src/graphics/vertex.c b/src/graphics/vertex.c
index dff9661..110c86d 100644
--- a/src/graphics/vertex.c
+++ b/src/graphics/vertex.c
@@ -5,6 +5,10 @@ void mgl_vertices_draw(mgl_context *context, const mgl_vertex *vertices, size_t
if(vertex_count == 0)
return;
+ context->gl.glMatrixMode(GL_TEXTURE);
+ context->gl.glLoadIdentity();
+ context->gl.glMatrixMode(GL_MODELVIEW);
+
context->gl.glVertexPointer(2, GL_FLOAT, sizeof(mgl_vertex), (void*)&vertices[0].position);
context->gl.glTexCoordPointer(2, GL_FLOAT, sizeof(mgl_vertex), (void*)&vertices[0].texcoords);
context->gl.glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(mgl_vertex), (void*)&vertices[0].color);
diff --git a/src/graphics/vertex_buffer.c b/src/graphics/vertex_buffer.c
index 325665c..9d15d47 100644
--- a/src/graphics/vertex_buffer.c
+++ b/src/graphics/vertex_buffer.c
@@ -100,12 +100,12 @@ void mgl_vertex_buffer_draw(mgl_context *context, mgl_vertex_buffer *vertex_buff
context->gl.glPushMatrix();
context->gl.glTranslatef(vertex_buffer->position.x, vertex_buffer->position.y, 0.0f);
- context->gl.glBindTexture(GL_TEXTURE_2D, texture ? texture->id : 0);
+ mgl_texture_use(texture);
context->gl.glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer->id);
mgl_vertex_buffer_set_gl_buffer_pointers(context);
context->gl.glDrawArrays(mgl_primitive_type_to_gl_mode(vertex_buffer->primitive_type), 0, vertex_buffer->vertex_count);
context->gl.glBindBuffer(GL_ARRAY_BUFFER, 0);
- context->gl.glBindTexture(GL_TEXTURE_2D, 0);
+ mgl_texture_use(NULL);
context->gl.glPopMatrix();
}
diff --git a/src/window/window.c b/src/window/window.c
index 642609b..4d72635 100644
--- a/src/window/window.c
+++ b/src/window/window.c
@@ -171,7 +171,7 @@ static int mgl_window_init(mgl_window *self, const char *title, int width, int h
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask | ButtonMotionMask |
- StructureNotifyMask;
+ StructureNotifyMask | EnterWindowMask | LeaveWindowMask | VisibilityChangeMask | PropertyChangeMask | FocusChangeMask;
window_attr.bit_gravity = NorthWestGravity;
if(existing_window) {
@@ -356,7 +356,7 @@ static void mgl_window_handle_key_event(mgl_window *self, XKeyEvent *xkey, mgl_e
event->key.alt = ((xkey->state & Mod1Mask) != 0);
event->key.control = ((xkey->state & ControlMask) != 0);
event->key.shift = ((xkey->state & ShiftMask) != 0);
- event->key.system = ((xkey->state & Mod4Mask) != 0); /* TODO: Fix, doesn't work */
+ event->key.system = ((xkey->state & Mod4Mask) != 0);
x11_context_update_key_state(self->context, event->key.code, pressed);
}
@@ -506,6 +506,7 @@ void mgl_window_set_view(mgl_window *self, mgl_view *new_view) {
context->gl.glMatrixMode(GL_PROJECTION);
context->gl.glLoadIdentity();
context->gl.glOrtho(0.0, new_view->size.x, new_view->size.y, 0.0, 0.0, 1.0);
+ context->gl.glMatrixMode(GL_MODELVIEW);
}
void mgl_window_get_view(mgl_window *self, mgl_view *view) {