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:
parent
90f29377fe
commit
6d385e674f
5 changed files with 167 additions and 89 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
16
src/Draw.cpp
16
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()
|
||||
|
|
106
src/Font.cpp
106
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue