Big scary messy unfinished font overhaul

This is currently hardcoded to 640x480 English builds.

What I've done is temporarily gutted the FreeType2 font renderer, and
instead introduced a system where the font is read from a
pre-rendered font atlas. This allows me to get 100% accurate font
rendering to Windows XP... because I literally created these atlases
with Windows XP.

So not only does this eliminate the issue of FreeType producing
misshapen glyphs, but it also works around the copyright issue
regarding bundling the Courier New and MS Gothic fonts with CSE2.

You see, font files can be copyrighted like any old software, however
typefaces have a lot less protection - they're basically fair-game.
IANAL, of course - look it up yourself.

I don't really want to throw away the FreeType font system since I
spent a ton of time working on it, it allows you to use other font
files, and I'll probably wind up needing it for CSE2EX. I guess I'll
just have to find some way to either have the two systems coexist, or
make it so one or the other can be selected at compile-time.
This commit is contained in:
Clownacy 2020-09-16 20:42:11 +01:00
parent febeb1b692
commit fe76fe6dea
16 changed files with 168 additions and 26 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

View file

@ -25,7 +25,7 @@ void RenderBackend_Blit(RenderBackend_Surface *source_surface, const RenderBacke
void RenderBackend_ColourFill(RenderBackend_Surface *surface, const RenderBackend_Rect *rect, unsigned char red, unsigned char green, unsigned char blue);
RenderBackend_GlyphAtlas* RenderBackend_CreateGlyphAtlas(size_t width, size_t height);
void RenderBackend_DestroyGlyphAtlas(RenderBackend_GlyphAtlas *atlas);
void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height);
void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height, size_t pitch);
void RenderBackend_PrepareToDrawGlyphs(RenderBackend_GlyphAtlas *atlas, RenderBackend_Surface *destination_surface, unsigned char red, unsigned char green, unsigned char blue);
void RenderBackend_DrawGlyph(long x, long y, size_t glyph_x, size_t glyph_y, size_t glyph_width, size_t glyph_height);
void RenderBackend_HandleRenderTargetLoss(void);

View file

