Split up Drawer2D module into Drawer2D and SystemFonts modules

This commit is contained in:
UnknownShadow200 2023-04-25 16:19:47 +10:00
parent 02e559e520
commit ac7602386e
16 changed files with 944 additions and 933 deletions

View file

@ -97,6 +97,7 @@ add_library(classicube SHARED
../../src/EnvRenderer.c
../../src/Animations.c
../../src/LBackend.c
../../src/SystemFonts.c
)
# add lib dependencies

View file

@ -420,6 +420,7 @@
<ClInclude Include="SelectionBox.h" />
<ClInclude Include="Server.h" />
<ClInclude Include="Stream.h" />
<ClInclude Include="SystemFonts.h" />
<ClInclude Include="TexturePack.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="PackedCol.h" />
@ -501,6 +502,7 @@
<ClCompile Include="Server.c" />
<ClCompile Include="Stream.c" />
<ClCompile Include="String.c" />
<ClCompile Include="SystemFonts.c" />
<ClCompile Include="TexturePack.c" />
<ClCompile Include="Utils.c" />
<ClCompile Include="Vectors.c" />

View file

@ -330,6 +330,9 @@
<ClInclude Include="_GLShared.h">
<Filter>Header Files\Graphics</Filter>
</ClInclude>
<ClInclude Include="SystemFonts.h">
<Filter>Header Files\2D</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="String.c">
@ -590,6 +593,9 @@
<ClCompile Include="GameVersion.c">
<Filter>Source Files\Game</Filter>
</ClCompile>
<ClCompile Include="SystemFonts.c">
<Filter>Source Files\2D</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\misc\CCicon.rc">

View file

