From 6d385e674fac76cb443d84210269f588d2ea1155 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Tue, 23 Jul 2019 16:38:04 +0100 Subject: [PATCH] Font refactor part 1: Software With this, font blitting is handled by the rendering backend, paving the way for hardware-accelerated font drawing in the accelerated backends. --- src/Backends/Rendering.h | 14 +++- src/Backends/Rendering/Software.cpp | 116 ++++++++++++++++++++++++++-- src/Draw.cpp | 16 ++-- src/Font.cpp | 106 ++++++++----------------- src/Font.h | 4 +- 5 files changed, 167 insertions(+), 89 deletions(-) diff --git a/src/Backends/Rendering.h b/src/Backends/Rendering.h index e3632006..8f2be6e0 100644 --- a/src/Backends/Rendering.h +++ b/src/Backends/Rendering.h @@ -4,9 +4,15 @@ #include "../WindowsWrapper.h" -#include "../Font.h" +enum +{ + FONT_PIXEL_MODE_LCD, + FONT_PIXEL_MODE_GRAY, + FONT_PIXEL_MODE_MONO, +}; typedef struct Backend_Surface Backend_Surface; +typedef struct Backend_Glyph Backend_Glyph; BOOL Backend_Init(SDL_Window *window); void Backend_Deinit(void); @@ -20,7 +26,9 @@ void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, lon void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue); void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue); void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect); -void Backend_DrawText(Backend_Surface *surface, FontObject *font, int x, int y, const char *text, unsigned long colour); -void Backend_DrawTextToScreen(FontObject *font, int x, int y, const char *text, unsigned long colour); +Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode); +void Backend_UnloadGlyph(Backend_Glyph *glyph); +void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours); +void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours); void Backend_HandleDeviceLoss(void); void Backend_HandleWindowResize(void); diff --git a/src/Backends/Rendering/Software.cpp b/src/Backends/Rendering/Software.cpp index 66d22946..25c69bfa 100644 --- a/src/Backends/Rendering/Software.cpp +++ b/src/Backends/Rendering/Software.cpp @@ -7,7 +7,10 @@ #include "../../WindowsWrapper.h" -#include "../../Font.h" +#undef MIN +#undef MAX +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) typedef struct Backend_Surface { @@ -17,6 +20,16 @@ typedef struct Backend_Surface unsigned int pitch; } Backend_Surface; +typedef struct Backend_Glyph +{ + unsigned char *pixels; + unsigned int width; + unsigned int height; + int pitch; + unsigned short total_greys; + unsigned char pixel_mode; +} Backend_Glyph; + static SDL_Window *window; static SDL_Surface *window_surface; static SDL_Surface *screen_surface; @@ -242,14 +255,107 @@ void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect) Backend_Blit(&framebuffer, rect, surface, rect->left, rect->top, FALSE); } -void Backend_DrawText(Backend_Surface *surface, FontObject *font, int x, int y, const char *text, unsigned long colour) +Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode) { - DrawText(font, surface->pixels, surface->pitch, surface->width, surface->height, x, y, colour, text, strlen(text)); + Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph)); + + if (glyph == NULL) + return NULL; + + glyph->pixels = (unsigned char*)malloc(pitch * height); + + if (glyph->pixels == NULL) + { + free(glyph); + return NULL; + } + + memcpy(glyph->pixels, pixels, pitch * height); + + glyph->width = width; + glyph->height = height; + glyph->pitch = pitch; + glyph->total_greys = total_greys; + glyph->pixel_mode = pixel_mode; + + return glyph; } -void Backend_DrawTextToScreen(FontObject *font, int x, int y, const char *text, unsigned long colour) +void Backend_UnloadGlyph(Backend_Glyph *glyph) { - Backend_DrawText(&framebuffer, font, x, y, text, colour); + free(glyph->pixels); + free(glyph); +} + +void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours) +{ + switch (glyph->pixel_mode) + { + case FONT_PIXEL_MODE_LCD: + for (unsigned int iy = MAX(-y, 0); y + iy < MIN(y + glyph->height, surface->height); ++iy) + { + for (unsigned int ix = MAX(-x, 0); x + ix < MIN(x + glyph->width / 3, surface->width); ++ix) + { + const unsigned char *font_pixel = glyph->pixels + iy * glyph->pitch + ix * 3; + + if (font_pixel[0] || font_pixel[1] || font_pixel[2]) + { + unsigned char *bitmap_pixel = surface->pixels + (y + iy) * surface->pitch + (x + ix) * 3; + + for (unsigned int j = 0; j < 3; ++j) + { + const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction + bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + } + } + } + } + + break; + + case FONT_PIXEL_MODE_GRAY: + for (unsigned int iy = MAX(-y, 0); y + iy < MIN(y + glyph->height, surface->height); ++iy) + { + for (unsigned int ix = MAX(-x, 0); x + ix < MIN(x + glyph->width, surface->width); ++ix) + { + const unsigned char font_pixel = glyph->pixels[iy * glyph->pitch + ix]; + + if (font_pixel) + { + const double alpha = pow((double)font_pixel / (glyph->total_greys - 1), 1.0 / 1.8); // Gamma-corrected + + unsigned char *bitmap_pixel = surface->pixels + (y + iy) * surface->pitch + (x + ix) * 3; + + for (unsigned int j = 0; j < 3; ++j) + bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + } + } + } + + break; + + case FONT_PIXEL_MODE_MONO: + for (unsigned int iy = MAX(-y, 0); y + iy < MIN(y + glyph->height, surface->height); ++iy) + { + for (unsigned int ix = MAX(-x, 0); x + ix < MIN(x + glyph->width, surface->width); ++ix) + { + if (glyph->pixels[iy * glyph->pitch + ix]) + { + unsigned char *bitmap_pixel = surface->pixels + (y + iy) * surface->pitch + (x + ix) * 3; + + for (unsigned int j = 0; j < 3; ++j) + bitmap_pixel[j] = colours[j]; + } + } + } + + break; + } +} + +void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours) +{ + Backend_DrawGlyph(&framebuffer, glyph, x, y, colours); } void Backend_HandleDeviceLoss(void) diff --git a/src/Draw.cpp b/src/Draw.cpp index 65a01e24..cfa8889e 100644 --- a/src/Draw.cpp +++ b/src/Draw.cpp @@ -16,12 +16,6 @@ #include "Tags.h" #include "Backends/Rendering.h" -struct SURFACE -{ - BOOL in_use; - Backend_Surface *backend; -}; - SDL_Window *gWindow; static SDL_PixelFormat *rgb24_pixel_format; // Needed because SDL2 is stupid @@ -32,6 +26,12 @@ RECT grcFull = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; int magnification; BOOL fullscreen; +struct SURFACE +{ + BOOL in_use; + Backend_Surface *backend; +}; + SURFACE surf[SURFACE_ID_MAX]; FontObject *gFont; @@ -559,12 +559,12 @@ void InitTextObject(const char *font_name) void PutText(int x, int y, const char *text, unsigned long color) { - Backend_DrawTextToScreen(gFont, x * magnification, y * magnification, text, color); + DrawText(gFont, NULL, x * magnification, y * magnification, color, text, strlen(text)); } void PutText2(int x, int y, const char *text, unsigned long color, Surface_Ids surf_no) { - Backend_DrawText(surf[surf_no].backend, gFont, x * magnification, y * magnification, text, color); + DrawText(gFont, surf[surf_no].backend, x * magnification, y * magnification, color, text, strlen(text)); } void EndTextObject() diff --git a/src/Font.cpp b/src/Font.cpp index d780611f..4487f181 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -12,7 +12,9 @@ #include "WindowsWrapper.h" +#include "Draw.h" #include "File.h" +#include "Backends/Rendering.h" // Cave Story wasn't intended to use font anti-aliasing. It's only because Microsoft enabled it // by default from Windows Vista onwards that the game started using it. @@ -25,19 +27,13 @@ #define DISABLE_FONT_ANTIALIASING #endif -#undef MIN -#undef MAX -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) - typedef struct CachedGlyph { unsigned long unicode_value; - FT_Bitmap bitmap; - unsigned char pixel_mode; int x; int y; int x_advance; + Backend_Glyph *backend; struct CachedGlyph *next; } CachedGlyph; @@ -1737,12 +1733,33 @@ static CachedGlyph* GetGlyphCached(FontObject *font_object, unsigned long unicod #endif glyph->unicode_value = unicode_value; - FT_Bitmap_New(&glyph->bitmap); - FT_Bitmap_Convert(font_object->library, &font_object->face->glyph->bitmap, &glyph->bitmap, 1); - glyph->pixel_mode = font_object->face->glyph->bitmap.pixel_mode; glyph->x = font_object->face->glyph->bitmap_left; glyph->y = (FT_MulFix(font_object->face->ascender, font_object->face->size->metrics.y_scale) - font_object->face->glyph->metrics.horiBearingY + (64 / 2)) / 64; glyph->x_advance = font_object->face->glyph->advance.x / 64; + + FT_Bitmap bitmap; + FT_Bitmap_New(&bitmap); + FT_Bitmap_Convert(font_object->library, &font_object->face->glyph->bitmap, &bitmap, 1); + + unsigned char pixel_mode; + switch (font_object->face->glyph->bitmap.pixel_mode) + { + case FT_PIXEL_MODE_LCD: + pixel_mode = FONT_PIXEL_MODE_LCD; + break; + + case FT_PIXEL_MODE_GRAY: + pixel_mode = FONT_PIXEL_MODE_GRAY; + break; + + case FT_PIXEL_MODE_MONO: + pixel_mode = FONT_PIXEL_MODE_MONO; + break; + } + + glyph->backend = Backend_LoadGlyph(bitmap.buffer, bitmap.width, bitmap.rows, bitmap.pitch, bitmap.num_grays, pixel_mode); + + FT_Bitmap_Done(font_object->library, &bitmap); } return glyph; @@ -1755,7 +1772,7 @@ static void UnloadCachedGlyphs(FontObject *font_object) { CachedGlyph *next_glyph = glyph->next; - FT_Bitmap_Done(font_object->library, &glyph->bitmap); + Backend_UnloadGlyph(glyph->backend); free(glyph); glyph = next_glyph; @@ -1838,7 +1855,7 @@ FontObject* LoadFont(const char *font_filename, unsigned int cell_width, unsigne return font_object; } -void DrawText(FontObject *font_object, unsigned char *bitmap_buffer, size_t bitmap_pitch, int bitmap_width, int bitmap_height, int x, int y, unsigned long colour, const char *string, size_t string_length) +void DrawText(FontObject *font_object, Backend_Surface *surface, int x, int y, unsigned long colour, const char *string, size_t string_length) { if (font_object != NULL) { @@ -1866,67 +1883,12 @@ void DrawText(FontObject *font_object, unsigned char *bitmap_buffer, size_t bitm const int letter_x = x + pen_x + glyph->x; const int letter_y = y + glyph->y; - switch (glyph->pixel_mode) + if (glyph->backend) { - case FT_PIXEL_MODE_LCD: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width / 3, bitmap_width); ++ix) - { - const unsigned char *font_pixel = glyph->bitmap.buffer + iy * glyph->bitmap.pitch + ix * 3; - - if (font_pixel[0] || font_pixel[1] || font_pixel[2]) - { - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; - - for (unsigned int j = 0; j < 3; ++j) - { - const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction - bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending - } - } - } - } - - break; - - case FT_PIXEL_MODE_GRAY: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width, bitmap_width); ++ix) - { - const unsigned char font_pixel = glyph->bitmap.buffer[iy * glyph->bitmap.pitch + ix]; - - if (font_pixel) - { - const double alpha = pow((double)font_pixel / (glyph->bitmap.num_grays - 1), 1.0 / 1.8); // Gamma-corrected - - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; - - for (unsigned int j = 0; j < 3; ++j) - bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending - } - } - } - - break; - - case FT_PIXEL_MODE_MONO: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width, bitmap_width); ++ix) - { - if (glyph->bitmap.buffer[iy * glyph->bitmap.pitch + ix]) - { - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; - - for (unsigned int j = 0; j < 3; ++j) - bitmap_pixel[j] = colours[j]; - } - } - } - - break; + if (surface) + Backend_DrawGlyph(surface, glyph->backend, letter_x, letter_y, colours); + else + Backend_DrawGlyphToScreen(glyph->backend, letter_x, letter_y, colours); } pen_x += glyph->x_advance; diff --git a/src/Font.h b/src/Font.h index 75e2e379..2e56ffe7 100644 --- a/src/Font.h +++ b/src/Font.h @@ -2,9 +2,11 @@ #include +#include "Backends/Rendering.h" + typedef struct FontObject FontObject; 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, unsigned char *bitmap_buffer, size_t bitmap_pitch, int bitmap_width, int bitmap_height, int x, int y, unsigned long colour, const char *string, size_t string_length); +void DrawText(FontObject *font_object, Backend_Surface *surface, int x, int y, unsigned long colour, const char *string, size_t string_length); void UnloadFont(FontObject *font_object);