add the text primitive, finally
This commit is contained in:
		| @@ -57,7 +57,6 @@ set(TOWNENGINE_SOURCE_FILES | |||||||
|         townengine/util.c townengine/util.h |         townengine/util.c townengine/util.h | ||||||
|         townengine/rendering.c townengine/rendering.h |         townengine/rendering.c townengine/rendering.h | ||||||
|         townengine/input/input.c townengine/input.h |         townengine/input/input.c townengine/input.h | ||||||
|         townengine/text.c townengine/text.h |  | ||||||
|         townengine/camera.c townengine/camera.h |         townengine/camera.c townengine/camera.h | ||||||
|         townengine/textures/textures.c |         townengine/textures/textures.c | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,6 +17,34 @@ static void title_tick(struct state *state) { | |||||||
|  |  | ||||||
|     m_sprite("/assets/title.png", ((t_frect) { |     m_sprite("/assets/title.png", ((t_frect) { | ||||||
|             (RENDER_BASE_WIDTH / 2) - (320 / 2), 64, 320, 128 })); |             (RENDER_BASE_WIDTH / 2) - (320 / 2), 64, 320, 128 })); | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     /* draw the tick count as an example of dynamic text */ | ||||||
|  |     size_t text_str_len = snprintf(NULL, 0, "%ld", state->ctx->tick_count) + 1; | ||||||
|  |     char *text_str = cmalloc(text_str_len); | ||||||
|  |     snprintf(text_str, text_str_len, "%ld", state->ctx->tick_count); | ||||||
|  |  | ||||||
|  |     const char *font = "fonts/kenney-pixel.ttf"; | ||||||
|  |     int text_h = 32; | ||||||
|  |     int text_w = get_text_width(text_str, text_h, font); | ||||||
|  |  | ||||||
|  |     push_rectangle( | ||||||
|  |         (t_frect) { | ||||||
|  |             .x = 0, | ||||||
|  |             .y = 0, | ||||||
|  |             .w = (float)text_w, | ||||||
|  |             .h = (float)text_h, | ||||||
|  |         }, | ||||||
|  |         (t_color) { 0, 0, 0, 255 } | ||||||
|  |     ); | ||||||
|  |     push_text( | ||||||
|  |         text_str, | ||||||
|  |         (t_fvec2){ 0, 0 }, | ||||||
|  |         text_h, | ||||||
|  |         (t_color) { 255, 255, 255, 255 }, | ||||||
|  |         font | ||||||
|  |     ); | ||||||
|  |     free(text_str); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								data/fonts/kenney-pixel.ttf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/fonts/kenney-pixel.ttf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,6 +1,10 @@ | |||||||
| #ifndef CONFIG_H | #ifndef CONFIG_H | ||||||
| #define CONFIG_H | #define CONFIG_H | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <glad/glad.h> | ||||||
|  |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * this file is for configuration values which are to be set at |  * this file is for configuration values which are to be set at | ||||||
|  * compile time.  generally speaking, it's for things that would be unwise to |  * compile time.  generally speaking, it's for things that would be unwise to | ||||||
| @@ -28,6 +32,10 @@ | |||||||
| #define AUDIO_FREQUENCY 48000 | #define AUDIO_FREQUENCY 48000 | ||||||
| #define AUDIO_N_CHANNELS 2 | #define AUDIO_N_CHANNELS 2 | ||||||
|  |  | ||||||
|  | #define TEXT_FONT_TEXTURE_SIZE 1024 | ||||||
|  | #define TEXT_FONT_OVERSAMPLING 4 | ||||||
|  | #define TEXT_FONT_FILTERING GL_LINEAR | ||||||
|  |  | ||||||
| /* 1024 * 1024 */ | /* 1024 * 1024 */ | ||||||
| /* #define UMKA_STACK_SIZE 1048576 */ | /* #define UMKA_STACK_SIZE 1048576 */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| #define CONTEXT_H | #define CONTEXT_H | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "rendering/internal_api.h" | ||||||
| #include "textures/internal_api.h" | #include "textures/internal_api.h" | ||||||
| #include "input.h" | #include "input.h" | ||||||
|  |  | ||||||
| @@ -21,6 +22,7 @@ typedef struct context { | |||||||
|  |  | ||||||
|     struct primitive_2d *render_queue_2d; |     struct primitive_2d *render_queue_2d; | ||||||
|     struct mesh_batch_item *uncolored_mesh_batches; |     struct mesh_batch_item *uncolored_mesh_batches; | ||||||
|  |     struct text_cache text_cache; | ||||||
|  |  | ||||||
|     struct audio_channel_item *audio_channels; |     struct audio_channel_item *audio_channels; | ||||||
|     SDL_AudioDeviceID audio_device; |     SDL_AudioDeviceID audio_device; | ||||||
|   | |||||||
| @@ -356,12 +356,14 @@ static bool initialize(void) { | |||||||
|     /* rendering */ |     /* rendering */ | ||||||
|     /* these are dynamic arrays and will be allocated lazily by stb_ds */ |     /* these are dynamic arrays and will be allocated lazily by stb_ds */ | ||||||
|     ctx.render_queue_2d = NULL; |     ctx.render_queue_2d = NULL; | ||||||
|  |     ctx.uncolored_mesh_batches = NULL; | ||||||
|  |  | ||||||
|     textures_cache_init(&ctx.texture_cache, ctx.window); |     textures_cache_init(&ctx.texture_cache, ctx.window); | ||||||
|     if (TTF_Init() < 0) { |     if (TTF_Init() < 0) { | ||||||
|         CRY_SDL("SDL_ttf initialization failed."); |         CRY_SDL("SDL_ttf initialization failed."); | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |     text_cache_init(&ctx.text_cache); | ||||||
|  |  | ||||||
|     /* input */ |     /* input */ | ||||||
|     input_state_init(&ctx.input); |     input_state_init(&ctx.input); | ||||||
| @@ -391,7 +393,15 @@ static void clean_up(void) { | |||||||
|  |  | ||||||
|     input_state_deinit(&ctx.input); |     input_state_deinit(&ctx.input); | ||||||
|  |  | ||||||
|  |     /* if you're gonna remove this, it's also being done in rendering.c */ | ||||||
|  |     for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { | ||||||
|  |         if (ctx.render_queue_2d[i].type == PRIMITIVE_2D_TEXT) { | ||||||
|  |             free(ctx.render_queue_2d[i].text.text); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     arrfree(ctx.render_queue_2d); |     arrfree(ctx.render_queue_2d); | ||||||
|  |  | ||||||
|  |     text_cache_deinit(&ctx.text_cache); | ||||||
|     textures_cache_deinit(&ctx.texture_cache); |     textures_cache_deinit(&ctx.texture_cache); | ||||||
|  |  | ||||||
|     SDL_DestroyMutex(game_object_mutex); |     SDL_DestroyMutex(game_object_mutex); | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| #include "rendering/sprites.h" | #include "rendering/sprites.h" | ||||||
| #include "rendering/triangles.h" | #include "rendering/triangles.h" | ||||||
| #include "rendering/circles.h" | #include "rendering/circles.h" | ||||||
|  | #include "rendering/text.h" | ||||||
| #include "textures/internal_api.h" | #include "textures/internal_api.h" | ||||||
| #include "townengine/context.h" | #include "townengine/context.h" | ||||||
|  |  | ||||||
| @@ -19,6 +20,14 @@ static t_matrix4 camera_look_at_matrix; | |||||||
|  |  | ||||||
|  |  | ||||||
| void render_queue_clear(void) { | void render_queue_clear(void) { | ||||||
|  |     /* this doesn't even _deserve_ a TODO */ | ||||||
|  |     /* if you're gonna remove it, this is also being done in main.c */ | ||||||
|  |     for (size_t i = 0; i < arrlenu(ctx.render_queue_2d); ++i) { | ||||||
|  |         if (ctx.render_queue_2d[i].type == PRIMITIVE_2D_TEXT) { | ||||||
|  |             free(ctx.render_queue_2d[i].text.text); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /* since i don't intend to free the queues, */ |     /* since i don't intend to free the queues, */ | ||||||
|     /* it's faster and simpler to just "start over" */ |     /* it's faster and simpler to just "start over" */ | ||||||
|     /* and start overwriting the existing data */ |     /* and start overwriting the existing data */ | ||||||
| @@ -103,6 +112,9 @@ static void render_2d(void) { | |||||||
|             case PRIMITIVE_2D_CIRCLE: |             case PRIMITIVE_2D_CIRCLE: | ||||||
|                 render_circle(¤t->circle); |                 render_circle(¤t->circle); | ||||||
|                 break; |                 break; | ||||||
|  |             case PRIMITIVE_2D_TEXT: | ||||||
|  |                 render_text(¤t->text); | ||||||
|  |                 break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,6 +34,11 @@ void push_rectangle(t_frect rect, t_color color); | |||||||
| /* pushes a filled circle onto the circle render queue */ | /* pushes a filled circle onto the circle render queue */ | ||||||
| void push_circle(t_fvec2 position, float radius, t_color color); | void push_circle(t_fvec2 position, float radius, t_color color); | ||||||
|  |  | ||||||
|  | void text_cache_init(struct text_cache *cache); | ||||||
|  | void text_cache_deinit(struct text_cache *cache); | ||||||
|  | void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path); | ||||||
|  | int get_text_width(char *string, int height_px, const char *font_path); | ||||||
|  |  | ||||||
| /* pushes a textured 3d triangle onto the render queue */ | /* pushes a textured 3d triangle onto the render queue */ | ||||||
| /* vertices are in absolute coordinates, relative to world origin */ | /* vertices are in absolute coordinates, relative to world origin */ | ||||||
| /* texture coordinates are in pixels */ | /* texture coordinates are in pixels */ | ||||||
|   | |||||||
| @@ -34,10 +34,19 @@ struct circle_primitive { | |||||||
|     t_fvec2 position; |     t_fvec2 position; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct text_primitive { | ||||||
|  |     t_color color; | ||||||
|  |     t_fvec2 position; | ||||||
|  |     char *text; | ||||||
|  |     const char *font; | ||||||
|  |     int height_px; | ||||||
|  | }; | ||||||
|  |  | ||||||
| enum primitive_2d_type { | enum primitive_2d_type { | ||||||
|     PRIMITIVE_2D_SPRITE, |     PRIMITIVE_2D_SPRITE, | ||||||
|     PRIMITIVE_2D_RECT, |     PRIMITIVE_2D_RECT, | ||||||
|     PRIMITIVE_2D_CIRCLE, |     PRIMITIVE_2D_CIRCLE, | ||||||
|  |     PRIMITIVE_2D_TEXT, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct primitive_2d { | struct primitive_2d { | ||||||
| @@ -47,6 +56,7 @@ struct primitive_2d { | |||||||
|         struct sprite_primitive sprite; |         struct sprite_primitive sprite; | ||||||
|         struct rect_primitive rect; |         struct rect_primitive rect; | ||||||
|         struct circle_primitive circle; |         struct circle_primitive circle; | ||||||
|  |         struct text_primitive text; | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -85,6 +95,10 @@ struct mesh_batch_item { | |||||||
|     struct mesh_batch value; |     struct mesh_batch value; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct text_cache { | ||||||
|  |     struct font_data **data; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* renders the background, then the primitives in all render queues */ | /* renders the background, then the primitives in all render queues */ | ||||||
| void render(void); | void render(void); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										248
									
								
								townengine/rendering/text.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								townengine/rendering/text.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | |||||||
|  | /* a rendering.c mixin */ | ||||||
|  | #ifndef TEXT_H | ||||||
|  | #define TEXT_H | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "../util.h" | ||||||
|  | #include "townengine/config.h" | ||||||
|  | #include "townengine/context.h" | ||||||
|  |  | ||||||
|  | #include <glad/glad.h> | ||||||
|  | #include <stb_truetype.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define ASCII_START 32 | ||||||
|  | #define ASCII_END 128 | ||||||
|  | #define NUM_DISPLAY_ASCII ((ASCII_END - ASCII_START) + 1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct font_data { | ||||||
|  |     stbtt_packedchar char_data[NUM_DISPLAY_ASCII]; | ||||||
|  |     stbtt_fontinfo info; | ||||||
|  |  | ||||||
|  |     const char *file_path; | ||||||
|  |     unsigned char *file_bytes; | ||||||
|  |     size_t file_bytes_len; | ||||||
|  |  | ||||||
|  |     GLuint texture; | ||||||
|  |  | ||||||
|  |     int height_px; | ||||||
|  |     float scale_factor; | ||||||
|  |     int ascent; | ||||||
|  |     int descent; | ||||||
|  |     int line_gap; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static struct font_data *text_load_font_data(const char *path, int height_px) { | ||||||
|  |     struct font_data *font_data = ccalloc(1, sizeof *font_data); | ||||||
|  |     font_data->file_path = path; | ||||||
|  |     font_data->height_px = height_px; | ||||||
|  |  | ||||||
|  |     unsigned char* bitmap = ccalloc(TEXT_FONT_TEXTURE_SIZE * TEXT_FONT_TEXTURE_SIZE, 1); | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         unsigned char *buf = NULL;  | ||||||
|  |         int64_t buf_len = file_to_bytes(path, &buf); | ||||||
|  |         stbtt_InitFont(&font_data->info, buf, stbtt_GetFontOffsetForIndex(buf, 0)); | ||||||
|  |          | ||||||
|  |         /* might as well get these now, for later */ | ||||||
|  |         font_data->file_bytes = buf; | ||||||
|  |         font_data->file_bytes_len = buf_len; | ||||||
|  |         font_data->scale_factor = stbtt_ScaleForPixelHeight(&font_data->info, (float)height_px); | ||||||
|  |         stbtt_GetFontVMetrics( | ||||||
|  |             &font_data->info, | ||||||
|  |             &font_data->ascent, | ||||||
|  |             &font_data->descent, | ||||||
|  |             &font_data->line_gap | ||||||
|  |         ); | ||||||
|  |         font_data->ascent = (int)((float)font_data->ascent * font_data->scale_factor); | ||||||
|  |         font_data->descent = (int)((float)font_data->descent * font_data->scale_factor); | ||||||
|  |         font_data->line_gap = (int)((float)font_data->line_gap * font_data->scale_factor); | ||||||
|  |  | ||||||
|  |         stbtt_pack_context pctx; | ||||||
|  |         stbtt_PackBegin(&pctx, bitmap, TEXT_FONT_TEXTURE_SIZE, TEXT_FONT_TEXTURE_SIZE, 0, 1, NULL); | ||||||
|  |         stbtt_PackSetOversampling(&pctx, TEXT_FONT_OVERSAMPLING, TEXT_FONT_OVERSAMPLING); | ||||||
|  |         stbtt_PackFontRange(&pctx, buf, 0, (float)height_px, ASCII_START, NUM_DISPLAY_ASCII, font_data->char_data); | ||||||
|  |         stbtt_PackEnd(&pctx); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     glGenTextures(1, &font_data->texture); | ||||||
|  |     glBindTexture(GL_TEXTURE_2D, font_data->texture); | ||||||
|  |     glTexImage2D( | ||||||
|  |         GL_TEXTURE_2D, | ||||||
|  |         0, | ||||||
|  |         GL_ALPHA, | ||||||
|  |         TEXT_FONT_TEXTURE_SIZE, | ||||||
|  |         TEXT_FONT_TEXTURE_SIZE, | ||||||
|  |         0, | ||||||
|  |         GL_ALPHA, | ||||||
|  |         GL_UNSIGNED_BYTE, | ||||||
|  |         bitmap | ||||||
|  |     ); | ||||||
|  |     free(bitmap); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TEXT_FONT_FILTERING); | ||||||
|  |     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TEXT_FONT_FILTERING); | ||||||
|  |     glBindTexture(GL_TEXTURE_2D, 0); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     return font_data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void text_destroy_font_data(struct font_data *font_data) { | ||||||
|  |     free(font_data->file_bytes); | ||||||
|  |     glDeleteTextures(1, &font_data->texture); | ||||||
|  |     free(font_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void text_draw_with(struct font_data* font_data, char* text, t_fvec2 position, t_color color) { | ||||||
|  |     glBindTexture(GL_TEXTURE_2D, font_data->texture); | ||||||
|  |  | ||||||
|  |     glEnable(GL_BLEND); | ||||||
|  |     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||||
|  |     glDepthFunc(GL_ALWAYS); | ||||||
|  |     glDepthMask(GL_FALSE); | ||||||
|  |     glDisable(GL_ALPHA_TEST); | ||||||
|  |  | ||||||
|  |     glColor4ub(color.r, color.g, color.b, color.a); | ||||||
|  |  | ||||||
|  |     glBegin(GL_QUADS); | ||||||
|  |     for (const char *p = text; *p != '\0'; ++p) { | ||||||
|  |         const char c = *p; | ||||||
|  |  | ||||||
|  |         /* outside the range of what we want to display */ | ||||||
|  |         //if (c < ASCII_START || c > ASCII_END) | ||||||
|  |         if (c < ASCII_START) | ||||||
|  |             continue; | ||||||
|  |  | ||||||
|  |         /* stb_truetype.h conveniently provides everything we need to draw here! */ | ||||||
|  |         stbtt_aligned_quad quad; | ||||||
|  |         stbtt_GetPackedQuad( | ||||||
|  |             font_data->char_data, | ||||||
|  |             TEXT_FONT_TEXTURE_SIZE, | ||||||
|  |             TEXT_FONT_TEXTURE_SIZE, | ||||||
|  |             c - ASCII_START, | ||||||
|  |             &position.x, | ||||||
|  |             &position.y, | ||||||
|  |             &quad, | ||||||
|  |             true | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         /* have to do this so the "origin" is at the top left */ | ||||||
|  |         /* maybe there's a better way, or maybe this isn't a good idea... */ | ||||||
|  |         /* who knows */ | ||||||
|  |         quad.y0 += (float)font_data->ascent; | ||||||
|  |         quad.y1 += (float)font_data->ascent; | ||||||
|  |  | ||||||
|  |         /* TODO: you know... */ | ||||||
|  |         glTexCoord2f(quad.s0, quad.t0); | ||||||
|  |         glVertex2f(quad.x0, quad.y0); | ||||||
|  |         glTexCoord2f(quad.s1, quad.t0); | ||||||
|  |         glVertex2f(quad.x1, quad.y0); | ||||||
|  |         glTexCoord2f(quad.s1, quad.t1); | ||||||
|  |         glVertex2f(quad.x1, quad.y1); | ||||||
|  |         glTexCoord2f(quad.s0, quad.t1); | ||||||
|  |         glVertex2f(quad.x0, quad.y1); | ||||||
|  |     } | ||||||
|  |     glEnd(); | ||||||
|  |  | ||||||
|  |     glColor4ub(255, 255, 255, 255); | ||||||
|  |     glBindTexture(GL_TEXTURE_2D, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void ensure_font_cache(const char *font_path, int height_px) { | ||||||
|  |     /* HACK: stupid, bad, don't do this */ | ||||||
|  |     bool is_cached = false; | ||||||
|  |     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||||
|  |         struct font_data *font_data = ctx.text_cache.data[i]; | ||||||
|  |         if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) { | ||||||
|  |             is_cached = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (!is_cached) { | ||||||
|  |         struct font_data *new_font_data = text_load_font_data(font_path, height_px); | ||||||
|  |         arrput(ctx.text_cache.data, new_font_data); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static struct font_data *get_font_data(const char *font_path, int height_px) { | ||||||
|  |     struct font_data *font_data = NULL; | ||||||
|  |     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||||
|  |         font_data = ctx.text_cache.data[i]; | ||||||
|  |         if ((strcmp(font_path, font_data->file_path) == 0) && height_px == font_data->height_px) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return font_data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void render_text(const struct text_primitive *text) { | ||||||
|  |     struct font_data *font_data = get_font_data(text->font, text->height_px); | ||||||
|  |     text_draw_with(font_data, text->text, text->position, text->color); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void text_cache_init(struct text_cache *cache) { | ||||||
|  |     arrsetlen(cache->data, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void text_cache_deinit(struct text_cache *cache) { | ||||||
|  |     for (size_t i = 0; i < arrlenu(ctx.text_cache.data); ++i) { | ||||||
|  |         text_destroy_font_data(ctx.text_cache.data[i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     arrfree(cache->data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path) { | ||||||
|  |     ensure_font_cache(font_path, height_px); | ||||||
|  |  | ||||||
|  |     /* the string might not be around by the time it's used, so copy it */ | ||||||
|  |     /* TODO: arena */ | ||||||
|  |     /* NOTE: can we trust strlen? */ | ||||||
|  |     char *dup_string = cmalloc(strlen(string) + 1); | ||||||
|  |     strcpy(dup_string, string); | ||||||
|  |  | ||||||
|  |     struct text_primitive text = { | ||||||
|  |         .color = color, | ||||||
|  |         .position = position, | ||||||
|  |         .text = dup_string, | ||||||
|  |         .font = font_path, | ||||||
|  |         .height_px = height_px, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct primitive_2d primitive = { | ||||||
|  |         .type = PRIMITIVE_2D_TEXT, | ||||||
|  |         .text = text, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     arrput(ctx.render_queue_2d, primitive); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int get_text_width(char *string, int height_px, const char *font_path) { | ||||||
|  |     ensure_font_cache(font_path, height_px); | ||||||
|  |     struct font_data *font_data = get_font_data(font_path, height_px); | ||||||
|  |  | ||||||
|  |     int length = 0; | ||||||
|  |     for (const char *p = string; *p != '\0'; ++p) { | ||||||
|  |         const char c = *p; | ||||||
|  |         int advance_width = 0; | ||||||
|  |         int left_side_bearing = 0; | ||||||
|  |         stbtt_GetCodepointHMetrics(&font_data->info, (int)c, &advance_width, &left_side_bearing); | ||||||
|  |         length += advance_width; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return (int)((float)length * font_data->scale_factor); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| #include "text.h" |  | ||||||
|  |  | ||||||
| #include "SDL.h" |  | ||||||
| #include "SDL_ttf.h" |  | ||||||
|  |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| #ifndef TEXT_H |  | ||||||
| #define TEXT_H |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #include "util.h" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct text { |  | ||||||
|     char *text; |  | ||||||
|     t_color color; |  | ||||||
|     int ptsize; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct text_cache_item { |  | ||||||
|     char *key; |  | ||||||
|     struct text *value; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct text_cache { |  | ||||||
|     struct text_cache_item *hash; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -5,12 +5,12 @@ | |||||||
| #include <physfsrwops.h> | #include <physfsrwops.h> | ||||||
| #define STB_DS_IMPLEMENTATION | #define STB_DS_IMPLEMENTATION | ||||||
| #define STBDS_ASSERT SDL_assert | #define STBDS_ASSERT SDL_assert | ||||||
| #define STBDS_REALLOC(c,p,s) crealloc(p, s) |  | ||||||
| #define STBDS_FREE(c,p) free(p) |  | ||||||
| #include <stb_ds.h> | #include <stb_ds.h> | ||||||
| #define STB_RECT_PACK_IMPLEMENTATION | #define STB_RECT_PACK_IMPLEMENTATION | ||||||
| #define STBRP_ASSERT SDL_assert | #define STBRP_ASSERT SDL_assert | ||||||
| #include <stb_rect_pack.h> | #include <stb_rect_pack.h> | ||||||
|  | #define STB_TRUETYPE_IMPLEMENTATION | ||||||
|  | #include <stb_truetype.h> | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user