@ -14,6 +14,7 @@
#include "Window.h"
#include "Options.h"
#include "TexturePack.h"
#include "SystemFonts.h"
struct _Drawer2DData Drawer2D;
#define Font_IsBitmap(font) (!(font)->handle)
@ -34,16 +35,8 @@ void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, struct FontDesc* font, cc
/*########################################################################################################################*
*-----------------------------------------------------Font functions------------------------------------------------------*
*#########################################################################################################################*/
static char defaultBuffer[STRING_SIZE];
static cc_string font_default = String_FromArray(defaultBuffer);
int Drawer2D_AdjHeight(int point) { return Math_CeilDiv(point * 3, 2); }
void Font_SetDefault(const cc_string* fontName) {
String_Copy(&font_default, fontName);
Event_RaiseVoid(&ChatEvents.FontChanged);
}
/* adjusts height to be closer to system fonts */
static int Drawer2D_AdjHeight(int point) { return Math_CeilDiv(point * 3, 2); }
void Font_MakeBitmapped(struct FontDesc* desc, int size, int flags) {
/* TODO: Scale X and Y independently */
size = Display_ScaleY(size);
@ -61,6 +54,15 @@ void Font_Make(struct FontDesc* desc, int size, int flags) {
}
}
void Font_Free(struct FontDesc* desc) {
struct SysFont* font;
desc->size = 0;
if (Font_IsBitmap(desc)) return;
SysFont_Free(desc);
desc->handle = NULL;
}
static struct Bitmap fontBitmap;
static int tileSize = 8; /* avoid divide by 0 if default.png missing */
/* So really 16 characters per row */
@ -129,11 +131,6 @@ void Font_SetPadding(struct FontDesc* desc, int amount) {
desc->height = desc->size + Display_ScaleY(amount) * 2;
}
/* Measures width of the given text when drawn with the given system font. */
static int Font_SysTextWidth(struct DrawTextArgs* args);
/* Draws the given text with the given system font onto the given bitmap. */
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow);
/*########################################################################################################################*
*---------------------------------------------------Drawing functions-----------------------------------------------------*
@ -383,18 +380,6 @@ char Drawer2D_LastColor(const cc_string* text, int start) {
}
cc_bool Drawer2D_IsWhiteColor(char c) { return c == '\0' || c == 'f' || c == 'F'; }
/* Divides R/G/B by 4 */
#define SHADOW_MASK ((0x3F << BITMAPCOLOR_R_SHIFT) | (0x3F << BITMAPCOLOR_G_SHIFT) | (0x3F << BITMAPCOLOR_B_SHIFT))
CC_NOINLINE static BitmapCol GetShadowColor(BitmapCol c) {
if (Drawer2D.BlackTextShadows) return BITMAPCOLOR_BLACK;
/* Initial layout: aaaa_aaaa|rrrr_rrrr|gggg_gggg|bbbb_bbbb */
/* Shift right 2: 00aa_aaaa|aarr_rrrr|rrgg_gggg|ggbb_bbbb */
/* And by 3f3f3f: 0000_0000|00rr_rrrr|00gg_gggg|00bb_bbbb */
/* Or by alpha : aaaa_aaaa|00rr_rrrr|00gg_gggg|00bb_bbbb */
return (c & BITMAPCOLOR_A_MASK) | ((c >> 2) & SHADOW_MASK);
}
/* TODO: Needs to account for DPI */
#define Drawer2D_ShadowOffset(point) (point / 8)
#define Drawer2D_XPadding(point) (Math_CeilDiv(point, 8))
@ -421,8 +406,7 @@ void Drawer2D_ReducePadding_Height(int* height, int point, int scale) {
*height -= padding * 2;
}
/* Quickly fills the given box region */
static void Drawer2D_Underline(struct Bitmap* bmp, int x, int y, int width, int height, BitmapCol color) {
void Drawer2D_Fill(struct Bitmap* bmp, int x, int y, int width, int height, BitmapCol color) {
BitmapCol* row;
int xx, yy;
@ -530,7 +514,7 @@ static void DrawBitmappedTextCore(struct Bitmap* bmp, struct DrawTextArgs* args,
for (; i < count && color == colors[i]; i++) {
dstWidth += dstWidths[i] + xPadding;
}
Drawer2D_Underline(bmp, x, underlineY, dstWidth, underlineHeight, color);
Drawer2D_Fill(bmp, x, underlineY, dstWidth, underlineHeight, color);
x += dstWidth;
}
}
@ -575,13 +559,13 @@ void Context2D_DrawText(struct Context2D* ctx, struct DrawTextArgs* args, int x,
if (Drawer2D_IsEmptyText(&args->text)) return;
if (Font_IsBitmap(args->font)) { DrawBitmappedText(bmp, args, x, y); return; }
if (args->useShadow) { Font_SysTextDraw(args, bmp, x, y, true); }
Font_SysTextDraw(args, bmp, x, y, false);
if (args->useShadow) { SysFont_DrawText(args, bmp, x, y, true); }
SysFont_DrawText(args, bmp, x, y, false);
}
int Drawer2D_TextWidth(struct DrawTextArgs* args) {
if (Font_IsBitmap(args->font)) return MeasureBitmappedWidth(args);
return Font_SysTextWidth(args);
return SysFont_TextWidth(args);
}
int Drawer2D_TextHeight(struct DrawTextArgs* args) {
@ -674,12 +658,10 @@ static void OnReset(void) {
static void OnInit(void) {
OnReset();
TextureEntry_Register(&default_entry);
Drawer2D.BitmappedText = Game_ClassicMode || !Options_GetBool(OPT_USE_CHAT_FONT, false);
Drawer2D.BlackTextShadows = Options_GetBool(OPT_BLACK_TEXT, false);
Options_Get(OPT_FONT_NAME, &font_default, "");
if (Game_ClassicMode) font_default.length = 0;
TextureEntry_Register(&default_entry);
}
static void OnFree(void) {
@ -692,841 +674,3 @@ struct IGameComponent Drawer2D_Component = {
OnFree, /* Free */
OnReset, /* Reset */
};
/*########################################################################################################################*
*------------------------------------------------------System fonts-------------------------------------------------------*
*#########################################################################################################################*/
#if defined CC_BUILD_FREETYPE
#include "freetype/ft2build.h"
#include "freetype/freetype.h"
#include "freetype/ftmodapi.h"
#include "freetype/ftglyph.h"
static FT_Library ft_lib;
static struct FT_MemoryRec_ ft_mem;
static struct StringsBuffer font_list;
static cc_bool fonts_changed;
/* Finds the path and face number of the given system font, with closest matching style */
static cc_string Font_Lookup(const cc_string* fontName, int flags);
struct SysFont {
FT_Face face;
struct Stream src, file;
FT_StreamRec stream;
cc_uint8 buffer[8192]; /* small buffer to minimise disk I/O */
cc_uint16 widths[256]; /* cached width of each character glyph */
FT_BitmapGlyph glyphs[256]; /* cached glyphs */
FT_BitmapGlyph shadow_glyphs[256]; /* cached glyphs (for back layer shadow) */
#ifdef CC_BUILD_DARWIN
char filename[FILENAME_SIZE + 1];
#endif
};
static unsigned long SysFont_Read(FT_Stream s, unsigned long offset, unsigned char* buffer, unsigned long count) {
struct SysFont* font;
cc_result res;
if (!count && offset > s->size) return 1;
font = (struct SysFont*)s->descriptor.pointer;
if (s->pos != offset) font->src.Seek(&font->src, offset);
res = Stream_Read(&font->src, buffer, count);
return res ? 0 : count;
}
static void SysFont_Free(struct SysFont* font) {
int i;
/* Close the actual underlying file */
struct Stream* source = &font->file;
if (!source->Meta.File) return;
source->Close(source);
for (i = 0; i < 256; i++) {
if (!font->glyphs[i]) continue;
FT_Done_Glyph((FT_Glyph)font->glyphs[i]);
}
for (i = 0; i < 256; i++) {
if (!font->shadow_glyphs[i]) continue;
FT_Done_Glyph((FT_Glyph)font->shadow_glyphs[i]);
}
}
static void SysFont_Close(FT_Stream stream) {
struct SysFont* font = (struct SysFont*)stream->descriptor.pointer;
SysFont_Free(font);
}
static cc_result SysFont_Init(const cc_string* path, struct SysFont* font, FT_Open_Args* args) {
cc_file file;
cc_uint32 size;
cc_result res;
#ifdef CC_BUILD_DARWIN
cc_string filename;
#endif
if ((res = File_Open(&file, path))) return res;
if ((res = File_Length(file, &size))) { File_Close(file); return res; }
font->stream.base = NULL;
font->stream.size = size;
font->stream.pos = 0;
font->stream.descriptor.pointer = font;
font->stream.read = SysFont_Read;
font->stream.close = SysFont_Close;
font->stream.memory = &ft_mem;
font->stream.cursor = NULL;
font->stream.limit = NULL;
args->flags = FT_OPEN_STREAM;
args->pathname = NULL;
args->stream = &font->stream;
Stream_FromFile(&font->file, file);
Stream_ReadonlyBuffered(&font->src, &font->file, font->buffer, sizeof(font->buffer));
/* For OSX font suitcase files */
#ifdef CC_BUILD_DARWIN
String_InitArray_NT(filename, font->filename);
String_Copy(&filename, path);
font->filename[filename.length] = '\0';
args->pathname = font->filename;
#endif
Mem_Set(font->widths, 0xFF, sizeof(font->widths));
Mem_Set(font->glyphs, 0x00, sizeof(font->glyphs));
Mem_Set(font->shadow_glyphs, 0x00, sizeof(font->shadow_glyphs));
return 0;
}
static void* FT_AllocWrapper(FT_Memory memory, long size) { return Mem_TryAlloc(size, 1); }
static void FT_FreeWrapper(FT_Memory memory, void* block) { Mem_Free(block); }
static void* FT_ReallocWrapper(FT_Memory memory, long cur_size, long new_size, void* block) {
return Mem_TryRealloc(block, new_size, 1);
}
#define FONT_CACHE_FILE "fontscache.txt"
static cc_string font_candidates[] = {
String_FromConst(""), /* replaced with font_default */
String_FromConst("Arial"), /* preferred font on all platforms */
String_FromConst("Liberation Sans"), /* ice looking fallbacks for linux */
String_FromConst("Nimbus Sans"),
String_FromConst("Bitstream Charter"),
String_FromConst("Cantarell"),
String_FromConst("DejaVu Sans Book"),
String_FromConst("Century Schoolbook L Roman"), /* commonly available on linux */
String_FromConst("Liberation Serif"), /* for SerenityOS */
String_FromConst("Slate For OnePlus"), /* Android 10, some devices */
String_FromConst("Roboto"), /* Android (broken on some Android 10 devices) */
String_FromConst("Geneva"), /* for ancient macOS versions */
String_FromConst("Droid Sans") /* for old Android versions */
};
static void SysFonts_InitLibrary(void) {
FT_Error err;
if (ft_lib) return;
ft_mem.alloc = FT_AllocWrapper;
ft_mem.free = FT_FreeWrapper;
ft_mem.realloc = FT_ReallocWrapper;
err = FT_New_Library(&ft_mem, &ft_lib);
if (err) Logger_Abort2(err, "Failed to init freetype");
FT_Add_Default_Modules(ft_lib);
}
/* Updates fonts list cache with system's list of fonts */
/* This should be avoided due to overhead potential */
static void SysFonts_Update(void) {
static cc_bool updatedFonts;
if (updatedFonts) return;
updatedFonts = true;
SysFonts_InitLibrary();
Platform_LoadSysFonts();
if (fonts_changed) EntryList_Save(&font_list, FONT_CACHE_FILE);
}
static void SysFonts_Load(void) {
/* Need to keep track of whether font cache has been checked at least once */
/* (Otherwise if unable to find any cached fonts and then unable to load any fonts,
/* font_list.count will always be 0 and the 'Initialising font cache' dialog will
confusingly get shown over and over until all font_candidates entries are checked) */
static cc_bool checkedCache;
if (checkedCache) return;
checkedCache = true;
EntryList_UNSAFE_Load(&font_list, FONT_CACHE_FILE);
if (font_list.count) return;
Window_ShowDialog("One time load", "Initialising font cache, this can take several seconds.");
SysFonts_Update();
}
/* Some language-specific fonts don't support English letters */
/* and show entirely as '[]' - better off ignoring such fonts */
static cc_bool SysFonts_SkipFont(FT_Face face) {
if (!face->charmap) return false;
return FT_Get_Char_Index(face, 'a') == 0 && FT_Get_Char_Index(face, 'z') == 0
&& FT_Get_Char_Index(face, 'A') == 0 && FT_Get_Char_Index(face, 'Z') == 0;
}
static void SysFonts_Add(const cc_string* path, FT_Face face, int index, char type, const char* defStyle) {
cc_string key; char keyBuffer[STRING_SIZE];
cc_string value; char valueBuffer[FILENAME_SIZE];
cc_string style = String_Empty;
if (!face->family_name || !(face->face_flags & FT_FACE_FLAG_SCALABLE)) return;
/* don't want 'Arial Regular' or 'Arial Bold' */
if (face->style_name) {
style = String_FromReadonly(face->style_name);
if (String_CaselessEqualsConst(&style, defStyle)) style.length = 0;
}
if (SysFonts_SkipFont(face)) type = 'X';
String_InitArray(key, keyBuffer);
if (style.length) {
String_Format3(&key, "%c %c %r", face->family_name, face->style_name, &type);
} else {
String_Format2(&key, "%c %r", face->family_name, &type);
}
String_InitArray(value, valueBuffer);
String_Format2(&value, "%s,%i", path, &index);
Platform_Log2("Face: %s = %s", &key, &value);
EntryList_Set(&font_list, &key, &value, '=');
fonts_changed = true;
}
static int SysFonts_DoRegister(const cc_string* path, int faceIndex) {
struct SysFont font;
FT_Open_Args args;
FT_Error err;
int flags, count;
if (SysFont_Init(path, &font, &args)) return 0;
err = FT_New_Face(ft_lib, &args, faceIndex, &font.face);
if (err) { SysFont_Free(&font); return 0; }
flags = font.face->style_flags;
count = font.face->num_faces;
if (flags == (FT_STYLE_FLAG_BOLD | FT_STYLE_FLAG_ITALIC)) {
SysFonts_Add(path, font.face, faceIndex, 'Z', "Bold Italic");
} else if (flags == FT_STYLE_FLAG_BOLD) {
SysFonts_Add(path, font.face, faceIndex, 'B', "Bold");
} else if (flags == FT_STYLE_FLAG_ITALIC) {
SysFonts_Add(path, font.face, faceIndex, 'I', "Italic");
} else if (flags == 0) {
SysFonts_Add(path, font.face, faceIndex, 'R', "Regular");
}
FT_Done_Face(font.face);
return count;
}
void SysFonts_Register(const cc_string* path) {
cc_string entry, name, value;
cc_string fontPath, index;
int i, count;
/* if font is already known, skip it */
for (i = 0; i < font_list.count; i++) {
StringsBuffer_UNSAFE_GetRaw(&font_list, i, &entry);
String_UNSAFE_Separate(&entry, '=', &name, &value);
String_UNSAFE_Separate(&value, ',', &fontPath, &index);
if (String_CaselessEquals(path, &fontPath)) return;
}
count = SysFonts_DoRegister(path, 0);
/* there may be more than one font in a font file */
for (i = 1; i < count; i++) {
SysFonts_DoRegister(path, i);
}
}
static cc_string Font_LookupOf(const cc_string* fontName, const char type) {
cc_string name; char nameBuffer[STRING_SIZE + 2];
String_InitArray(name, nameBuffer);
String_Format2(&name, "%s %r", fontName, &type);
return EntryList_UNSAFE_Get(&font_list, &name, '=');
}
static cc_string Font_DoLookup(const cc_string* fontName, int flags) {
cc_string path;
if (!font_list.count) SysFonts_Load();
path = String_Empty;
if (flags & FONT_FLAGS_BOLD) path = Font_LookupOf(fontName, 'B');
return path.length ? path : Font_LookupOf(fontName, 'R');
}
static cc_string Font_Lookup(const cc_string* fontName, int flags) {
cc_string path = Font_DoLookup(fontName, flags);
if (path.length) return path;
SysFonts_Update();
return Font_DoLookup(fontName, flags);
}
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
cc_string* font, path;
int i;
font_candidates[0] = font_default;
for (i = 0; i < Array_Elems(font_candidates); i++) {
font = &font_candidates[i];
if (!font->length) continue;
path = Font_Lookup(font, FONT_FLAGS_NONE);
if (path.length) return font;
}
return &String_Empty;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
cc_string entry, name, path;
int i;
if (!font_list.count) SysFonts_Load();
SysFonts_Update();
for (i = 0; i < font_list.count; i++) {
StringsBuffer_UNSAFE_GetRaw(&font_list, i, &entry);
String_UNSAFE_Separate(&entry, '=', &name, &path);
/* only want Regular fonts here */
if (name.length < 2 || name.buffer[name.length - 1] != 'R') continue;
name.length -= 2;
StringsBuffer_Add(buffer, &name);
}
StringsBuffer_Sort(buffer);
}
#define TEXT_CEIL(x) (((x) + 63) >> 6)
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
struct SysFont* font;
cc_string value, path, index;
int faceIndex, dpiX, dpiY;
FT_Open_Args args;
FT_Error err;
desc->size = size;
desc->flags = flags;
desc->handle = NULL;
value = Font_Lookup(fontName, flags);
if (!value.length) return ERR_INVALID_ARGUMENT;
String_UNSAFE_Separate(&value, ',', &path, &index);
Convert_ParseInt(&index, &faceIndex);
font = (struct SysFont*)Mem_TryAlloc(1, sizeof(struct SysFont));
if (!font) return ERR_OUT_OF_MEMORY;
SysFonts_InitLibrary();
if ((err = SysFont_Init(&path, font, &args))) { Mem_Free(font); return err; }
desc->handle = font;
/* TODO: Use 72 instead of 96 dpi for mobile devices */
dpiX = (int)(DisplayInfo.ScaleX * 96);
dpiY = (int)(DisplayInfo.ScaleY * 96);
if ((err = FT_New_Face(ft_lib, &args, faceIndex, &font->face))) return err;
if ((err = FT_Set_Char_Size(font->face, size * 64, 0, dpiX, dpiY))) return err;
/* height of any text when drawn with the given system font */
desc->height = TEXT_CEIL(font->face->size->metrics.height);
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
cc_string* font;
cc_result res;
int i;
font_candidates[0] = font_default;
for (i = 0; i < Array_Elems(font_candidates); i++) {
font = &font_candidates[i];
if (!font->length) continue;
res = SysFont_Make(desc, &font_candidates[i], size, flags);
if (res == ERR_INVALID_ARGUMENT) {
/* Fon't doesn't exist in list, skip over it */
} else if (res) {
Font_Free(desc);
Logger_SysWarn2(res, "creating font", font);
} else {
if (i) String_Copy(&font_candidates[0], font);
return;
}
}
Logger_Abort2(res, "Failed to init default font");
}
void Font_Free(struct FontDesc* desc) {
struct SysFont* font;
desc->size = 0;
if (Font_IsBitmap(desc)) return;
font = (struct SysFont*)desc->handle;
FT_Done_Face(font->face);
Mem_Free(font);
desc->handle = NULL;
}
static int Font_SysTextWidth(struct DrawTextArgs* args) {
struct SysFont* font = (struct SysFont*)args->font->handle;
FT_Face face = font->face;
cc_string text = args->text;
int i, width = 0, charWidth;
FT_Error res;
cc_unichar uc;
for (i = 0; i < text.length; i++) {
char c = text.buffer[i];
if (c == '&' && Drawer2D_ValidColorCodeAt(&text, i + 1)) {
i++; continue; /* skip over the color code */
}
charWidth = font->widths[(cc_uint8)c];
/* need to calculate glyph width */
if (charWidth == UInt16_MaxValue) {
uc = Convert_CP437ToUnicode(c);
res = FT_Load_Char(face, uc, 0);
if (res) {
Platform_Log2("Error %i measuring width of %r", &res, &c);
charWidth = 0;
} else {
charWidth = face->glyph->advance.x;
}
font->widths[(cc_uint8)c] = charWidth;
}
width += charWidth;
}
if (!width) return 0;
width = TEXT_CEIL(width);
if (args->useShadow) width += 2;
return width;
}
static void DrawGrayscaleGlyph(FT_Bitmap* img, struct Bitmap* bmp, int x, int y, BitmapCol col) {
cc_uint8* src;
BitmapCol* dst;
cc_uint8 I, invI; /* intensity */
int xx, yy;
for (yy = 0; yy < img->rows; yy++) {
if ((unsigned)(y + yy) >= (unsigned)bmp->height) continue;
src = img->buffer + (yy * img->pitch);
dst = Bitmap_GetRow(bmp, y + yy) + x;
for (xx = 0; xx < img->width; xx++, src++, dst++) {
if ((unsigned)(x + xx) >= (unsigned)bmp->width) continue;
I = *src; invI = UInt8_MaxValue - I;
/* TODO: Support transparent text */
/* dst->A = ((col.A * intensity) >> 8) + ((dst->A * invIntensity) >> 8);*/
/* TODO: Not shift when multiplying */
*dst = BitmapCol_Make(
((BitmapCol_R(col) * I) >> 8) + ((BitmapCol_R(*dst) * invI) >> 8),
((BitmapCol_G(col) * I) >> 8) + ((BitmapCol_G(*dst) * invI) >> 8),
((BitmapCol_B(col) * I) >> 8) + ((BitmapCol_B(*dst) * invI) >> 8),
I + ((BitmapCol_A(*dst) * invI) >> 8)
);
}
}
}
static void DrawBlackWhiteGlyph(FT_Bitmap* img, struct Bitmap* bmp, int x, int y, BitmapCol col) {
cc_uint8* src;
BitmapCol* dst;
cc_uint8 intensity;
int xx, yy;
for (yy = 0; yy < img->rows; yy++) {
if ((unsigned)(y + yy) >= (unsigned)bmp->height) continue;
src = img->buffer + (yy * img->pitch);
dst = Bitmap_GetRow(bmp, y + yy) + x;
for (xx = 0; xx < img->width; xx++, dst++) {
if ((unsigned)(x + xx) >= (unsigned)bmp->width) continue;
intensity = src[xx >> 3];
/* TODO: transparent text (don't set A to 255) */
if (intensity & (1 << (7 - (xx & 7)))) {
*dst = col | BitmapColor_A_Bits(255);
}
}
}
}
static FT_Vector shadow_delta = { 83, -83 };
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
struct SysFont* font = (struct SysFont*)args->font->handle;
FT_BitmapGlyph* glyphs = font->glyphs;
FT_Face face = font->face;
cc_string text = args->text;
int descender, height, begX = x;
BitmapCol color;
/* glyph state */
FT_BitmapGlyph glyph;
FT_Bitmap* img;
int i, offset;
FT_Error res;
cc_unichar uc;
if (shadow) {
glyphs = font->shadow_glyphs;
FT_Set_Transform(face, NULL, &shadow_delta);
}
height = args->font->height;
descender = TEXT_CEIL(face->size->metrics.descender);
color = Drawer2D.Colors['f'];
if (shadow) color = GetShadowColor(color);
for (i = 0; i < text.length; i++) {
char c = text.buffer[i];
if (c == '&' && Drawer2D_ValidColorCodeAt(&text, i + 1)) {
color = Drawer2D_GetColor(text.buffer[i + 1]);
if (shadow) color = GetShadowColor(color);
i++; continue; /* skip over the color code */
}
glyph = glyphs[(cc_uint8)c];
if (!glyph) {
uc = Convert_CP437ToUnicode(c);
res = FT_Load_Char(face, uc, FT_LOAD_RENDER);
if (res) {
Platform_Log2("Error %i drawing %r", &res, &text.buffer[i]);
continue;
}
/* due to FT_LOAD_RENDER, glyph is always a bitmap one */
FT_Get_Glyph(face->glyph, (FT_Glyph*)&glyph); /* TODO: Check error */
glyphs[(cc_uint8)c] = glyph;
}
offset = (height + descender) - glyph->top;
x += glyph->left; y += offset;
img = &glyph->bitmap;
if (img->num_grays == 2) {
DrawBlackWhiteGlyph(img, bmp, x, y, color);
} else {
DrawGrayscaleGlyph(img, bmp, x, y, color);
}
x += TEXT_CEIL(glyph->root.advance.x >> 10);
x -= glyph->left; y -= offset;
}
if (args->font->flags & FONT_FLAGS_UNDERLINE) {
int ul_pos = FT_MulFix(face->underline_position, face->size->metrics.y_scale);
int ul_thick = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale);
int ulHeight = TEXT_CEIL(ul_thick);
int ulY = height + TEXT_CEIL(ul_pos);
Drawer2D_Underline(bmp, begX, ulY + y, x - begX, ulHeight, color);
}
if (shadow) FT_Set_Transform(face, NULL, NULL);
}
#elif defined CC_BUILD_WEB
static cc_string font_arial = String_FromConst("Arial");
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
/* Fallback to Arial as default font */
/* TODO use serif instead?? */
return font_default.length ? &font_default : &font_arial;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
static const char* font_names[] = {
"Arial", "Arial Black", "Courier New", "Comic Sans MS", "Georgia", "Garamond",
"Helvetica", "Impact", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana",
"cursive", "fantasy", "monospace", "sans-serif", "serif", "system-ui"
};
int i;
for (i = 0; i < Array_Elems(font_names); i++) {
cc_string str = String_FromReadonly(font_names[i]);
StringsBuffer_Add(buffer, &str);
}
}
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = Mem_TryAlloc(fontName->length + 1, 1);
if (!desc->handle) return ERR_OUT_OF_MEMORY;
String_CopyToRaw(desc->handle, fontName->length + 1, fontName);
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, SysFonts_UNSAFE_GetDefault(), size, flags);
}
void Font_Free(struct FontDesc* desc) {
Mem_Free(desc->handle);
desc->handle = NULL;
desc->size = 0;
}
void SysFonts_Register(const cc_string* path) { }
extern void interop_SetFont(const char* font, int size, int flags);
extern double interop_TextWidth(const char* text, const int len);
extern double interop_TextDraw(const char* text, const int len, struct Bitmap* bmp, int x, int y, cc_bool shadow, const char* hex);
static int Font_SysTextWidth(struct DrawTextArgs* args) {
struct FontDesc* font = args->font;
cc_string left = args->text, part;
double width = 0;
char colorCode;
interop_SetFont(font->handle, font->size, font->flags);
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
char buffer[NATIVE_STR_LEN];
int len = String_EncodeUtf8(buffer, &part);
width += interop_TextWidth(buffer, len);
}
return Math_Ceil(width);
}
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
struct FontDesc* font = args->font;
cc_string left = args->text, part;
BitmapCol color;
char colorCode = 'f';
double xOffset = 0;
char hexBuffer[7];
cc_string hex;
/* adjust y position to more closely match FreeType drawn text */
y += (args->font->height - args->font->size) / 2;
interop_SetFont(font->handle, font->size, font->flags);
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
char buffer[NATIVE_STR_LEN];
int len = String_EncodeUtf8(buffer, &part);
color = Drawer2D_GetColor(colorCode);
if (shadow) color = GetShadowColor(color);
String_InitArray(hex, hexBuffer);
String_Append(&hex, '#');
String_AppendHex(&hex, BitmapCol_R(color));
String_AppendHex(&hex, BitmapCol_G(color));
String_AppendHex(&hex, BitmapCol_B(color));
/* TODO pass as double directly instead of (int) ?*/
xOffset += interop_TextDraw(buffer, len, bmp, x + (int)xOffset, y, shadow, hexBuffer);
}
}
#elif defined CC_BUILD_IOS
/* implemented in interop_ios.m */
extern void interop_GetFontNames(struct StringsBuffer* buffer);
extern cc_result interop_SysFontMake(struct FontDesc* desc, const cc_string* fontName, int size, int flags);
extern void interop_SysMakeDefault(struct FontDesc* desc, int size, int flags);
extern void interop_SysFontFree(void* handle);
extern int interop_SysTextWidth(struct DrawTextArgs* args);
extern void interop_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow);
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
return &String_Empty;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
interop_GetFontNames(buffer);
}
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
return interop_SysFontMake(desc, fontName, size, flags);
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
interop_SysMakeDefault(desc, size, flags);
}
void Font_Free(struct FontDesc* desc) {
desc->size = 0;
if (Font_IsBitmap(desc)) return;
interop_SysFontFree(desc->handle);
desc->handle = NULL;
}
static int Font_SysTextWidth(struct DrawTextArgs* args) {
return interop_SysTextWidth(args);
}
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
interop_SysTextDraw(args, bmp, x, y, shadow);
}
#elif defined CC_BUILD_PSP
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) { return &String_Empty; }
void SysFonts_GetNames(struct StringsBuffer* buffer) { }
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = (void*)1;
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, NULL, size, flags);
}
void Font_Free(struct FontDesc* desc) {
desc->size = 0;
desc->handle = NULL;
}
static int Font_SysTextWidth(struct DrawTextArgs* args) {
return 10;
}
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
}
#elif defined CC_BUILD_3DS
#include <3ds.h>
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) { return &String_Empty; }
void SysFonts_GetNames(struct StringsBuffer* buffer) { }
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = fontGetSystemFont();
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, NULL, size, flags);
}
void Font_Free(struct FontDesc* desc) {
desc->size = 0;
desc->handle = NULL;
}
static int Font_SysTextWidth(struct DrawTextArgs* args) {
int width = 0;
cc_string left = args->text, part;
char colorCode = 'f';
CFNT_s* font = (CFNT_s*)args->font->handle;
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
for (int i = 0; i < part.length; i++)
{
cc_unichar cp = Convert_CP437ToUnicode(part.buffer[i]);
int glyphIndex = fontGlyphIndexFromCodePoint(font, cp);
if (glyphIndex < 0) continue;
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
width += wInfo->charWidth;
}
}
return max(1, width);
}
static void DrawGlyph(CFNT_s* font, struct Bitmap* bmp, int x, int y, int glyphIndex) {
TGLP_s* tglp = font->finf.tglp;
//int glyphsPerSheet = tglp->nRows * tglp->nLines;
//int sheetIdx = glyphIndex / glyphsPerSheet;
//int sheetPos = glyphIndex % glyphsPerSheet;
//u8* sheet = tglp->sheetData + (sheetIdx * tglp->sheetSize);
//int rowY = sheetPos / tglp->nRows;
//int rowX = sheetPos % tglp->nRows;
//int FMT = tglp->sheetFmt;
//Platform_Log1("FMT: %i", &FMT);
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
/*for (int Y = 0; Y < tglp->cellHeight; Y++)
{
for (int X = wInfo->left; X < wInfo->left + wInfo->glyphWidth; X++)
{
int XX = x + X, YY = y + Y;
if (XX < 0 || YY < 0 || XX >= bmp->width || YY >= bmp->height) continue;
int srcX = X + rowX * tglp->cellWidth;
int srcY = Y + rowY * tglp->cellHeight; // TODO wrong. morton offset too?
u8 VALUE = ((sheet + srcY * (tglp->sheetWidth >> 1))[srcX >> 1] & 0x0F) << 4;
Bitmap_GetPixel(bmp, XX, YY) = BitmapColor_RGB(VALUE, VALUE, VALUE);
}
}*/
for (int Y = 0; Y < tglp->cellHeight; Y++)
{
for (int X = wInfo->left; X < wInfo->left + wInfo->glyphWidth; X++)
{
int XX = x + X, YY = y + Y;
if (XX < 0 || YY < 0 || XX >= bmp->width || YY >= bmp->height) continue;
Bitmap_GetPixel(bmp, XX, YY) = BITMAPCOLOR_WHITE;
}
}
}
static void Font_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
cc_string left = args->text, part;
char colorCode = 'f';
CFNT_s* font = (CFNT_s*)args->font->handle;
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
for (int i = 0; i < part.length; i++)
{
cc_unichar cp = Convert_CP437ToUnicode(part.buffer[i]);
int glyphIndex = fontGlyphIndexFromCodePoint(font, cp);
if (glyphIndex < 0) continue;
DrawGlyph(font, bmp, x, y, glyphIndex);
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
x += wInfo->charWidth;
}
}
}
#endif

