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.
This commit is contained in:
Clownacy 2019-07-23 16:38:04 +01:00
parent 90f29377fe
commit 6d385e674f
5 changed files with 167 additions and 89 deletions

View file

@ -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);

View file

@ -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)

View file

@ -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()

View file

@ -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;

View file

@ -2,9 +2,11 @@
#include <stddef.h>
#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);