#include "font.h" #include #include #include #include #include #include #define FILE_NAME_SIZE 300 void free_sprite_font(sprite_font* font) { free(font->characters); SDL_DestroyTexture(font->spritesheet); memset(font, 0, sizeof(sprite_font)); } int get_string_parameter(const char* line, const char* parameter, const char** start) { *start = strstr(line, parameter); if ((*start) == NULL) return -1; *start += strlen(parameter); if ((*start)[0] != '\"') { return -1; } int length = 0; while ((*start)[length+1] != '\0' && (*start)[length+1] != '\"') length++; (*start)++; return length; } unsigned char parse_character_tag(const char* character_tag) { if (strncmp(character_tag, "space", 5) == 0) return ' '; return character_tag[0]; } void extract_dir_name(const char* filename, char dir_name_buffer[FILE_NAME_SIZE]) { strcpy(dir_name_buffer, ""); const char* head = filename; const char* next; while ((next = strchr(head+1, '/')) != NULL) { head=next; } int dir_name_length = SDL_min(head - filename, FILE_NAME_SIZE-1); strncpy(dir_name_buffer, filename, dir_name_length); dir_name_buffer[dir_name_length] = '\0'; } bool get_int_parameter(const char* line, const char* parameter, int* value) { const char* start = strstr(line, parameter); if (start == NULL) { return false; } start += strlen(parameter); int c = 0; bool negative = false; if (start[0] == '-') { negative = true; start++; } for(int i = 0; start[i] >= '0' && start[i] <= '9'; i++) { c *= 10; c += start[i] - '0'; } *value = negative ? -c : c; return true; } bool load_sprite_font(SDL_Renderer* renderer, const char* filename, sprite_font* font) { if (font->characters || font->spritesheet) { SDL_Log("Trying to reallocate font causing memory leak, exitting!"); exit(EXIT_FAILURE); } FILE* f = fopen(filename, "r"); if (f == NULL) { SDL_Log("Failed to open sprite font file %s, error: %s!", filename, strerror(errno) ); return false; } char line[PARSER_LINE_SIZE]; if (fgets(line, sizeof(line), f) == NULL || strncmp(line, "info", 4) != 0) { SDL_Log("File %s is not a valid font file!", filename); fclose(f); return false; } char font_name[FONT_NAME_SIZE]; const char* font_name_start; int font_name_length = SDL_min(get_string_parameter(line, "face=", &font_name_start), FONT_NAME_SIZE-1 ); strncpy(font_name, font_name_start, font_name_length); font_name[font_name_length] = '\0'; int char_size = 0, char_count = 0; font_char* characters_unarranged = NULL; unsigned char smallest_char = UCHAR_MAX; unsigned char largest_char = 0; char sprite_file_name[FILE_NAME_SIZE] = ""; while (fgets(line, PARSER_LINE_SIZE, f) != NULL) { font_char* temp; if (strncmp(line, "chars", 5) == 0) { int total_char_count; if(!get_int_parameter(line, "count=", &total_char_count)) continue; char_size = total_char_count; temp = realloc(characters_unarranged, char_size * sizeof(font_char)); if (temp == NULL) { SDL_Log("Failed to allocate space while loading font %s, error %s", filename, strerror(errno)); if (characters_unarranged != NULL) { free(characters_unarranged); } fclose(f); return false; } characters_unarranged = temp; } //Only one page supported else if (strcmp(sprite_file_name, "") == 0 && strncmp(line, "page", 4) == 0) { const char* sprite_fname_start; int sprite_fname_length = SDL_min(get_string_parameter(line, "file=", &sprite_fname_start), FONT_NAME_SIZE-1 ); if (sprite_fname_length <= 0) { SDL_Log("Malformed spritesheet file name in font file %s", filename); continue; } strncpy(sprite_file_name, sprite_fname_start, sprite_fname_length); font_name[sprite_fname_length] = '\0'; } else if (strncmp(line, "char", 4) == 0) { font_char new_font_char; new_font_char.active = true; int val; if (!get_int_parameter(line, "x=", &val)) continue; new_font_char.x = val; if (!get_int_parameter(line, "y=", &val)) continue; new_font_char.y = val; if (!get_int_parameter(line, "width=", &val)) continue; new_font_char.width = val; if (!get_int_parameter(line, "height=", &val)) continue; new_font_char.height = val; if (!get_int_parameter(line, "xoffset=", &val)) continue; new_font_char.x_offset = val; if (!get_int_parameter(line, "yoffset=", &val)) continue; new_font_char.y_offset = val; if (!get_int_parameter(line, "xadvance=", &val)) continue; new_font_char.x_advance = val; const char* character_tag; if (get_string_parameter(line, "letter=", &character_tag) <= 0) continue; new_font_char.character = parse_character_tag(character_tag); if (new_font_char.character < smallest_char) { smallest_char = new_font_char.character; } if (new_font_char.character > largest_char) { largest_char = new_font_char.character; } if (char_count >= char_size) { char_size = char_count+1; temp = realloc(characters_unarranged, char_size * sizeof(font_char)); if (temp == NULL) { SDL_Log("Failed to allocate space while loading font %s, error %s", filename, strerror(errno)); if (characters_unarranged != NULL) { free(characters_unarranged); } fclose(f); return false; } characters_unarranged = temp; } characters_unarranged[char_count] = new_font_char; char_count++; } } if (char_count <= 0) { SDL_Log("Font %s defines no characters!", filename); if (characters_unarranged != NULL) { free(characters_unarranged); } fclose(f); return false; } if (strcmp(sprite_file_name, "") == 0) { SDL_Log("Font file %s has no spritesheet defined!", filename ); if (characters_unarranged != NULL) { free(characters_unarranged); } fclose(f); return false; } char font_dir_name[FILE_NAME_SIZE]; extract_dir_name(filename, font_dir_name); char font_sprite_fname_full[FILE_NAME_SIZE*2]; snprintf(font_sprite_fname_full, FILE_NAME_SIZE*2, "%s/%s", strcmp(font_dir_name, "") == 0 ? "." : font_dir_name, sprite_file_name ); SDL_Texture* font_spritesheet = IMG_LoadTexture(renderer, font_sprite_fname_full); if (font_spritesheet == NULL) { SDL_Log("Failed to load texture %s as spritesheet for font %s!", font_sprite_fname_full, filename); if (characters_unarranged != NULL) { free(characters_unarranged); } fclose(f); return false; } font_char* characters_arranged = malloc((largest_char-smallest_char+1) * sizeof(font_char)); memset(characters_arranged, 0, (largest_char-smallest_char+1) * sizeof(font_char)); if (characters_arranged == NULL) { SDL_Log("Failed to allocate space while loading font %s, error %s", filename, strerror(errno)); if (characters_unarranged != NULL) { free(characters_unarranged); } SDL_DestroyTexture(font_spritesheet); fclose(f); return false; } for (int i = 0; i < char_count; i++) { font_char* f = &characters_unarranged[i]; characters_arranged[f->character - smallest_char] = characters_unarranged[i]; } *font = (sprite_font) { .smallest_character = smallest_char, .largest_character = largest_char, .characters = characters_arranged, .spritesheet = font_spritesheet }; strncpy(font->name, font_name, FONT_NAME_SIZE-1); font_name[FONT_NAME_SIZE-1] = '\0'; free(characters_unarranged); fclose(f); return true; } void measure_text ( sprite_font* font, const unsigned char* text, float scale, float* width, float* height ) { *width = 0; *height = 0; while (*text) { const font_char* fc = &font->characters[*text-font->smallest_character]; if (!fc->active) { text++; continue; } *width += fc->x_advance * scale; *height = SDL_max(*height, (fc->y_offset+fc->height) * scale); text++; } } void draw_text_with_spritefont_extended ( SDL_Renderer* renderer, sprite_font* font, const unsigned char* text, float x_position, float y_position, float scale ) { float text_column_length, text_column_height; measure_text(font, text, scale, &text_column_length, &text_column_height); x_position -= text_column_length / 2; y_position -= text_column_height / 2; while (*text) { const font_char* fc = &font->characters[*text-font->smallest_character]; if (!fc->active) { text++; continue; } SDL_RenderTexture(renderer, font->spritesheet, &(SDL_FRect){ .x = fc->x, .y = fc->y, .w = fc->width, .h = fc->height }, &(SDL_FRect){ .x = x_position + fc->x_offset*scale, .y = y_position + fc->y_offset * scale, .w = fc->width * scale, .h = fc->height * scale }); x_position += fc->x_advance * scale; text++; } } void draw_text_with_spritefont ( SDL_Renderer* renderer, sprite_font* font, const unsigned char* text, float x_position, float y_position ) { draw_text_with_spritefont_extended(renderer, font, text, x_position, y_position, 1.0 ); }