View file

@ -12,7 +12,6 @@ struct DrawTextArgs { cc_string text; struct FontDesc* font; cc_bool useShadow;
struct Context2D { struct Bitmap bmp; int width, height; void* meta; };
struct Texture;
struct IGameComponent;
struct StringsBuffer;
extern struct IGameComponent Drawer2D_Component;
#define DRAWER2D_MAX_TEXT_LENGTH 256
@ -93,18 +92,33 @@ char Drawer2D_LastColor(const cc_string* text, int start);
cc_bool Drawer2D_IsWhiteColor(char c);
cc_bool Drawer2D_UNSAFE_NextPart(cc_string* left, cc_string* part, char* colorCode);
/* Divides R/G/B by 4 */
#define SHADOW_MASK ((0x3F << BITMAPCOLOR_R_SHIFT) | (0x3F << BITMAPCOLOR_G_SHIFT) | (0x3F << BITMAPCOLOR_B_SHIFT))
static CC_INLINE BitmapCol GetShadowColor(BitmapCol c) {
if (Drawer2D.BlackTextShadows) return BITMAPCOLOR_BLACK;
/* Initial layout: aaaa_aaaa|rrrr_rrrr|gggg_gggg|bbbb_bbbb */
/* Shift right 2: 00aa_aaaa|aarr_rrrr|rrgg_gggg|ggbb_bbbb */
/* And by 3f3f3f: 0000_0000|00rr_rrrr|00gg_gggg|00bb_bbbb */
/* Or by alpha : aaaa_aaaa|00rr_rrrr|00gg_gggg|00bb_bbbb */
return (c & BITMAPCOLOR_A_MASK) | ((c >> 2) & SHADOW_MASK);
}
/* Allocates a new instance of the default font using the given size and flags */
/* Uses Font_MakeBitmapped or SysFont_MakeDefault depending on Drawer2D_BitmappedText */
CC_API void Font_Make(struct FontDesc* desc, int size, int flags);
/* Frees an allocated font */
CC_API void Font_Free(struct FontDesc* desc);
/* Sets default system font name and raises ChatEvents.FontChanged */
void Font_SetDefault(const cc_string* fontName);
/* Returns the line height for drawing text using the given font */
int Font_CalcHeight(const struct FontDesc* font, cc_bool useShadow);
/* Adjusts height to be closer to system fonts */
int Drawer2D_AdjHeight(int point);
void Drawer2D_ReducePadding_Tex(struct Texture* tex, int point, int scale);
void Drawer2D_ReducePadding_Height(int* height, int point, int scale);
/* Quickly fills the given box region */
void Drawer2D_Fill(struct Bitmap* bmp, int x, int y, int width, int height, BitmapCol color);
/* Sets the bitmap used for drawing bitmapped fonts. (i.e. default.png) */
/* The bitmap must be square and consist of a 16x16 tile layout */
cc_bool Font_SetBitmapAtlas(struct Bitmap* bmp);
@ -112,18 +126,4 @@ cc_bool Font_SetBitmapAtlas(struct Bitmap* bmp);
void Font_SetPadding(struct FontDesc* desc, int amount);
/* Initialises the given font for drawing bitmapped text using default.png */
void Font_MakeBitmapped(struct FontDesc* desc, int size, int flags);
/* Allocates a new system font from the given arguments */
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags);
/* Allocates a new system font from the given arguments using default system font */
/* NOTE: Unlike SysFont_Make, this may fallback onto other system fonts (e.g. Arial, Roboto, etc) */
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags);
/* Gets the name of the default system font used */
const cc_string* SysFonts_UNSAFE_GetDefault(void);
/* Gets the list of all supported system font names on this platform */
CC_API void SysFonts_GetNames(struct StringsBuffer* buffer);
/* Attempts to decode one or more fonts from the given file */
/* NOTE: If this file has been decoded before (fontscache.txt), does nothing */
void SysFonts_Register(const cc_string* path);
#endif

