mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-22 09:01:57 -05:00
Split up Drawer2D module into Drawer2D and SystemFonts modules
This commit is contained in:
parent
02e559e520
commit
ac7602386e
16 changed files with 944 additions and 933 deletions
|
@ -97,6 +97,7 @@ add_library(classicube SHARED
|
|||
../../src/EnvRenderer.c
|
||||
../../src/Animations.c
|
||||
../../src/LBackend.c
|
||||
../../src/SystemFonts.c
|
||||
)
|
||||
|
||||
# add lib dependencies
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
892
src/Drawer2D.c
892
src/Drawer2D.c
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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----------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
|
|
|
@ -315,12 +315,6 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) {
|
|||
}
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*--------------------------------------------------------Font/Text--------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
void Platform_LoadSysFonts(void) { }
|
||||
|
||||
|
||||
/*########################################################################################################################*
|
||||
*---------------------------------------------------------Socket----------------------------------------------------------*
|
||||
*#########################################################################################################################*/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
854
src/SystemFonts.c
Normal 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
36
src/SystemFonts.h
Normal 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
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue