From 90ef30becef2e3ff4bdbaf3f3abd2ad9517d7802 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Tue, 29 Jan 2019 10:18:34 +0000 Subject: [PATCH] Added option for fonts to be loaded from Windows This restores some compatibility with Config.dat's font settings. --- Makefile | 8 +- src/Draw.cpp | 81 ++++++++++++- src/Draw.h | 2 +- src/Font.cpp | 312 +++++++++++++++++++++++++++++---------------------- src/Font.h | 3 +- src/Main.cpp | 2 +- 6 files changed, 264 insertions(+), 144 deletions(-) diff --git a/Makefile b/Makefile index 587c5bec..12698bfd 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,12 @@ ifeq ($(FIX_BUGS), 1) CXXFLAGS += -DFIX_BUGS endif -ifeq ($(CONSOLE), 1) -CXXFLAGS += -mconsole +ifeq ($(WINDOWS), 1) + ifeq ($(CONSOLE), 1) + CXXFLAGS += -mconsole + endif +CXXFLAGS += -DWINDOWS +LIBS += -lkernel32 endif CXXFLAGS += `sdl2-config --cflags` `pkg-config freetype2 --cflags` diff --git a/src/Draw.cpp b/src/Draw.cpp index 40dbbe8e..de06a8ec 100644 --- a/src/Draw.cpp +++ b/src/Draw.cpp @@ -4,6 +4,18 @@ #include #include +#ifdef WINDOWS +#define RECT WINRECT +#define FindResource WinFindResource // All these damn name collisions... +#define DrawText WinDrawText +#define LoadFont WinLoadFont +#include +#undef LoadFont +#undef DrawText +#undef FindResource +#undef RECT +#endif + #include #include #include @@ -388,7 +400,54 @@ void CortBox2(RECT *rect, uint32_t col, int surf_no) SDL_SetRenderTarget(gRenderer, NULL); } -void InitTextObject() +#ifdef WINDOWS +static unsigned char* GetFontFromWindows(size_t *data_size, const char *font_name, unsigned int fontWidth, unsigned int fontHeight) +{ + unsigned char* buffer = NULL; + +#ifdef JAPANESE + const DWORD charset = SHIFTJIS_CHARSET; +#else + const DWORD charset = DEFAULT_CHARSET; +#endif + + HFONT hfont = CreateFontA(fontHeight, fontWidth, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, charset, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, font_name); + + if (hfont == NULL) + hfont = CreateFontA(fontHeight, fontWidth, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, charset, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, NULL); + + if (hfont != NULL) + { + HDC hdc = CreateCompatibleDC(NULL); + + if (hdc != NULL) + { + SelectObject(hdc, hfont); + const DWORD size = GetFontData(hdc, 0, 0, NULL, 0); + + if (size != GDI_ERROR) + { + buffer = new unsigned char[size]; + + if (data_size != NULL) + *data_size = size; + + if (GetFontData(hdc, 0, 0, buffer, size) != size) + { + delete[] buffer; + buffer = NULL; + } + } + + DeleteDC(hdc); + } + } + + return buffer; +} +#endif + +void InitTextObject(const char *font_name) { //Get font size unsigned int fontWidth, fontHeight; @@ -403,6 +462,24 @@ void InitTextObject() fontHeight = 10 * gWindowScale; } +#ifdef WINDOWS + // Actually use the font Config.dat specifies + size_t data_size; + unsigned char *data = GetFontFromWindows(&data_size, font_name, fontWidth, fontHeight); + + if (data != NULL) + { + gFont = LoadFontFromData(data, data_size, fontWidth, fontHeight); + + delete[] data; + + if (gFont) + return; + } +#endif + // Fall back on the built-in fonts + (void)font_name; + //Open Font.ttf char path[PATH_LENGTH]; #ifdef JAPANESE @@ -411,7 +488,7 @@ void InitTextObject() sprintf(path, "%s/cour.ttf", gDataPath); #endif - gFont = LoadFont(fontWidth, fontHeight, path); + gFont = LoadFont(path, fontWidth, fontHeight); } void PutText(int x, int y, const char *text, uint32_t color) diff --git a/src/Draw.h b/src/Draw.h index 580cf37a..9305b816 100644 --- a/src/Draw.h +++ b/src/Draw.h @@ -66,7 +66,7 @@ void PutBitmap4(RECT *rcView, int x, int y, RECT *rect, int surf_no); void Surface2Surface(int x, int y, RECT *rect, int to, int from); void CortBox(RECT *rect, uint32_t col); void CortBox2(RECT *rect, uint32_t col, int surf_no); -void InitTextObject(); +void InitTextObject(const char *font_name); void PutText(int x, int y, const char *text, uint32_t color); void PutText2(int x, int y, const char *text, uint32_t color, int surf_no); void EndTextObject(); diff --git a/src/Font.cpp b/src/Font.cpp index e0875146..fa097b8d 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -2,7 +2,9 @@ #include #include +#include #include +#include #ifdef JAPANESE #include @@ -27,6 +29,7 @@ typedef struct FontObject { FT_Library library; FT_Face face; + unsigned char *data; #ifndef DISABLE_FONT_ANTIALIASING bool lcd_mode; #endif @@ -89,7 +92,7 @@ static unsigned long UTF8ToCode(const unsigned char *string, unsigned int *bytes return charcode; } -FontObject* LoadFont(unsigned int cell_width, unsigned int cell_height, char *font_filename) +FontObject* LoadFontFromData(const unsigned char *data, size_t data_size, unsigned int cell_width, unsigned int cell_height) { FontObject *font_object = (FontObject*)malloc(sizeof(FontObject)); @@ -99,10 +102,19 @@ FontObject* LoadFont(unsigned int cell_width, unsigned int cell_height, char *fo font_object->lcd_mode = FT_Library_SetLcdFilter(font_object->library, FT_LCD_FILTER_DEFAULT) != FT_Err_Unimplemented_Feature; #endif - FT_New_Face(font_object->library, font_filename, 0, &font_object->face); + font_object->data = (unsigned char*)malloc(data_size); + memcpy(font_object->data, data, data_size); + + FT_Error error = FT_New_Memory_Face(font_object->library, font_object->data, data_size, 0, &font_object->face); + + if (error) + { + free(font_object->data); + FT_Done_FreeType(font_object->library); + free(font_object); + return NULL; + } - unsigned int best_cell_width = 0; - unsigned int best_cell_height = 0; unsigned int best_pixel_width = 0; unsigned int best_pixel_height = 0; @@ -120,15 +132,10 @@ FontObject* LoadFont(unsigned int cell_width, unsigned int cell_height, char *fo else { if (current_cell_width <= cell_width) - { best_pixel_width = i; - best_cell_width = current_cell_width; - } + if (current_cell_height <= cell_height) - { best_pixel_height = i; - best_cell_height = current_cell_height; - } } } @@ -145,145 +152,176 @@ FontObject* LoadFont(unsigned int cell_width, unsigned int cell_height, char *fo return font_object; } -void DrawText(FontObject *font_object, SDL_Renderer *renderer, SDL_Texture *texture, int x, int y, unsigned long colour, const char *string, size_t string_length) +FontObject* LoadFont(const char *font_filename, unsigned int cell_width, unsigned int cell_height) { - const unsigned char colours[3] = {(unsigned char)(colour >> 16), (unsigned char)(colour >> 8), (unsigned char)colour}; + FontObject *font_object = NULL; - SDL_Texture *old_render_target = SDL_GetRenderTarget(renderer); - SDL_SetRenderTarget(renderer, texture); + FILE *file = fopen(font_filename, "rb"); - int surface_width, surface_height; - SDL_GetRendererOutputSize(renderer, &surface_width, &surface_height); - - SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormat(0, surface_width, surface_height, 0, SDL_PIXELFORMAT_RGBA32); - SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGBA32, surface->pixels, surface->pitch); - unsigned char (*surface_buffer)[surface->pitch / 4][4] = (unsigned char (*)[surface->pitch / 4][4])surface->pixels; - - FT_Face face = font_object->face; - - unsigned int pen_x = 0; - - const unsigned char *string_pointer = (unsigned char*)string; - const unsigned char *string_end = (unsigned char*)string + string_length; - - while (string_pointer != string_end) + if (file != NULL) { -#ifdef JAPANESE - size_t out_size = 4; - unsigned char out_buffer[4]; // Max UTF-8 length is four bytes - unsigned char *out_pointer = out_buffer; + fseek(file, 0, SEEK_END); + const size_t file_size = ftell(file); + rewind(file); + unsigned char *file_buffer = (unsigned char*)malloc(file_size); + fread(file_buffer, 1, file_size, file); + fclose(file); - size_t in_size = ((*string_pointer >= 0x81 && *string_pointer <= 0x9F) || (*string_pointer >= 0xE0 && *string_pointer <= 0xEF)) ? 2 : 1; - unsigned char in_buffer[2]; - unsigned char *in_pointer = in_buffer; + font_object = LoadFontFromData(file_buffer, file_size, cell_width, cell_height); - for (size_t i = 0; i < in_size; ++i) - in_buffer[i] = string_pointer[i]; - - string_pointer += in_size; - - iconv(font_object->conv, (char**)&in_pointer, &in_size, (char**)&out_pointer, &out_size); - - const unsigned long val = UTF8ToCode(out_buffer, NULL); -#else - unsigned int bytes_read; - const unsigned long val = UTF8ToCode(string_pointer, &bytes_read); - string_pointer += bytes_read; -#endif - - unsigned int glyph_index = FT_Get_Char_Index(face, val); - -#ifndef DISABLE_FONT_ANTIALIASING - FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | (font_object->lcd_mode ? FT_LOAD_TARGET_LCD : 0)); -#else - FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); -#endif - - FT_Bitmap converted; - FT_Bitmap_New(&converted); - FT_Bitmap_Convert(font_object->library, &face->glyph->bitmap, &converted, 1); - - const int letter_x = x + pen_x + face->glyph->bitmap_left; - const int letter_y = y + ((FT_MulFix(face->ascender, face->size->metrics.y_scale) + (64 - 1)) / 64) - (face->glyph->metrics.horiBearingY / 64); - - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + converted.rows, surface_height); ++iy) - { - if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width / 3, surface_width); ++ix) - { - const unsigned char (*font_buffer)[converted.pitch / 3][3] = (unsigned char (*)[converted.pitch / 3][3])converted.buffer; - - const unsigned char *font_pixel = font_buffer[iy][ix]; - unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; - - if (font_pixel[0] || font_pixel[1] || font_pixel[2]) - { - for (unsigned int j = 0; j < 3; ++j) - { - const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction - surface_pixel[j] = (colours[j] * alpha) + (surface_pixel[j] * (1.0 - alpha)); // Alpha blending - } - - surface_pixel[3] = 0xFF; - } - } - } - else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, surface_width); ++ix) - { - unsigned char (*font_buffer)[converted.pitch] = (unsigned char (*)[converted.pitch])converted.buffer; - - const double alpha = pow((double)font_buffer[iy][ix] / (converted.num_grays - 1), 1.0 / 1.8); // Gamma-corrected - - unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; - - if (alpha) - { - for (unsigned int j = 0; j < 3; ++j) - surface_pixel[j] = (colours[j] * alpha) + (surface_pixel[j] * (1.0 - alpha)); // Alpha blending - - surface_pixel[3] = 0xFF; - } - } - } - else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, surface_width); ++ix) - { - unsigned char (*font_buffer)[converted.pitch] = (unsigned char (*)[converted.pitch])converted.buffer; - - unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; - - if (font_buffer[iy][ix]) - { - for (unsigned int j = 0; j < 3; ++j) - surface_pixel[j] = colours[j]; - - surface_pixel[3] = 0xFF; - } - } - } - } - - FT_Bitmap_Done(font_object->library, &converted); - - pen_x += face->glyph->advance.x / 64; + free(file_buffer); } - SDL_Texture *screen_texture = SDL_CreateTextureFromSurface(renderer, surface); - SDL_FreeSurface(surface); - SDL_RenderCopy(renderer, screen_texture, NULL, NULL); - SDL_DestroyTexture(screen_texture); - SDL_SetRenderTarget(renderer, old_render_target); + return font_object; +} + +void DrawText(FontObject *font_object, SDL_Renderer *renderer, SDL_Texture *texture, int x, int y, unsigned long colour, const char *string, size_t string_length) +{ + if (font_object != NULL) + { + const unsigned char colours[3] = {(unsigned char)(colour >> 16), (unsigned char)(colour >> 8), (unsigned char)colour}; + + SDL_Texture *old_render_target = SDL_GetRenderTarget(renderer); + SDL_SetRenderTarget(renderer, texture); + + int surface_width, surface_height; + SDL_GetRendererOutputSize(renderer, &surface_width, &surface_height); + + SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormat(0, surface_width, surface_height, 0, SDL_PIXELFORMAT_RGBA32); + SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGBA32, surface->pixels, surface->pitch); + unsigned char (*surface_buffer)[surface->pitch / 4][4] = (unsigned char (*)[surface->pitch / 4][4])surface->pixels; + + FT_Face face = font_object->face; + + unsigned int pen_x = 0; + + const unsigned char *string_pointer = (unsigned char*)string; + const unsigned char *string_end = (unsigned char*)string + string_length; + + while (string_pointer != string_end) + { +#ifdef JAPANESE + size_t out_size = 4; + unsigned char out_buffer[4]; // Max UTF-8 length is four bytes + unsigned char *out_pointer = out_buffer; + + size_t in_size = ((*string_pointer >= 0x81 && *string_pointer <= 0x9F) || (*string_pointer >= 0xE0 && *string_pointer <= 0xEF)) ? 2 : 1; + unsigned char in_buffer[2]; + unsigned char *in_pointer = in_buffer; + + for (size_t i = 0; i < in_size; ++i) + in_buffer[i] = string_pointer[i]; + + string_pointer += in_size; + + iconv(font_object->conv, (char**)&in_pointer, &in_size, (char**)&out_pointer, &out_size); + + const unsigned long val = UTF8ToCode(out_buffer, NULL); +#else + unsigned int bytes_read; + const unsigned long val = UTF8ToCode(string_pointer, &bytes_read); + string_pointer += bytes_read; +#endif + + unsigned int glyph_index = FT_Get_Char_Index(face, val); + +#ifndef DISABLE_FONT_ANTIALIASING + FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | (font_object->lcd_mode ? FT_LOAD_TARGET_LCD : 0)); +#else + FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); +#endif + + FT_Bitmap converted; + FT_Bitmap_New(&converted); + FT_Bitmap_Convert(font_object->library, &face->glyph->bitmap, &converted, 1); + + const int letter_x = x + pen_x + face->glyph->bitmap_left; + const int letter_y = y + ((FT_MulFix(face->ascender, face->size->metrics.y_scale) + (64 - 1)) / 64) - (face->glyph->metrics.horiBearingY / 64); + + for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + converted.rows, surface_height); ++iy) + { + if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) + { + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width / 3, surface_width); ++ix) + { + const unsigned char (*font_buffer)[converted.pitch / 3][3] = (unsigned char (*)[converted.pitch / 3][3])converted.buffer; + + const unsigned char *font_pixel = font_buffer[iy][ix]; + unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; + + if (font_pixel[0] || font_pixel[1] || font_pixel[2]) + { + for (unsigned int j = 0; j < 3; ++j) + { + const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction + surface_pixel[j] = (colours[j] * alpha) + (surface_pixel[j] * (1.0 - alpha)); // Alpha blending + } + + surface_pixel[3] = 0xFF; + } + } + } + else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) + { + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, surface_width); ++ix) + { + unsigned char (*font_buffer)[converted.pitch] = (unsigned char (*)[converted.pitch])converted.buffer; + + const double alpha = pow((double)font_buffer[iy][ix] / (converted.num_grays - 1), 1.0 / 1.8); // Gamma-corrected + + unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; + + if (alpha) + { + for (unsigned int j = 0; j < 3; ++j) + surface_pixel[j] = (colours[j] * alpha) + (surface_pixel[j] * (1.0 - alpha)); // Alpha blending + + surface_pixel[3] = 0xFF; + } + } + } + else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) + { + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, surface_width); ++ix) + { + unsigned char (*font_buffer)[converted.pitch] = (unsigned char (*)[converted.pitch])converted.buffer; + + unsigned char *surface_pixel = surface_buffer[letter_y + iy][letter_x + ix]; + + if (font_buffer[iy][ix]) + { + for (unsigned int j = 0; j < 3; ++j) + surface_pixel[j] = colours[j]; + + surface_pixel[3] = 0xFF; + } + } + } + } + + FT_Bitmap_Done(font_object->library, &converted); + + pen_x += face->glyph->advance.x / 64; + } + + SDL_Texture *screen_texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_FreeSurface(surface); + SDL_RenderCopy(renderer, screen_texture, NULL, NULL); + SDL_DestroyTexture(screen_texture); + SDL_SetRenderTarget(renderer, old_render_target); + } } void UnloadFont(FontObject *font_object) { + if (font_object != NULL) + { #ifdef JAPANESE - iconv_close(font_object->conv); + iconv_close(font_object->conv); #endif - FT_Done_Face(font_object->face); - FT_Done_FreeType(font_object->library); + FT_Done_Face(font_object->face); + free(font_object->data); + FT_Done_FreeType(font_object->library); + free(font_object); + } } diff --git a/src/Font.h b/src/Font.h index 89a2adb3..fbd7ddc4 100644 --- a/src/Font.h +++ b/src/Font.h @@ -6,6 +6,7 @@ typedef struct FontObject FontObject; -FontObject* LoadFont(unsigned int cell_width, unsigned int cell_height, char *font_filename); +FontObject* LoadFontFromData(const unsigned char *data, size_t data_size, unsigned int cell_width, unsigned int cell_height); +FontObject* LoadFont(const char *font_filename, unsigned int cell_width, unsigned int cell_height); void DrawText(FontObject *font_object, SDL_Renderer *renderer, SDL_Texture *texture, int x, int y, unsigned long colour, const char *string, size_t string_length); void UnloadFont(FontObject *font_object); diff --git a/src/Main.cpp b/src/Main.cpp index 2afe2b38..a47c7bcc 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -284,7 +284,7 @@ int main(int argc, char *argv[]) } //Initialize stuff - InitTextObject(); + InitTextObject(config.font_name); InitTriangleTable(); //Run game code