View file

@ -36,6 +36,7 @@
#include "Protocol.h"
#include "Picking.h"
#include "Animations.h"
#include "SystemFonts.h"
struct _GameData Game;
cc_uint64 Game_FrameStart;
@ -388,6 +389,7 @@ static void Game_Load(void) {
Game_AddComponent(&Gfx_Component);
Game_AddComponent(&Blocks_Component);
Game_AddComponent(&Drawer2D_Component);
Game_AddComponent(&SystemFonts_Component);
Game_AddComponent(&Chat_Component);
Game_AddComponent(&Particles_Component);

View file

@ -20,6 +20,7 @@
#include "Options.h"
#include "LBackend.h"
#include "PackedCol.h"
#include "SystemFonts.h"
struct LScreen* Launcher_Active;
cc_bool Launcher_ShouldExit, Launcher_ShouldUpdate;
@ -234,6 +235,7 @@ void Launcher_Run(void) {
#endif
Drawer2D_Component.Init();
SystemFonts_Component.Init();
Drawer2D.BitmappedText = false;
Drawer2D.BlackTextShadows = true;

View file

@ -32,6 +32,7 @@
#include "Input.h"
#include "Utils.h"
#include "Errors.h"
#include "SystemFonts.h"
/* Describes a menu option button */
struct MenuOptionDesc {
@ -1573,7 +1574,7 @@ static void FontListScreen_EntryClick(void* screen, void* widget) {
cc_string fontName = ListScreen_UNSAFE_GetCur(s, widget);
Options_Set(OPT_FONT_NAME, &fontName);
Font_SetDefault(&fontName);
SysFont_SetDefault(&fontName);
}
static void FontListScreen_UpdateEntry(struct ListScreen* s, struct ButtonWidget* button, const cc_string* text) {

View file

@ -4,7 +4,6 @@
#include "_PlatformBase.h"
#include "Stream.h"
#include "ExtMath.h"
#include "Drawer2D.h"
#include "Funcs.h"
#include "Window.h"
#include "Utils.h"
@ -309,13 +308,6 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
}
/*########################################################################################################################*
*--------------------------------------------------------Font/Text--------------------------------------------------------*
*#########################################################################################################################*/
void Platform_LoadSysFonts(void) {
}
/*########################################################################################################################*
*---------------------------------------------------------Socket----------------------------------------------------------*
*#########################################################################################################################*/

View file

@ -315,12 +315,6 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
}
/*########################################################################################################################*
*--------------------------------------------------------Font/Text--------------------------------------------------------*
*#########################################################################################################################*/
void Platform_LoadSysFonts(void) { }
/*########################################################################################################################*
*---------------------------------------------------------Socket----------------------------------------------------------*
*#########################################################################################################################*/

View file

@ -4,7 +4,7 @@
#include "_PlatformBase.h"
#include "Stream.h"
#include "ExtMath.h"
#include "Drawer2D.h"
#include "SystemFonts.h"
#include "Funcs.h"
#include "Window.h"
#include "Utils.h"

View file

@ -4,7 +4,7 @@
#include "_PlatformBase.h"
#include "Stream.h"
#include "ExtMath.h"
#include "Drawer2D.h"
#include "SystemFonts.h"
#include "Funcs.h"
#include "Window.h"
#include "Utils.h"

View file

@ -3,7 +3,7 @@
#include "_PlatformBase.h"
#include "Stream.h"
#include "Drawer2D.h"
#include "SystemFonts.h"
#include "Funcs.h"
#include "Utils.h"
#include "Errors.h"

854
src/SystemFonts.c Normal file
View file

@ -0,0 +1,854 @@
#include "SystemFonts.h"
#include "Drawer2D.h"
#include "String.h"
#include "Funcs.h"
#include "Platform.h"
#include "ExtMath.h"
#include "Logger.h"
#include "Game.h"
#include "Event.h"
#include "Stream.h"
#include "Utils.h"
#include "Errors.h"
#include "Window.h"
#include "Options.h"
static char defaultBuffer[STRING_SIZE];
static cc_string font_default = String_FromArray(defaultBuffer);
void SysFont_SetDefault(const cc_string* fontName) {
String_Copy(&font_default, fontName);
Event_RaiseVoid(&ChatEvents.FontChanged);
}
static void OnInit(void) {
Options_Get(OPT_FONT_NAME, &font_default, "");
if (Game_ClassicMode) font_default.length = 0;
}
struct IGameComponent SystemFonts_Component = {
OnInit /* Init */
};
/*########################################################################################################################*
*--------------------------------------------------------Freetype---------------------------------------------------------*
*#########################################################################################################################*/
#if defined CC_BUILD_FREETYPE
#include "freetype/ft2build.h"
#include "freetype/freetype.h"
#include "freetype/ftmodapi.h"
#include "freetype/ftglyph.h"
static FT_Library ft_lib;
static struct FT_MemoryRec_ ft_mem;
static struct StringsBuffer font_list;
static cc_bool fonts_changed;
/* Finds the path and face number of the given system font, with closest matching style */
static cc_string Font_Lookup(const cc_string* fontName, int flags);
struct SysFont {
FT_Face face;
struct Stream src, file;
FT_StreamRec stream;
cc_uint8 buffer[8192]; /* small buffer to minimise disk I/O */
cc_uint16 widths[256]; /* cached width of each character glyph */
FT_BitmapGlyph glyphs[256]; /* cached glyphs */
FT_BitmapGlyph shadow_glyphs[256]; /* cached glyphs (for back layer shadow) */
#ifdef CC_BUILD_DARWIN
char filename[FILENAME_SIZE + 1];
#endif
};
static unsigned long SysFont_Read(FT_Stream s, unsigned long offset, unsigned char* buffer, unsigned long count) {
struct SysFont* font;
cc_result res;
if (!count && offset > s->size) return 1;
font = (struct SysFont*)s->descriptor.pointer;
if (s->pos != offset) font->src.Seek(&font->src, offset);
res = Stream_Read(&font->src, buffer, count);
return res ? 0 : count;
}
static void SysFont_Done(struct SysFont* font) {
int i;
/* Close the actual underlying file */
struct Stream* source = &font->file;
if (!source->Meta.File) return;
source->Close(source);
for (i = 0; i < 256; i++) {
if (!font->glyphs[i]) continue;
FT_Done_Glyph((FT_Glyph)font->glyphs[i]);
}
for (i = 0; i < 256; i++) {
if (!font->shadow_glyphs[i]) continue;
FT_Done_Glyph((FT_Glyph)font->shadow_glyphs[i]);
}
}
static void SysFont_Close(FT_Stream stream) {
struct SysFont* font = (struct SysFont*)stream->descriptor.pointer;
SysFont_Done(font);
}
static cc_result SysFont_Init(const cc_string* path, struct SysFont* font, FT_Open_Args* args) {
cc_file file;
cc_uint32 size;
cc_result res;
#ifdef CC_BUILD_DARWIN
cc_string filename;
#endif
if ((res = File_Open(&file, path))) return res;
if ((res = File_Length(file, &size))) { File_Close(file); return res; }
font->stream.base = NULL;
font->stream.size = size;
font->stream.pos = 0;
font->stream.descriptor.pointer = font;
font->stream.read = SysFont_Read;
font->stream.close = SysFont_Close;
font->stream.memory = &ft_mem;
font->stream.cursor = NULL;
font->stream.limit = NULL;
args->flags = FT_OPEN_STREAM;
args->pathname = NULL;
args->stream = &font->stream;
Stream_FromFile(&font->file, file);
Stream_ReadonlyBuffered(&font->src, &font->file, font->buffer, sizeof(font->buffer));
/* For OSX font suitcase files */
#ifdef CC_BUILD_DARWIN
String_InitArray_NT(filename, font->filename);
String_Copy(&filename, path);
font->filename[filename.length] = '\0';
args->pathname = font->filename;
#endif
Mem_Set(font->widths, 0xFF, sizeof(font->widths));
Mem_Set(font->glyphs, 0x00, sizeof(font->glyphs));
Mem_Set(font->shadow_glyphs, 0x00, sizeof(font->shadow_glyphs));
return 0;
}
static void* FT_AllocWrapper(FT_Memory memory, long size) { return Mem_TryAlloc(size, 1); }
static void FT_FreeWrapper(FT_Memory memory, void* block) { Mem_Free(block); }
static void* FT_ReallocWrapper(FT_Memory memory, long cur_size, long new_size, void* block) {
return Mem_TryRealloc(block, new_size, 1);
}
#define FONT_CACHE_FILE "fontscache.txt"
static cc_string font_candidates[] = {
String_FromConst(""), /* replaced with font_default */
String_FromConst("Arial"), /* preferred font on all platforms */
String_FromConst("Liberation Sans"), /* ice looking fallbacks for linux */
String_FromConst("Nimbus Sans"),
String_FromConst("Bitstream Charter"),
String_FromConst("Cantarell"),
String_FromConst("DejaVu Sans Book"),
String_FromConst("Century Schoolbook L Roman"), /* commonly available on linux */
String_FromConst("Liberation Serif"), /* for SerenityOS */
String_FromConst("Slate For OnePlus"), /* Android 10, some devices */
String_FromConst("Roboto"), /* Android (broken on some Android 10 devices) */
String_FromConst("Geneva"), /* for ancient macOS versions */
String_FromConst("Droid Sans") /* for old Android versions */
};
static void SysFonts_InitLibrary(void) {
FT_Error err;
if (ft_lib) return;
ft_mem.alloc = FT_AllocWrapper;
ft_mem.free = FT_FreeWrapper;
ft_mem.realloc = FT_ReallocWrapper;
err = FT_New_Library(&ft_mem, &ft_lib);
if (err) Logger_Abort2(err, "Failed to init freetype");
FT_Add_Default_Modules(ft_lib);
}
/* Updates fonts list cache with system's list of fonts */
/* This should be avoided due to overhead potential */
static void SysFonts_Update(void) {
static cc_bool updatedFonts;
if (updatedFonts) return;
updatedFonts = true;
SysFonts_InitLibrary();
Platform_LoadSysFonts();
if (fonts_changed) EntryList_Save(&font_list, FONT_CACHE_FILE);
}
static void SysFonts_Load(void) {
/* Need to keep track of whether font cache has been checked at least once */
/* (Otherwise if unable to find any cached fonts and then unable to load any fonts,
/* font_list.count will always be 0 and the 'Initialising font cache' dialog will
confusingly get shown over and over until all font_candidates entries are checked) */
static cc_bool checkedCache;
if (checkedCache) return;
checkedCache = true;
EntryList_UNSAFE_Load(&font_list, FONT_CACHE_FILE);
if (font_list.count) return;
Window_ShowDialog("One time load", "Initialising font cache, this can take several seconds.");
SysFonts_Update();
}
/* Some language-specific fonts don't support English letters */
/* and show entirely as '[]' - better off ignoring such fonts */
static cc_bool SysFonts_SkipFont(FT_Face face) {
if (!face->charmap) return false;
return FT_Get_Char_Index(face, 'a') == 0 && FT_Get_Char_Index(face, 'z') == 0
&& FT_Get_Char_Index(face, 'A') == 0 && FT_Get_Char_Index(face, 'Z') == 0;
}
static void SysFonts_Add(const cc_string* path, FT_Face face, int index, char type, const char* defStyle) {
cc_string key; char keyBuffer[STRING_SIZE];
cc_string value; char valueBuffer[FILENAME_SIZE];
cc_string style = String_Empty;
if (!face->family_name || !(face->face_flags & FT_FACE_FLAG_SCALABLE)) return;
/* don't want 'Arial Regular' or 'Arial Bold' */
if (face->style_name) {
style = String_FromReadonly(face->style_name);
if (String_CaselessEqualsConst(&style, defStyle)) style.length = 0;
}
if (SysFonts_SkipFont(face)) type = 'X';
String_InitArray(key, keyBuffer);
if (style.length) {
String_Format3(&key, "%c %c %r", face->family_name, face->style_name, &type);
} else {
String_Format2(&key, "%c %r", face->family_name, &type);
}
String_InitArray(value, valueBuffer);
String_Format2(&value, "%s,%i", path, &index);
Platform_Log2("Face: %s = %s", &key, &value);
EntryList_Set(&font_list, &key, &value, '=');
fonts_changed = true;
}
static int SysFonts_DoRegister(const cc_string* path, int faceIndex) {
struct SysFont font;
FT_Open_Args args;
FT_Error err;
int flags, count;
if (SysFont_Init(path, &font, &args)) return 0;
err = FT_New_Face(ft_lib, &args, faceIndex, &font.face);
if (err) { SysFont_Done(&font); return 0; }
flags = font.face->style_flags;
count = font.face->num_faces;
if (flags == (FT_STYLE_FLAG_BOLD | FT_STYLE_FLAG_ITALIC)) {
SysFonts_Add(path, font.face, faceIndex, 'Z', "Bold Italic");
} else if (flags == FT_STYLE_FLAG_BOLD) {
SysFonts_Add(path, font.face, faceIndex, 'B', "Bold");
} else if (flags == FT_STYLE_FLAG_ITALIC) {
SysFonts_Add(path, font.face, faceIndex, 'I', "Italic");
} else if (flags == 0) {
SysFonts_Add(path, font.face, faceIndex, 'R', "Regular");
}
FT_Done_Face(font.face);
return count;
}
void SysFonts_Register(const cc_string* path) {
cc_string entry, name, value;
cc_string fontPath, index;
int i, count;
/* if font is already known, skip it */
for (i = 0; i < font_list.count; i++) {
StringsBuffer_UNSAFE_GetRaw(&font_list, i, &entry);
String_UNSAFE_Separate(&entry, '=', &name, &value);
String_UNSAFE_Separate(&value, ',', &fontPath, &index);
if (String_CaselessEquals(path, &fontPath)) return;
}
count = SysFonts_DoRegister(path, 0);
/* there may be more than one font in a font file */
for (i = 1; i < count; i++) {
SysFonts_DoRegister(path, i);
}
}
static cc_string Font_LookupOf(const cc_string* fontName, const char type) {
cc_string name; char nameBuffer[STRING_SIZE + 2];
String_InitArray(name, nameBuffer);
String_Format2(&name, "%s %r", fontName, &type);
return EntryList_UNSAFE_Get(&font_list, &name, '=');
}
static cc_string Font_DoLookup(const cc_string* fontName, int flags) {
cc_string path;
if (!font_list.count) SysFonts_Load();
path = String_Empty;
if (flags & FONT_FLAGS_BOLD) path = Font_LookupOf(fontName, 'B');
return path.length ? path : Font_LookupOf(fontName, 'R');
}
static cc_string Font_Lookup(const cc_string* fontName, int flags) {
cc_string path = Font_DoLookup(fontName, flags);
if (path.length) return path;
SysFonts_Update();
return Font_DoLookup(fontName, flags);
}
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
cc_string* font, path;
int i;
font_candidates[0] = font_default;
for (i = 0; i < Array_Elems(font_candidates); i++) {
font = &font_candidates[i];
if (!font->length) continue;
path = Font_Lookup(font, FONT_FLAGS_NONE);
if (path.length) return font;
}
return &String_Empty;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
cc_string entry, name, path;
int i;
if (!font_list.count) SysFonts_Load();
SysFonts_Update();
for (i = 0; i < font_list.count; i++) {
StringsBuffer_UNSAFE_GetRaw(&font_list, i, &entry);
String_UNSAFE_Separate(&entry, '=', &name, &path);
/* only want Regular fonts here */
if (name.length < 2 || name.buffer[name.length - 1] != 'R') continue;
name.length -= 2;
StringsBuffer_Add(buffer, &name);
}
StringsBuffer_Sort(buffer);
}
#define TEXT_CEIL(x) (((x) + 63) >> 6)
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
struct SysFont* font;
cc_string value, path, index;
int faceIndex, dpiX, dpiY;
FT_Open_Args args;
FT_Error err;
desc->size = size;
desc->flags = flags;
desc->handle = NULL;
value = Font_Lookup(fontName, flags);
if (!value.length) return ERR_INVALID_ARGUMENT;
String_UNSAFE_Separate(&value, ',', &path, &index);
Convert_ParseInt(&index, &faceIndex);
font = (struct SysFont*)Mem_TryAlloc(1, sizeof(struct SysFont));
if (!font) return ERR_OUT_OF_MEMORY;
SysFonts_InitLibrary();
if ((err = SysFont_Init(&path, font, &args))) { Mem_Free(font); return err; }
desc->handle = font;
/* TODO: Use 72 instead of 96 dpi for mobile devices */
dpiX = (int)(DisplayInfo.ScaleX * 96);
dpiY = (int)(DisplayInfo.ScaleY * 96);
if ((err = FT_New_Face(ft_lib, &args, faceIndex, &font->face))) return err;
if ((err = FT_Set_Char_Size(font->face, size * 64, 0, dpiX, dpiY))) return err;
/* height of any text when drawn with the given system font */
desc->height = TEXT_CEIL(font->face->size->metrics.height);
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
cc_string* font;
cc_result res;
int i;
font_candidates[0] = font_default;
for (i = 0; i < Array_Elems(font_candidates); i++) {
font = &font_candidates[i];
if (!font->length) continue;
res = SysFont_Make(desc, &font_candidates[i], size, flags);
if (res == ERR_INVALID_ARGUMENT) {
/* Fon't doesn't exist in list, skip over it */
} else if (res) {
Font_Free(desc);
Logger_SysWarn2(res, "creating font", font);
} else {
if (i) String_Copy(&font_candidates[0], font);
return;
}
}
Logger_Abort2(res, "Failed to init default font");
}
void SysFont_Free(struct FontDesc* desc) {
struct SysFont* font = (struct SysFont*)desc->handle;
FT_Done_Face(font->face);
Mem_Free(font);
}
int SysFont_TextWidth(struct DrawTextArgs* args) {
struct SysFont* font = (struct SysFont*)args->font->handle;
FT_Face face = font->face;
cc_string text = args->text;
int i, width = 0, charWidth;
FT_Error res;
cc_unichar uc;
for (i = 0; i < text.length; i++) {
char c = text.buffer[i];
if (c == '&' && Drawer2D_ValidColorCodeAt(&text, i + 1)) {
i++; continue; /* skip over the color code */
}
charWidth = font->widths[(cc_uint8)c];
/* need to calculate glyph width */
if (charWidth == UInt16_MaxValue) {
uc = Convert_CP437ToUnicode(c);
res = FT_Load_Char(face, uc, 0);
if (res) {
Platform_Log2("Error %i measuring width of %r", &res, &c);
charWidth = 0;
} else {
charWidth = face->glyph->advance.x;
}
font->widths[(cc_uint8)c] = charWidth;
}
width += charWidth;
}
if (!width) return 0;
width = TEXT_CEIL(width);
if (args->useShadow) width += 2;
return width;
}
static void DrawGrayscaleGlyph(FT_Bitmap* img, struct Bitmap* bmp, int x, int y, BitmapCol col) {
cc_uint8* src;
BitmapCol* dst;
cc_uint8 I, invI; /* intensity */
int xx, yy;
for (yy = 0; yy < img->rows; yy++) {
if ((unsigned)(y + yy) >= (unsigned)bmp->height) continue;
src = img->buffer + (yy * img->pitch);
dst = Bitmap_GetRow(bmp, y + yy) + x;
for (xx = 0; xx < img->width; xx++, src++, dst++) {
if ((unsigned)(x + xx) >= (unsigned)bmp->width) continue;
I = *src; invI = UInt8_MaxValue - I;
/* TODO: Support transparent text */
/* dst->A = ((col.A * intensity) >> 8) + ((dst->A * invIntensity) >> 8);*/
/* TODO: Not shift when multiplying */
*dst = BitmapCol_Make(
((BitmapCol_R(col) * I) >> 8) + ((BitmapCol_R(*dst) * invI) >> 8),
((BitmapCol_G(col) * I) >> 8) + ((BitmapCol_G(*dst) * invI) >> 8),
((BitmapCol_B(col) * I) >> 8) + ((BitmapCol_B(*dst) * invI) >> 8),
I + ((BitmapCol_A(*dst) * invI) >> 8)
);
}
}
}
static void DrawBlackWhiteGlyph(FT_Bitmap* img, struct Bitmap* bmp, int x, int y, BitmapCol col) {
cc_uint8* src;
BitmapCol* dst;
cc_uint8 intensity;
int xx, yy;
for (yy = 0; yy < img->rows; yy++) {
if ((unsigned)(y + yy) >= (unsigned)bmp->height) continue;
src = img->buffer + (yy * img->pitch);
dst = Bitmap_GetRow(bmp, y + yy) + x;
for (xx = 0; xx < img->width; xx++, dst++) {
if ((unsigned)(x + xx) >= (unsigned)bmp->width) continue;
intensity = src[xx >> 3];
/* TODO: transparent text (don't set A to 255) */
if (intensity & (1 << (7 - (xx & 7)))) {
*dst = col | BitmapColor_A_Bits(255);
}
}
}
}
static FT_Vector shadow_delta = { 83, -83 };
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
struct SysFont* font = (struct SysFont*)args->font->handle;
FT_BitmapGlyph* glyphs = font->glyphs;
FT_Face face = font->face;
cc_string text = args->text;
int descender, height, begX = x;
BitmapCol color;
/* glyph state */
FT_BitmapGlyph glyph;
FT_Bitmap* img;
int i, offset;
FT_Error res;
cc_unichar uc;
if (shadow) {
glyphs = font->shadow_glyphs;
FT_Set_Transform(face, NULL, &shadow_delta);
}
height = args->font->height;
descender = TEXT_CEIL(face->size->metrics.descender);
color = Drawer2D.Colors['f'];
if (shadow) color = GetShadowColor(color);
for (i = 0; i < text.length; i++) {
char c = text.buffer[i];
if (c == '&' && Drawer2D_ValidColorCodeAt(&text, i + 1)) {
color = Drawer2D_GetColor(text.buffer[i + 1]);
if (shadow) color = GetShadowColor(color);
i++; continue; /* skip over the color code */
}
glyph = glyphs[(cc_uint8)c];
if (!glyph) {
uc = Convert_CP437ToUnicode(c);
res = FT_Load_Char(face, uc, FT_LOAD_RENDER);
if (res) {
Platform_Log2("Error %i drawing %r", &res, &text.buffer[i]);
continue;
}
/* due to FT_LOAD_RENDER, glyph is always a bitmap one */
FT_Get_Glyph(face->glyph, (FT_Glyph*)&glyph); /* TODO: Check error */
glyphs[(cc_uint8)c] = glyph;
}
offset = (height + descender) - glyph->top;
x += glyph->left; y += offset;
img = &glyph->bitmap;
if (img->num_grays == 2) {
DrawBlackWhiteGlyph(img, bmp, x, y, color);
} else {
DrawGrayscaleGlyph(img, bmp, x, y, color);
}
x += TEXT_CEIL(glyph->root.advance.x >> 10);
x -= glyph->left; y -= offset;
}
if (args->font->flags & FONT_FLAGS_UNDERLINE) {
int ul_pos = FT_MulFix(face->underline_position, face->size->metrics.y_scale);
int ul_thick = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale);
int ulHeight = TEXT_CEIL(ul_thick);
int ulY = height + TEXT_CEIL(ul_pos);
Drawer2D_Fill(bmp, begX, ulY + y, x - begX, ulHeight, color);
}
if (shadow) FT_Set_Transform(face, NULL, NULL);
}
#elif defined CC_BUILD_WEB
static cc_string font_arial = String_FromConst("Arial");
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
/* Fallback to Arial as default font */
/* TODO use serif instead?? */
return font_default.length ? &font_default : &font_arial;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
static const char* font_names[] = {
"Arial", "Arial Black", "Courier New", "Comic Sans MS", "Georgia", "Garamond",
"Helvetica", "Impact", "Tahoma", "Times New Roman", "Trebuchet MS", "Verdana",
"cursive", "fantasy", "monospace", "sans-serif", "serif", "system-ui"
};
int i;
for (i = 0; i < Array_Elems(font_names); i++) {
cc_string str = String_FromReadonly(font_names[i]);
StringsBuffer_Add(buffer, &str);
}
}
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = Mem_TryAlloc(fontName->length + 1, 1);
if (!desc->handle) return ERR_OUT_OF_MEMORY;
String_CopyToRaw(desc->handle, fontName->length + 1, fontName);
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, SysFonts_UNSAFE_GetDefault(), size, flags);
}
void SysFont_Free(struct FontDesc* desc) {
Mem_Free(desc->handle);
}
void SysFonts_Register(const cc_string* path) { }
extern void interop_SetFont(const char* font, int size, int flags);
extern double interop_TextWidth(const char* text, const int len);
extern double interop_TextDraw(const char* text, const int len, struct Bitmap* bmp, int x, int y, cc_bool shadow, const char* hex);
int SysFont_TextWidth(struct DrawTextArgs* args) {
struct FontDesc* font = args->font;
cc_string left = args->text, part;
double width = 0;
char colorCode;
interop_SetFont(font->handle, font->size, font->flags);
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
char buffer[NATIVE_STR_LEN];
int len = String_EncodeUtf8(buffer, &part);
width += interop_TextWidth(buffer, len);
}
return Math_Ceil(width);
}
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
struct FontDesc* font = args->font;
cc_string left = args->text, part;
BitmapCol color;
char colorCode = 'f';
double xOffset = 0;
char hexBuffer[7];
cc_string hex;
/* adjust y position to more closely match FreeType drawn text */
y += (args->font->height - args->font->size) / 2;
interop_SetFont(font->handle, font->size, font->flags);
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
char buffer[NATIVE_STR_LEN];
int len = String_EncodeUtf8(buffer, &part);
color = Drawer2D_GetColor(colorCode);
if (shadow) color = GetShadowColor(color);
String_InitArray(hex, hexBuffer);
String_Append(&hex, '#');
String_AppendHex(&hex, BitmapCol_R(color));
String_AppendHex(&hex, BitmapCol_G(color));
String_AppendHex(&hex, BitmapCol_B(color));
/* TODO pass as double directly instead of (int) ?*/
xOffset += interop_TextDraw(buffer, len, bmp, x + (int)xOffset, y, shadow, hexBuffer);
}
}
#elif defined CC_BUILD_IOS
/* implemented in interop_ios.m */
extern void interop_GetFontNames(struct StringsBuffer* buffer);
extern cc_result interop_SysFontMake(struct FontDesc* desc, const cc_string* fontName, int size, int flags);
extern void interop_SysMakeDefault(struct FontDesc* desc, int size, int flags);
extern void interop_SysFontFree(void* handle);
extern int interop_SysTextWidth(struct DrawTextArgs* args);
extern void interop_SysTextDraw(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow);
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) {
return &String_Empty;
}
void SysFonts_GetNames(struct StringsBuffer* buffer) {
interop_GetFontNames(buffer);
}
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
return interop_SysFontMake(desc, fontName, size, flags);
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
interop_SysMakeDefault(desc, size, flags);
}
void Font_Free(struct FontDesc* desc) {
interop_SysFontFree(desc->handle);
}
int SysFont_TextWidth(struct DrawTextArgs* args) {
return interop_SysTextWidth(args);
}
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
interop_SysTextDraw(args, bmp, x, y, shadow);
}
#elif defined CC_BUILD_PSP
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) { return &String_Empty; }
void SysFonts_GetNames(struct StringsBuffer* buffer) { }
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = (void*)1;
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, NULL, size, flags);
}
void SysFont_Free(struct FontDesc* desc) {
}
int SysFont_TextWidth(struct DrawTextArgs* args) {
return 10;
}
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
}
#elif defined CC_BUILD_3DS
#include <3ds.h>
void SysFonts_Register(const cc_string* path) { }
const cc_string* SysFonts_UNSAFE_GetDefault(void) { return &String_Empty; }
void SysFonts_GetNames(struct StringsBuffer* buffer) { }
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) {
desc->size = size;
desc->flags = flags;
desc->height = Drawer2D_AdjHeight(size);
desc->handle = fontGetSystemFont();
return 0;
}
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) {
SysFont_Make(desc, NULL, size, flags);
}
void SysFont_Free(struct FontDesc* desc) {
}
int SysFont_TextWidth(struct DrawTextArgs* args) {
int width = 0;
cc_string left = args->text, part;
char colorCode = 'f';
CFNT_s* font = (CFNT_s*)args->font->handle;
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
for (int i = 0; i < part.length; i++)
{
cc_unichar cp = Convert_CP437ToUnicode(part.buffer[i]);
int glyphIndex = fontGlyphIndexFromCodePoint(font, cp);
if (glyphIndex < 0) continue;
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
width += wInfo->charWidth;
}
}
return max(1, width);
}
static void DrawGlyph(CFNT_s* font, struct Bitmap* bmp, int x, int y, int glyphIndex) {
TGLP_s* tglp = font->finf.tglp;
//int glyphsPerSheet = tglp->nRows * tglp->nLines;
//int sheetIdx = glyphIndex / glyphsPerSheet;
//int sheetPos = glyphIndex % glyphsPerSheet;
//u8* sheet = tglp->sheetData + (sheetIdx * tglp->sheetSize);
//int rowY = sheetPos / tglp->nRows;
//int rowX = sheetPos % tglp->nRows;
//int FMT = tglp->sheetFmt;
//Platform_Log1("FMT: %i", &FMT);
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
/*for (int Y = 0; Y < tglp->cellHeight; Y++)
{
for (int X = wInfo->left; X < wInfo->left + wInfo->glyphWidth; X++)
{
int XX = x + X, YY = y + Y;
if (XX < 0 || YY < 0 || XX >= bmp->width || YY >= bmp->height) continue;
int srcX = X + rowX * tglp->cellWidth;
int srcY = Y + rowY * tglp->cellHeight; // TODO wrong. morton offset too?
u8 VALUE = ((sheet + srcY * (tglp->sheetWidth >> 1))[srcX >> 1] & 0x0F) << 4;
Bitmap_GetPixel(bmp, XX, YY) = BitmapColor_RGB(VALUE, VALUE, VALUE);
}
}*/
for (int Y = 0; Y < tglp->cellHeight; Y++)
{
for (int X = wInfo->left; X < wInfo->left + wInfo->glyphWidth; X++)
{
int XX = x + X, YY = y + Y;
if (XX < 0 || YY < 0 || XX >= bmp->width || YY >= bmp->height) continue;
Bitmap_GetPixel(bmp, XX, YY) = BITMAPCOLOR_WHITE;
}
}
}
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) {
cc_string left = args->text, part;
char colorCode = 'f';
CFNT_s* font = (CFNT_s*)args->font->handle;
while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode))
{
for (int i = 0; i < part.length; i++)
{
cc_unichar cp = Convert_CP437ToUnicode(part.buffer[i]);
int glyphIndex = fontGlyphIndexFromCodePoint(font, cp);
if (glyphIndex < 0) continue;
DrawGlyph(font, bmp, x, y, glyphIndex);
charWidthInfo_s* wInfo = fontGetCharWidthInfo(font, glyphIndex);
x += wInfo->charWidth;
}
}
}
#endif