@ -341,22 +341,26 @@ void RenderBackend_DestroyGlyphAtlas(RenderBackend_GlyphAtlas *atlas)
free(atlas);
}
void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height)
void RenderBackend_UploadGlyph(RenderBackend_GlyphAtlas *atlas, size_t x, size_t y, const unsigned char *pixels, size_t width, size_t height, size_t pitch)
{
unsigned char *buffer = (unsigned char*)malloc(width * height * 4);
if (buffer != NULL)
{
unsigned char *destination_pointer = buffer;
const unsigned char *source_pointer = pixels;
for (size_t i = 0; i < width * height; ++i)
for (size_t iy = 0; iy < height; ++iy)
{
const unsigned char *source_pointer = &pixels[iy * pitch];
for (size_t ix = 0; ix < width; ++ix)
{
*destination_pointer++ = 0xFF;
*destination_pointer++ = 0xFF;
*destination_pointer++ = 0xFF;
*destination_pointer++ = *source_pointer++;
}
}
SDL_Rect rect;
rect.x = x;

View file

@ -6,16 +6,17 @@
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STBI_ONLY_BMP
#define STBI_ONLY_PNG
#define STBI_NO_LINEAR
#define STBI_NO_STDIO
#include "../external/stb_image.h"
#include "File.h"
unsigned char* DecodeBitmap(const unsigned char *in_buffer, size_t in_buffer_size, size_t *width, size_t *height)
unsigned char* DecodeBitmap(const unsigned char *in_buffer, size_t in_buffer_size, size_t *width, size_t *height, unsigned int bytes_per_pixel)
{
int int_width, int_height;
unsigned char *image_buffer = stbi_load_from_memory(in_buffer, in_buffer_size, &int_width, &int_height, NULL, 3);
unsigned char *image_buffer = stbi_load_from_memory(in_buffer, in_buffer_size, &int_width, &int_height, NULL, bytes_per_pixel);
*width = int_width;
*height = int_height;
@ -23,14 +24,14 @@ unsigned char* DecodeBitmap(const unsigned char *in_buffer, size_t in_buffer_siz
return image_buffer;
}
unsigned char* DecodeBitmapFromFile(const char *path, size_t *width, size_t *height)
unsigned char* DecodeBitmapFromFile(const char *path, size_t *width, size_t *height, unsigned int bytes_per_pixel)
{
size_t file_size;
unsigned char *file_buffer = LoadFileToMemory(path, &file_size);
if (file_buffer != NULL)
{
unsigned char *image_buffer = DecodeBitmap(file_buffer, file_size, width, height);
unsigned char *image_buffer = DecodeBitmap(file_buffer, file_size, width, height, bytes_per_pixel);
free(file_buffer);

View file

@ -2,6 +2,6 @@
#include <stddef.h>
unsigned char* DecodeBitmap(const unsigned char *in_buffer, size_t in_buffer_size, size_t *width, size_t *height);
unsigned char* DecodeBitmapFromFile(const char *path, size_t *width, size_t *height);
unsigned char* DecodeBitmap(const unsigned char *in_buffer, size_t in_buffer_size, size_t *width, size_t *height, unsigned int bytes_per_pixel);
unsigned char* DecodeBitmapFromFile(const char *path, size_t *width, size_t *height, unsigned int bytes_per_pixel);
void FreeBitmap(unsigned char *buffer);

View file

@ -212,7 +212,7 @@ BOOL MakeSurface_Resource(const char *name, SurfaceID surf_no)
return FALSE;
size_t width, height;
unsigned char *image_buffer = DecodeBitmap(data, size, &width, &height);
unsigned char *image_buffer = DecodeBitmap(data, size, &width, &height, 3);
if (image_buffer == NULL)
return FALSE;
@ -271,7 +271,7 @@ BOOL MakeSurface_File(const char *name, SurfaceID surf_no)
}
size_t width, height;
unsigned char *image_buffer = DecodeBitmapFromFile(path.c_str(), &width, &height);
unsigned char *image_buffer = DecodeBitmapFromFile(path.c_str(), &width, &height, 3);
if (image_buffer == NULL)
{
@ -318,7 +318,7 @@ BOOL ReloadBitmap_Resource(const char *name, SurfaceID surf_no)
return FALSE;
size_t width, height;
unsigned char *image_buffer = DecodeBitmap(data, size, &width, &height);
unsigned char *image_buffer = DecodeBitmap(data, size, &width, &height, 3);
if (!ScaleAndUploadSurface(image_buffer, width, height, surf_no))
{
@ -356,7 +356,7 @@ BOOL ReloadBitmap_File(const char *name, SurfaceID surf_no)
}
size_t width, height;
unsigned char *image_buffer = DecodeBitmapFromFile(path.c_str(), &width, &height);
unsigned char *image_buffer = DecodeBitmapFromFile(path.c_str(), &width, &height, 3);
if (image_buffer == NULL)
{
@ -705,7 +705,11 @@ void InitTextObject(const char *name)
#endif
}
font = LoadFont(path.c_str(), width, height);
// font = LoadFont(path.c_str(), width, height);
std::string bitmap_path = gDataPath + "/Font/couriernew2.PNG";
std::string metadata_path = gDataPath + "/Font/couriernew2.dat";
font = LoadBitmapFont(bitmap_path.c_str(), metadata_path.c_str());
}
void PutText(int x, int y, const char *text, unsigned long color)

View file

@ -9,6 +9,7 @@
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include "Bitmap.h"
#include "File.h"
#include "Backends/Rendering.h"
@ -45,9 +46,18 @@ typedef struct Glyph
typedef struct Font
{
unsigned char *image_buffer;
size_t image_buffer_width;
size_t image_buffer_height;
size_t glyph_slot_width;
size_t glyph_slot_height;
size_t total_local_glyphs;
Glyph *local_glyphs;
/*
FT_Library library;
FT_Face face;
unsigned char *data;
*/
Glyph glyphs[TOTAL_GLYPH_SLOTS];
Glyph *glyph_list_head;
RenderBackend_GlyphAtlas *atlas;
@ -989,6 +999,34 @@ static Glyph* GetGlyph(Font *font, unsigned long unicode_value)
// Couldn't find glyph - overwrite the old at the end.
// The one at the end hasn't been used in a while anyway.
for (size_t i = 0; i < font->total_local_glyphs; ++i)
{
if (font->local_glyphs[i].unicode_value == unicode_value)
{
glyph->unicode_value = font->local_glyphs[i].unicode_value;
glyph->width = font->local_glyphs[i].width;
glyph->height = font->local_glyphs[i].height;
glyph->x_offset = font->local_glyphs[i].x_offset;
glyph->y_offset = font->local_glyphs[i].y_offset;
glyph->x_advance = font->local_glyphs[i].x_advance;
RenderBackend_UploadGlyph(font->atlas, glyph->x, glyph->y, &font->image_buffer[font->local_glyphs[i].y * font->image_buffer_width + font->local_glyphs[i].x], glyph->width, glyph->height, font->image_buffer_width);
*glyph_pointer = glyph->next;
glyph->next = font->glyph_list_head;
font->glyph_list_head = glyph;
return glyph;
}
}
/*
unsigned int glyph_index = FT_Get_Char_Index(font->face, unicode_value);
#ifdef ENABLE_FONT_ANTIALIASING
@ -1053,10 +1091,12 @@ static Glyph* GetGlyph(Font *font, unsigned long unicode_value)
FT_Bitmap_Done(font->library, &bitmap);
}
*/
return NULL;
}
/*
Font* LoadFontFromData(const unsigned char *data, size_t data_size, size_t cell_width, size_t cell_height)
{
Font *font = (Font*)malloc(sizeof(Font));
@ -1133,6 +1173,96 @@ Font* LoadFont(const char *font_filename, size_t cell_width, size_t cell_height)
return font;
}
*/
Font* LoadBitmapFont(const char *bitmap_path, const char *metadata_path)
{
Font *font = NULL;
size_t bitmap_width, bitmap_height;
unsigned char *image_buffer = DecodeBitmapFromFile(bitmap_path, &bitmap_width, &bitmap_height, 1);
if (image_buffer != NULL)
{
size_t metadata_size;
unsigned char *metadata_buffer = LoadFileToMemory(metadata_path, &metadata_size);
if (metadata_buffer != NULL)
{
font = (Font*)malloc(sizeof(Font));
if (font != NULL)
{
font->glyph_slot_width = (metadata_buffer[0] << 8) | metadata_buffer[1];
font->glyph_slot_height = (metadata_buffer[2] << 8) | metadata_buffer[3];
font->total_local_glyphs = (metadata_buffer[4] << 8) | metadata_buffer[5];
font->local_glyphs = (Glyph*)malloc(sizeof(Glyph) * font->total_local_glyphs);
if (font->local_glyphs != NULL)
{
for (size_t i = 0; i < font->total_local_glyphs; ++i)
{
font->local_glyphs[i].unicode_value = (metadata_buffer[6 + i * 4 + 0] << 8) | metadata_buffer[6 + i * 4 + 1];
font->local_glyphs[i].x = (i % (bitmap_width / font->glyph_slot_width)) * font->glyph_slot_width;
font->local_glyphs[i].y = (i / (bitmap_width / font->glyph_slot_width)) * font->glyph_slot_height;
font->local_glyphs[i].width = font->glyph_slot_width;
font->local_glyphs[i].height = font->glyph_slot_height;
font->local_glyphs[i].x_offset = 0;
font->local_glyphs[i].y_offset = 0;
font->local_glyphs[i].x_advance = (metadata_buffer[6 + i * 4 + 2] << 8) | metadata_buffer[6 + i * 4 + 3];
font->local_glyphs[i].next = NULL;
}
size_t atlas_entry_width = font->glyph_slot_width;
size_t atlas_entry_height = font->glyph_slot_height;
size_t atlas_columns = ceil(sqrt(atlas_entry_width * atlas_entry_height * TOTAL_GLYPH_SLOTS) / atlas_entry_width);
size_t atlas_rows = (TOTAL_GLYPH_SLOTS + (atlas_columns - 1)) / atlas_columns;
font->atlas_row_length = atlas_columns;
font->atlas = RenderBackend_CreateGlyphAtlas(atlas_columns * atlas_entry_width, atlas_rows * atlas_entry_height);
if (font->atlas != NULL)
{
// Initialise the linked-list
for (size_t i = 0; i < TOTAL_GLYPH_SLOTS; ++i)
{
font->glyphs[i].next = (i == 0) ? NULL : &font->glyphs[i - 1];
font->glyphs[i].x = (i % font->atlas_row_length) * atlas_entry_width;
font->glyphs[i].y = (i / font->atlas_row_length) * atlas_entry_height;
}
font->glyph_list_head = &font->glyphs[TOTAL_GLYPH_SLOTS - 1];
font->image_buffer = image_buffer;
font->image_buffer_width = bitmap_width;
font->image_buffer_height = bitmap_height;
free(metadata_buffer);
return font;
}
}
free(font);
}
free(metadata_buffer);
}
FreeBitmap(image_buffer);
}
return font;
}
void DrawText(Font *font, RenderBackend_Surface *surface, int x, int y, unsigned long colour, const char *string)
{
@ -1176,9 +1306,11 @@ void UnloadFont(Font *font)
{
RenderBackend_DestroyGlyphAtlas(font->atlas);
FT_Done_Face(font->face);
free(font->data);
FT_Done_FreeType(font->library);
FreeBitmap(font->image_buffer);
// FT_Done_Face(font->face);
// free(font->data);
// FT_Done_FreeType(font->library);
free(font);
}
}

View file

@ -6,7 +6,8 @@
typedef struct Font Font;
Font* LoadFontFromData(const unsigned char *data, size_t data_size, size_t cell_width, size_t cell_height);
Font* LoadFont(const char *font_filename, size_t cell_width, size_t cell_height);
//Font* LoadFontFromData(const unsigned char *data, size_t data_size, size_t cell_width, size_t cell_height);
//Font* LoadFont(const char *font_filename, size_t cell_width, size_t cell_height);
Font* LoadBitmapFont(const char *bitmap_path, const char *metadata_path);
void DrawText(Font *font, RenderBackend_Surface *surface, int x, int y, unsigned long colour, const char *string);
void UnloadFont(Font *font);

View file

@ -295,7 +295,7 @@ int main(int argc, char *argv[])
if (window_icon_resource_data != NULL)
{
size_t window_icon_width, window_icon_height;
unsigned char *window_icon_rgb_pixels = DecodeBitmap(window_icon_resource_data, window_icon_resource_size, &window_icon_width, &window_icon_height);
unsigned char *window_icon_rgb_pixels = DecodeBitmap(window_icon_resource_data, window_icon_resource_size, &window_icon_width, &window_icon_height, 3);
if (window_icon_rgb_pixels != NULL)
{
@ -312,7 +312,7 @@ int main(int argc, char *argv[])
if (cursor_resource_data != NULL)
{
size_t cursor_width, cursor_height;
unsigned char *cursor_rgb_pixels = DecodeBitmap(cursor_resource_data, cursor_resource_size, &cursor_width, &cursor_height);
unsigned char *cursor_rgb_pixels = DecodeBitmap(cursor_resource_data, cursor_resource_size, &cursor_width, &cursor_height, 3);
if (cursor_rgb_pixels != NULL)
{