36
src/SystemFonts.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef CC_SYSTEMFONTS_H
#define CC_SYSTEMFONTS_H
#include "Core.h"
/* Manages loading and drawing platform specific system fonts
Copyright 2014-2022 ClassiCube | Licensed under BSD-3
*/
struct Bitmap;
struct FontDesc;
struct DrawTextArgs;
struct IGameComponent;
struct StringsBuffer;
extern struct IGameComponent SystemFonts_Component;
/* Allocates a new system font from the given arguments */
cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags);
/* Frees an allocated system font */
void SysFont_Free(struct FontDesc* desc);
/* Allocates a new system font from the given arguments using default system font */
/* NOTE: Unlike SysFont_Make, this may fallback onto other system fonts (e.g. Arial, Roboto, etc) */
void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags);
/* Sets default system font name and raises ChatEvents.FontChanged */
void SysFont_SetDefault(const cc_string* fontName);
/* Measures width of the given text when drawn with the given system font */
int SysFont_TextWidth(struct DrawTextArgs* args);
/* Draws the given text with the given system font onto the given bitmap */
void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow);
/* Gets the name of the default system font used */
const cc_string* SysFonts_UNSAFE_GetDefault(void);
/* Gets the list of all supported system font names on this platform */
CC_API void SysFonts_GetNames(struct StringsBuffer* buffer);
/* Attempts to decode one or more fonts from the given file */
/* NOTE: If this file has been decoded before (fontscache.txt), does nothing */
void SysFonts_Register(const cc_string* path);
#endif

View file

@ -896,17 +896,6 @@ int interop_SysTextWidth(struct DrawTextArgs* args) {
return Math_Ceil(width);
}
#define SHADOW_MASK ((0x3F << BITMAPCOL_R_SHIFT) | (0x3F << BITMAPCOL_G_SHIFT) | (0x3F << BITMAPCOL_B_SHIFT))
CC_NOINLINE static BitmapCol GetShadowColor(BitmapCol c) {
if (Drawer2D.BlackTextShadows) return BITMAPCOL_BLACK;
// Initial layout: aaaa_aaaa|rrrr_rrrr|gggg_gggg|bbbb_bbbb
// Shift right 2: 00aa_aaaa|aarr_rrrr|rrgg_gggg|ggbb_bbbb
// And by 3f3f3f: 0000_0000|00rr_rrrr|00gg_gggg|00bb_bbbb
// Or by alpha : aaaa_aaaa|00rr_rrrr|00gg_gggg|00bb_bbbb
return (c & BITMAPCOL_A_MASK) | ((c >> 2) & SHADOW_MASK);
}
void interop_SysTextDraw(struct DrawTextArgs* args, struct Context2D* ctx, int x, int y, cc_bool shadow) {
CTFontRef font = (CTFontRef)args->font->handle;
cc_string left = args->text, part;
@ -990,18 +979,6 @@ void interop_SysFontFree(void* handle) {
CFBridgingRelease(handle);
}
#define SHADOW_MASK ((0x3F << BITMAPCOL_R_SHIFT) | (0x3F << BITMAPCOL_G_SHIFT) | (0x3F << BITMAPCOL_B_SHIFT))
CC_NOINLINE static BitmapCol GetShadowColor(BitmapCol c) {
// TODO move to Drawer2D.h
if (Drawer2D.BlackTextShadows) return BITMAPCOL_BLACK;
// Initial layout: aaaa_aaaa|rrrr_rrrr|gggg_gggg|bbbb_bbbb
// Shift right 2: 00aa_aaaa|aarr_rrrr|rrgg_gggg|ggbb_bbbb
// And by 3f3f3f: 0000_0000|00rr_rrrr|00gg_gggg|00bb_bbbb
// Or by alpha : aaaa_aaaa|00rr_rrrr|00gg_gggg|00bb_bbbb
return (c & BITMAPCOL_A_MASK) | ((c >> 2) & SHADOW_MASK);
}
static NSMutableAttributedString* GetAttributedString(struct DrawTextArgs* args, cc_bool shadow) {
UIFont* font = (__bridge UIFont*)args->font->handle;
cc_string left = args->text, part;