diff --git a/src/Drawer2D.c b/src/Drawer2D.c index d1a39350c..b0aa3be7c 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -4,10 +4,13 @@ #include "Platform.h" #include "ExtMath.h" #include "Logger.h" -#include "Bitmap.h" #include "Game.h" #include "Event.h" #include "Chat.h" +#include "Stream.h" +#include "Utils.h" +#include "Errors.h" +#include "Window.h" bool Drawer2D_BitmappedText; bool Drawer2D_BlackTextShadows; @@ -16,15 +19,15 @@ BitmapCol Drawer2D_Cols[DRAWER2D_MAX_COLS]; static char fontNameBuffer[STRING_SIZE]; String Drawer2D_FontName = String_FromArray(fontNameBuffer); -void DrawTextArgs_Make(struct DrawTextArgs* args, STRING_REF const String* text, const FontDesc* font, bool useShadow) { +void DrawTextArgs_Make(struct DrawTextArgs* args, STRING_REF const String* text, FontDesc* font, bool useShadow) { args->text = *text; - args->font = *font; + args->font = font; args->useShadow = useShadow; } -void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, const FontDesc* font, bool useShadow) { +void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, FontDesc* font, bool useShadow) { args->text = String_Empty; - args->font = *font; + args->font = font; args->useShadow = useShadow; } @@ -132,6 +135,14 @@ void Drawer2D_SetFontBitmap(Bitmap* bmp) { Drawer2D_CalculateTextWidths(); } +/* Measures width of the given text when drawn with the given system font. */ +static int Font_SysTextWidth(struct DrawTextArgs* args); +/* Measures height of any text when drawn with the given system font. */ +static int Font_SysFontHeight(const FontDesc* desc); +/* Draws the given text with the given system font onto the given bitmap. */ +static int Font_SysTextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow); + + /*########################################################################################################################* *---------------------------------------------------Drawing functions-----------------------------------------------------* @@ -416,7 +427,7 @@ static void Drawer2D_DrawCore(Bitmap* bmp, struct DrawTextArgs* args, int x, int BitmapCol black = BITMAPCOL_CONST(0, 0, 0, 255); BitmapCol col; String text = args->text; - int i, point = args->font.Size, count = 0; + int i, point = args->font->Size, count = 0; int xPadding, yPadding; int srcX, srcY, dstX, dstY; @@ -493,7 +504,7 @@ static void Drawer2D_DrawCore(Bitmap* bmp, struct DrawTextArgs* args, int x, int x = begX; } - if (!(args->font.Style & FONT_FLAG_UNDERLINE)) return; + if (!(args->font->Style & FONT_FLAG_UNDERLINE)) return; /* scale up bottom row of a cell to drawn text font */ cellY = (8 - 1) * dstHeight / 8; underlineY = y + (cellY + yPadding); @@ -512,7 +523,7 @@ static void Drawer2D_DrawCore(Bitmap* bmp, struct DrawTextArgs* args, int x, int } static void Drawer2D_DrawBitmapText(Bitmap* bmp, struct DrawTextArgs* args, int x, int y) { - int offset = Drawer2D_ShadowOffset(args->font.Size); + int offset = Drawer2D_ShadowOffset(args->font->Size); if (args->useShadow) { Drawer2D_DrawCore(bmp, args, x + offset, y + offset, true); @@ -521,7 +532,7 @@ static void Drawer2D_DrawBitmapText(Bitmap* bmp, struct DrawTextArgs* args, int } static int Drawer2D_MeasureBitmapWidth(const struct DrawTextArgs* args) { - int i, point = args->font.Size; + int i, point = args->font->Size; int xPadding, width; String text; @@ -563,10 +574,10 @@ void Drawer2D_DrawText(Bitmap* bmp, struct DrawTextArgs* args, int x, int y) { col = Drawer2D_GetCol(colCode); if (args->useShadow) { backCol = Drawer2D_BlackTextShadows ? black : Drawer2D_ShadowCol(col); - Platform_TextDraw(args, bmp, x, y, backCol, true); + Font_SysTextDraw(args, bmp, x, y, backCol, true); } - partWidth = Platform_TextDraw(args, bmp, x, y, col, false); + partWidth = Font_SysTextDraw(args, bmp, x, y, col, false); x += partWidth; } args->text = value; @@ -585,7 +596,7 @@ int Drawer2D_TextWidth(struct DrawTextArgs* args) { i = Drawer2D_NextPart(i, &value, &args->text, &nextCol); if (!args->text.length) continue; - width += Platform_TextWidth(args); + width += Font_SysTextWidth(args); } if (args->useShadow) width += 2; @@ -594,7 +605,7 @@ int Drawer2D_TextWidth(struct DrawTextArgs* args) { } int Drawer2D_TextHeight(struct DrawTextArgs* args) { - return Drawer2D_FontHeight(&args->font, args->useShadow); + return Drawer2D_FontHeight(args->font, args->useShadow); } int Drawer2D_FontHeight(const FontDesc* font, bool useShadow) { @@ -606,7 +617,7 @@ int Drawer2D_FontHeight(const FontDesc* font, bool useShadow) { if (useShadow) { height += Drawer2D_ShadowOffset(point); } } else { - height = Platform_FontHeight(font); + height = Font_SysFontHeight(font); if (useShadow) height += 2; } return height; @@ -717,3 +728,454 @@ struct IGameComponent Drawer2D_Component = { Drawer2D_Free, /* Free */ Drawer2D_Reset, /* Reset */ }; + + +/*########################################################################################################################* +*---------------------------------------------------Drawer2D component----------------------------------------------------* +*#########################################################################################################################*/ +#ifdef CC_BUILD_WEB +void Font_GetNames(StringsBuffer* buffer) { } +String Font_Lookup(const String* fontName, int style) { + String str = String_FromConst("-----"); return str; +} + +ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style) { + desc->Size = size; + desc->Style = style; + return 0; +} +void Font_Free(FontDesc* desc) { + desc->Size = 0; + desc->Style = 0; +} + +void SysFonts_Register(const String* path) { } +static int Font_SysTextWidth(struct DrawTextArgs* args) { return 0; } +static int Font_SysFontHeight(const FontDesc* font) { return 0; } +static int Font_SysTextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow) { return 0; } +#else +#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 EntryList font_list; +static bool font_list_changed; + +static void* FT_AllocWrapper(FT_Memory memory, long size) { + return Mem_Alloc(size, 1, "Freetype data"); +} +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_Realloc(block, new_size, 1, "Freetype data"); +} + +#define FONT_CACHE_FILE "fontscache.txt" +static void SysFonts_Init(void) { + static const String cachePath = String_FromConst(FONT_CACHE_FILE); + FT_Error err; + 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); + + if (!File_Exists(&cachePath)) { + Window_ShowDialog("One time load", "Initialising font cache, this can take several seconds."); + } + + EntryList_Init(&font_list, FONT_CACHE_FILE, '='); + Platform_LoadSysFonts(); + if (font_list_changed) EntryList_Save(&font_list); +} + +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_OSX + 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; + ReturnCode 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 ReturnCode SysFont_Init(const String* path, struct SysFont* font, FT_Open_Args* args) { + FileHandle file; + cc_uint32 size; + ReturnCode res; + + 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_OSX + String filename = String_NT_Array(data->filename); + String_Copy(&filename, path); + data->filename[filename.length] = '\0'; + args->pathname = data->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; +} + +void Font_GetNames(StringsBuffer* buffer) { + String entry, name, path; + int i; + if (!font_list.entries.count) SysFonts_Init(); + + for (i = 0; i < font_list.entries.count; i++) { + entry = StringsBuffer_UNSAFE_Get(&font_list.entries, i); + String_UNSAFE_Separate(&entry, font_list.separator, &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); + } +} + +static String Font_LookupOf(const String* fontName, const char type) { + 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); +} + +String Font_Lookup(const String* fontName, int style) { + String path; + if (!font_list.entries.count) SysFonts_Init(); + path = String_Empty; + + if (style & FONT_STYLE_BOLD) path = Font_LookupOf(fontName, 'B'); + if (style & FONT_STYLE_ITALIC) path = Font_LookupOf(fontName, 'I'); + + return path.length ? path : Font_LookupOf(fontName, 'R'); +} + +ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style) { + struct SysFont* font; + String value, path, index; + int faceIndex; + FT_Open_Args args; + FT_Error err; + + desc->Size = size; + desc->Style = style; + desc->Handle = NULL; + + value = Font_Lookup(fontName, style); + if (!value.length) return ERR_INVALID_ARGUMENT; + String_UNSAFE_Separate(&value, ',', &path, &index); + Convert_ParseInt(&index, &faceIndex); + + font = (struct SysFont*)Mem_Alloc(1, sizeof(struct SysFont), "SysFont"); + if ((err = SysFont_Init(&path, font, &args))) { Mem_Free(font); return err; } + desc->Handle = font; + + if ((err = FT_New_Face(ft_lib, &args, faceIndex, &font->face))) return err; + return FT_Set_Char_Size(font->face, size * 64, 0, Display_DpiX, Display_DpiY); +} + +void Font_Free(FontDesc* desc) { + struct SysFont* font; + desc->Size = 0; + desc->Style = 0; + /* NULL for fonts created by Drawer2D_MakeFont and bitmapped text mode is on */ + if (!desc->Handle) return; + + font = (struct SysFont*)desc->Handle; + FT_Done_Face(font->face); + Mem_Free(font); + desc->Handle = NULL; +} + +static void Font_Add(const String* path, FT_Face face, int index, char type, const char* defStyle) { + String key; char keyBuffer[STRING_SIZE]; + String value; char valueBuffer[FILENAME_SIZE]; + 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; + } + + 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); + font_list_changed = true; +} + +static int Font_Register(const 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)) { + Font_Add(path, font.face, faceIndex, 'Z', "Bold Italic"); + } else if (flags == FT_STYLE_FLAG_BOLD) { + Font_Add(path, font.face, faceIndex, 'B', "Bold"); + } else if (flags == FT_STYLE_FLAG_ITALIC) { + Font_Add(path, font.face, faceIndex, 'I', "Italic"); + } else if (flags == 0) { + Font_Add(path, font.face, faceIndex, 'R', "Regular"); + } + + FT_Done_Face(font.face); + return count; +} + +void SysFonts_Register(const String* path) { + String entry, name, value; + String fontPath, index; + int i, count; + + /* if font is already known, skip it */ + for (i = 0; i < font_list.entries.count; i++) { + entry = StringsBuffer_UNSAFE_Get(&font_list.entries, i); + String_UNSAFE_Separate(&entry, font_list.separator, &name, &value); + + String_UNSAFE_Separate(&value, ',', &fontPath, &index); + if (String_CaselessEquals(path, &fontPath)) return; + } + + count = Font_Register(path, 0); + /* there may be more than one font in a font file */ + for (i = 1; i < count; i++) { + Font_Register(path, i); + } +} + +#define TEXT_CEIL(x) (((x) + 63) >> 6) +static int Font_SysTextWidth(struct DrawTextArgs* args) { + struct SysFont* font = (struct SysFont*)args->font->Handle; + FT_Face face = font->face; + String text = args->text; + int i, width = 0, charWidth; + FT_Error res; + Codepoint cp; + + for (i = 0; i < text.length; i++) { + charWidth = font->widths[(cc_uint8)text.buffer[i]]; + /* need to calculate glyph width */ + if (charWidth == UInt16_MaxValue) { + cp = Convert_CP437ToUnicode(text.buffer[i]); + res = FT_Load_Char(face, cp, 0); + + if (res) { + Platform_Log2("Error %i measuring width of %r", &res, &text.buffer[i]); + charWidth = 0; + } else { + charWidth = face->glyph->advance.x; + } + + font->widths[(cc_uint8)text.buffer[i]] = charWidth; + } + width += charWidth; + } + return TEXT_CEIL(width); +} + +static int Font_SysFontHeight(const FontDesc* desc) { + struct SysFont* font = (struct SysFont*)desc->Handle; + FT_Face face = font->face; + return TEXT_CEIL(face->size->metrics.height); +} + +static void DrawGrayscaleGlyph(FT_Bitmap* img, Bitmap* bmp, int x, int y, BitmapCol col) { + cc_uint8* src; + BitmapCol* dst; + cc_uint8 intensity, invIntensity; + 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; + intensity = *src; invIntensity = UInt8_MaxValue - intensity; + + dst->B = ((col.B * intensity) >> 8) + ((dst->B * invIntensity) >> 8); + dst->G = ((col.G * intensity) >> 8) + ((dst->G * invIntensity) >> 8); + dst->R = ((col.R * intensity) >> 8) + ((dst->R * invIntensity) >> 8); + /*dst->A = ((col.A * intensity) >> 8) + ((dst->A * invIntensity) >> 8);*/ + dst->A = intensity + ((dst->A * invIntensity) >> 8); + } + } +} + +static void DrawBlackWhiteGlyph(FT_Bitmap* img, 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]; + + if (intensity & (1 << (7 - (xx & 7)))) { + dst->B = col.B; dst->G = col.G; dst->R = col.R; + /*dst->A = col.A*/ + dst->A = 255; + } + } + } +} + +static int Font_SysTextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow) { + struct SysFont* font = (struct SysFont*)args->font->Handle; + FT_BitmapGlyph* glyphs = font->glyphs; + + FT_Face face = font->face; + String text = args->text; + int descender, height, begX = x; + + /* glyph state */ + FT_BitmapGlyph glyph; + FT_Bitmap* img; + int i, offset; + FT_Error res; + Codepoint cp; + + if (shadow) { + glyphs = font->shadow_glyphs; + FT_Vector delta = { 83, -83 }; + FT_Set_Transform(face, NULL, &delta); + } + + height = TEXT_CEIL(face->size->metrics.height); + descender = TEXT_CEIL(face->size->metrics.descender); + + for (i = 0; i < text.length; i++) { + glyph = glyphs[(cc_uint8)text.buffer[i]]; + if (!glyph) { + cp = Convert_CP437ToUnicode(text.buffer[i]); + res = FT_Load_Char(face, cp, 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)text.buffer[i]] = glyph; + } + + offset = (height + descender) - glyph->top; + x += glyph->left; y += offset; + + img = &glyph->bitmap; + if (img->num_grays == 2) { + DrawBlackWhiteGlyph(img, bmp, x, y, col); + } else { + DrawGrayscaleGlyph(img, bmp, x, y, col); + } + + x += TEXT_CEIL(glyph->root.advance.x >> 10); + x -= glyph->left; y -= offset; + } + + if (args->font->Style & FONT_FLAG_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, col); + } + + if (shadow) FT_Set_Transform(face, NULL, NULL); + return x - begX; +} +#endif diff --git a/src/Drawer2D.h b/src/Drawer2D.h index d33476085..59a965299 100644 --- a/src/Drawer2D.h +++ b/src/Drawer2D.h @@ -7,13 +7,13 @@ Copyright 2014-2019 ClassiCube | Licensed under BSD-3 */ -struct DrawTextArgs { String text; FontDesc font; bool useShadow; }; +struct DrawTextArgs { String text; FontDesc* font; bool useShadow; }; struct Texture; struct IGameComponent; extern struct IGameComponent Drawer2D_Component; -void DrawTextArgs_Make(struct DrawTextArgs* args, STRING_REF const String* text, const FontDesc* font, bool useShadow); -void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, const FontDesc* font, bool useShadow); +void DrawTextArgs_Make(struct DrawTextArgs* args, STRING_REF const String* text, FontDesc* font, bool useShadow); +void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, FontDesc* font, bool useShadow); /* Initialises the given font. When Drawer2D_BitmappedText is false, creates native font handle using Font_Make. */ CC_NOINLINE void Drawer2D_MakeFont(FontDesc* desc, int size, int style); @@ -97,4 +97,16 @@ void Drawer2D_ReducePadding_Height(int* height, int point, int scale); /* Sets the bitmap used for drawing bitmapped fonts. (i.e. default.png) */ /* The bitmap must be square and consist of a 16x16 tile layout. */ void Drawer2D_SetFontBitmap(Bitmap* bmp); + +/* Gets the list of all supported system font names on this platform. */ +void Font_GetNames(StringsBuffer* buffer); +/* Finds the path and face number of the given system font, with closest matching style */ +String Font_Lookup(const String* fontName, int style); +/* Allocates a new system font from the given arguments. */ +ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style); +/* Frees an allocated font. */ +CC_API void Font_Free(FontDesc* desc); +/* Attempts to decode one or fonts from the given file. */ +/* NOTE: If this file has been decoded before (fontscache.txt), does nothing. */ +void SysFonts_Register(const String* path); #endif diff --git a/src/Entity.c b/src/Entity.c index 4e8e563d6..d7f3c7e27 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -233,6 +233,7 @@ static void Entity_MakeNameTexture(struct Entity* e) { BitmapCol origWhiteCol; struct DrawTextArgs args; + FontDesc font; bool bitmapped; String name; Size2D size; @@ -243,8 +244,8 @@ static void Entity_MakeNameTexture(struct Entity* e) { Drawer2D_BitmappedText = true; name = String_FromRawArray(e->DisplayNameRaw); - Drawer2D_MakeFont(&args.font, 24, FONT_STYLE_NORMAL); - DrawTextArgs_Make(&args, &name, &args.font, false); + Drawer2D_MakeFont(&font, 24, FONT_STYLE_NORMAL); + DrawTextArgs_Make(&args, &name, &font, false); size = Drawer2D_MeasureText(&args); if (size.Width == 0) { diff --git a/src/LWidgets.c b/src/LWidgets.c index dd09728ef..ec4a84562 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -242,7 +242,7 @@ static void LInput_DrawText(struct LInput* w, struct DrawTextArgs* args) { Drawer2D_DrawText(&Launcher_Framebuffer, args, w->X + 5, y + 2); } else { args->text = String_FromReadonly(w->HintText); - args->font = Launcher_HintFont; + args->font = &Launcher_HintFont; hintHeight = Drawer2D_TextHeight(args); y = w->Y + (w->Height - hintHeight) / 2; diff --git a/src/Platform.c b/src/Platform.c index f229f5af7..bbf6b45de 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -8,7 +8,6 @@ #include "Window.h" #include "Utils.h" #include "Errors.h" -#define CC_BUILD_FREETYPE #if defined CC_BUILD_WIN #define WIN32_LEAN_AND_MEAN @@ -92,7 +91,6 @@ const ReturnCode ReturnCode_SocketWouldBlock = EWOULDBLOCK; #include #elif defined CC_BUILD_WEB #include -#undef CC_BUILD_FREETYPE #endif @@ -861,422 +859,18 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { /*########################################################################################################################* *--------------------------------------------------------Font/Text--------------------------------------------------------* *#########################################################################################################################*/ -#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 EntryList font_list; -static bool font_list_changed; -static void Font_Init(void); - -struct FontData { - 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_OSX - char filename[FILENAME_SIZE + 1]; -#endif -}; - -static unsigned long FontData_Read(FT_Stream s, unsigned long offset, unsigned char* buffer, unsigned long count) { - struct FontData* data; - ReturnCode res; - if (!count && offset > s->size) return 1; - - data = (struct FontData*)s->descriptor.pointer; - if (s->pos != offset) data->src.Seek(&data->src, offset); - - res = Stream_Read(&data->src, buffer, count); - return res ? 0 : count; -} - -static void FontData_Free(struct FontData* 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 FontData_Close(FT_Stream stream) { - struct FontData* data = (struct FontData*)stream->descriptor.pointer; - FontData_Free(data); -} - -static ReturnCode FontData_Init(const String* path, struct FontData* data, FT_Open_Args* args) { - FileHandle file; - cc_uint32 size; - ReturnCode res; - - if ((res = File_Open(&file, path))) return res; - if ((res = File_Length(file, &size))) { File_Close(file); return res; } - - data->stream.base = NULL; - data->stream.size = size; - data->stream.pos = 0; - - data->stream.descriptor.pointer = data; - data->stream.read = FontData_Read; - data->stream.close = FontData_Close; - - data->stream.memory = &ft_mem; - data->stream.cursor = NULL; - data->stream.limit = NULL; - - args->flags = FT_OPEN_STREAM; - args->pathname = NULL; - args->stream = &data->stream; - - Stream_FromFile(&data->file, file); - Stream_ReadonlyBuffered(&data->src, &data->file, data->buffer, sizeof(data->buffer)); - - /* For OSX font suitcase files */ -#ifdef CC_BUILD_OSX - String filename = String_NT_Array(data->filename); - String_Copy(&filename, path); - data->filename[filename.length] = '\0'; - args->pathname = data->filename; -#endif - Mem_Set(data->widths, 0xFF, sizeof(data->widths)); - Mem_Set(data->glyphs, 0x00, sizeof(data->glyphs)); - Mem_Set(data->shadow_glyphs, 0x00, sizeof(data->shadow_glyphs)); - return 0; -} - -void Font_GetNames(StringsBuffer* buffer) { - String entry, name, path; - int i; - if (!font_list.entries.count) Font_Init(); - - for (i = 0; i < font_list.entries.count; i++) { - entry = StringsBuffer_UNSAFE_Get(&font_list.entries, i); - String_UNSAFE_Separate(&entry, font_list.separator, &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); - } -} - -static String Font_LookupOf(const String* fontName, const char type) { - 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); -} - -String Font_Lookup(const String* fontName, int style) { - String path; - if (!font_list.entries.count) Font_Init(); - path = String_Empty; - - if (style & FONT_STYLE_BOLD) path = Font_LookupOf(fontName, 'B'); - if (style & FONT_STYLE_ITALIC) path = Font_LookupOf(fontName, 'I'); - - return path.length ? path : Font_LookupOf(fontName, 'R'); -} - -ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style) { - struct FontData* data; - String value, path, index; - int faceIndex; - FT_Open_Args args; - FT_Error err; - - desc->Size = size; - desc->Style = style; - desc->Handle = NULL; - - value = Font_Lookup(fontName, style); - if (!value.length) return ERR_INVALID_ARGUMENT; - String_UNSAFE_Separate(&value, ',', &path, &index); - Convert_ParseInt(&index, &faceIndex); - - data = (struct FontData*)Mem_Alloc(1, sizeof(struct FontData), "FontData"); - if ((err = FontData_Init(&path, data, &args))) { Mem_Free(data); return err; } - desc->Handle = data; - - if ((err = FT_New_Face(ft_lib, &args, faceIndex, &data->face))) return err; - return FT_Set_Char_Size(data->face, size * 64, 0, Display_DpiX, Display_DpiY); -} - -void Font_Free(FontDesc* desc) { - struct FontData* data; - desc->Size = 0; - desc->Style = 0; - /* NULL for fonts created by Drawer2D_MakeFont and bitmapped text mode is on */ - if (!desc->Handle) return; - - data = (struct FontData*)desc->Handle; - FT_Done_Face(data->face); - Mem_Free(data); - desc->Handle = NULL; -} - -static void Font_Add(const String* path, FT_Face face, int index, char type, const char* defStyle) { - String key; char keyBuffer[STRING_SIZE]; - String value; char valueBuffer[FILENAME_SIZE]; - 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; - } - - 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); - font_list_changed = true; -} - -static int Font_Register(const String* path, int faceIndex) { - struct FontData data; - FT_Open_Args args; - FT_Error err; - int flags, count; - - if (FontData_Init(path, &data, &args)) return 0; - err = FT_New_Face(ft_lib, &args, faceIndex, &data.face); - if (err) { FontData_Free(&data); return 0; } - - flags = data.face->style_flags; - count = data.face->num_faces; - - if (flags == (FT_STYLE_FLAG_BOLD | FT_STYLE_FLAG_ITALIC)) { - Font_Add(path, data.face, faceIndex, 'Z', "Bold Italic"); - } else if (flags == FT_STYLE_FLAG_BOLD) { - Font_Add(path, data.face, faceIndex, 'B', "Bold"); - } else if (flags == FT_STYLE_FLAG_ITALIC) { - Font_Add(path, data.face, faceIndex, 'I', "Italic"); - } else if (flags == 0) { - Font_Add(path, data.face, faceIndex, 'R', "Regular"); - } - - FT_Done_Face(data.face); - return count; -} - -static void Font_DirCallback(const String* path, void* obj) { +#ifdef CC_BUILD_WEB +void Platform_LoadSysFonts(void) { } +#else +static void FontDirCallback(const String* path, void* obj) { static const String fonExt = String_FromConst(".fon"); - String entry, name, value; - String fontPath, index; - int i, count; - /* Completely skip windows .FON files */ if (String_CaselessEnds(path, &fonExt)) return; - - /* If font is already known, skip it */ - for (i = 0; i < font_list.entries.count; i++) { - entry = StringsBuffer_UNSAFE_Get(&font_list.entries, i); - String_UNSAFE_Separate(&entry, font_list.separator, &name, &value); - - String_UNSAFE_Separate(&value, ',', &fontPath, &index); - if (String_CaselessEquals(path, &fontPath)) return; - } - - count = Font_Register(path, 0); - /* There may be more than one font in a font file */ - for (i = 1; i < count; i++) { - Font_Register(path, i); - } + SysFonts_Register(path); } -#define TEXT_CEIL(x) (((x) + 63) >> 6) -int Platform_TextWidth(struct DrawTextArgs* args) { - struct FontData* data = (struct FontData*)args->font.Handle; - FT_Face face = data->face; - String text = args->text; - int i, width = 0, charWidth; - FT_Error res; - Codepoint cp; - - for (i = 0; i < text.length; i++) { - charWidth = data->widths[(cc_uint8)text.buffer[i]]; - /* need to calculate glyph width */ - if (charWidth == UInt16_MaxValue) { - cp = Convert_CP437ToUnicode(text.buffer[i]); - res = FT_Load_Char(face, cp, 0); - - if (res) { - Platform_Log2("Error %i measuring width of %r", &res, &text.buffer[i]); - charWidth = 0; - } else { - charWidth = face->glyph->advance.x; - } - - data->widths[(cc_uint8)text.buffer[i]] = charWidth; - } - width += charWidth; - } - return TEXT_CEIL(width); -} - -int Platform_FontHeight(const FontDesc* font) { - struct FontData* data = (struct FontData*)font->Handle; - FT_Face face = data->face; - return TEXT_CEIL(face->size->metrics.height); -} - -static void Platform_GrayscaleGlyph(FT_Bitmap* img, Bitmap* bmp, int x, int y, BitmapCol col) { - cc_uint8* src; - BitmapCol* dst; - cc_uint8 intensity, invIntensity; - 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; - intensity = *src; invIntensity = UInt8_MaxValue - intensity; - - dst->B = ((col.B * intensity) >> 8) + ((dst->B * invIntensity) >> 8); - dst->G = ((col.G * intensity) >> 8) + ((dst->G * invIntensity) >> 8); - dst->R = ((col.R * intensity) >> 8) + ((dst->R * invIntensity) >> 8); - /*dst->A = ((col.A * intensity) >> 8) + ((dst->A * invIntensity) >> 8);*/ - dst->A = intensity + ((dst->A * invIntensity) >> 8); - } - } -} - -static void Platform_BlackWhiteGlyph(FT_Bitmap* img, 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]; - - if (intensity & (1 << (7 - (xx & 7)))) { - dst->B = col.B; dst->G = col.G; dst->R = col.R; - /*dst->A = col.A*/ - dst->A = 255; - } - } - } -} - -int Platform_TextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow) { - struct FontData* data = (struct FontData*)args->font.Handle; - FT_BitmapGlyph* glyphs = data->glyphs; - - FT_Face face = data->face; - String text = args->text; - int descender, height, begX = x; - - /* glyph state */ - FT_BitmapGlyph glyph; - FT_Bitmap* img; - int i, offset; - FT_Error res; - Codepoint cp; - - if (shadow) { - glyphs = data->shadow_glyphs; - FT_Vector delta = { 83, -83 }; - FT_Set_Transform(face, NULL, &delta); - } - - height = TEXT_CEIL(face->size->metrics.height); - descender = TEXT_CEIL(face->size->metrics.descender); - - for (i = 0; i < text.length; i++) { - glyph = glyphs[(cc_uint8)text.buffer[i]]; - if (!glyph) { - cp = Convert_CP437ToUnicode(text.buffer[i]); - res = FT_Load_Char(face, cp, 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)text.buffer[i]] = glyph; - } - - offset = (height + descender) - glyph->top; - x += glyph->left; y += offset; - - img = &glyph->bitmap; - if (img->num_grays == 2) { - Platform_BlackWhiteGlyph(img, bmp, x, y, col); - } else { - Platform_GrayscaleGlyph(img, bmp, x, y, col); - } - - x += TEXT_CEIL(glyph->root.advance.x >> 10); - x -= glyph->left; y -= offset; - } - - if (args->font.Style & FONT_FLAG_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, col); - } - - if (shadow) FT_Set_Transform(face, NULL, NULL); - return x - begX; -} - -static void* FT_AllocWrapper(FT_Memory memory, long size) { - return Mem_Alloc(size, 1, "Freetype data"); -} - -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_Realloc(block, new_size, 1, "Freetype data"); -} - -#define FONT_CACHE_FILE "fontscache.txt" -static void Font_Init(void) { +void Platform_LoadSysFonts(void) { + int i; #if defined CC_BUILD_WIN char winFolder[FILENAME_SIZE]; TCHAR winTmp[FILENAME_SIZE]; @@ -1315,52 +909,10 @@ static void Font_Init(void) { String_FromConst("/System/Library/Fonts"), String_FromConst("/Library/Fonts") }; -#elif defined CC_BUILD_WEB - /* TODO: Implement fonts */ - static const String dirs[1] = { String_FromConst("Fonts") }; #endif - static const String cachePath = String_FromConst(FONT_CACHE_FILE); - FT_Error err; - int i; - - 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); - - if (!File_Exists(&cachePath)) { - Window_ShowDialog("One time load", "Initialising font cache, this can take several seconds."); - } - - EntryList_Init(&font_list, FONT_CACHE_FILE, '='); for (i = 0; i < Array_Elems(dirs); i++) { - Directory_Enum(&dirs[i], NULL, Font_DirCallback); + Directory_Enum(&dirs[i], NULL, FontDirCallback); } - if (font_list_changed) EntryList_Save(&font_list); -} -#else -void Font_GetNames(StringsBuffer* buffer) { } -String Font_Lookup(const String* fontName, int style) { - String str = String_FromConst("-----"); return str; -} - -ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style) { - desc->Size = size; - desc->Style = style; - return 0; -} -void Font_Free(FontDesc* desc) { - desc->Size = 0; - desc->Style = 0; -} - -int Platform_TextWidth(struct DrawTextArgs* args) { return 0; } -int Platform_FontHeight(const FontDesc* font) { return 0; } -int Platform_TextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow) { - return 0; } #endif diff --git a/src/Platform.h b/src/Platform.h index 52baaa872..9b05b731e 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -191,20 +191,7 @@ CC_API void Waitable_Wait(void* handle); /* Blocks the calling thread until the waitable gets signalled, or milliseconds delay passes. */ CC_API void Waitable_WaitFor(void* handle, cc_uint32 milliseconds); -/* Gets the list of all supported font names on this platform. */ -void Font_GetNames(StringsBuffer* buffer); -/* Finds the path and face number of the given font, with closest matching style */ -String Font_Lookup(const String* fontName, int style); -/* Allocates a new font from the given arguments. */ -ReturnCode Font_Make(FontDesc* desc, const String* fontName, int size, int style); -/* Frees an allocated font. */ -CC_API void Font_Free(FontDesc* desc); -/* Measures width of the given text when drawn with the given font. */ -int Platform_TextWidth(struct DrawTextArgs* args); -/* Measures height of any text when drawn with the given font. */ -int Platform_FontHeight(const FontDesc* font); -/* Draws the given text with the given font onto the given bitmap. */ -int Platform_TextDraw(struct DrawTextArgs* args, Bitmap* bmp, int x, int y, BitmapCol col, bool shadow); +void Platform_LoadSysFonts(void); /* Allocates a new socket. */ CC_API void Socket_Create(SocketHandle* socket); diff --git a/src/Widgets.c b/src/Widgets.c index 6f7fa681c..5f0b5d6c1 100644 --- a/src/Widgets.c +++ b/src/Widgets.c @@ -2333,7 +2333,7 @@ static bool TextGroupWidget_GetUrl(struct TextGroupWidget* w, String* text, int for (i = 0, x = 0; i < portionsCount; i++) { bit = portions[i]; args.text = String_UNSAFE_Substring(&line, bit.LineBeg, bit.LineLen); - args.font = *w->font; + args.font = w->font; width = Drawer2D_TextWidth(&args); if ((bit.Len & TEXTGROUPWIDGET_URL) && mouseX >= x && mouseX < x + width) { @@ -2407,9 +2407,9 @@ static void TextGroupWidget_DrawAdvanced(struct TextGroupWidget* w, struct Textu ul = (bit.Len & TEXTGROUPWIDGET_URL); args->text = String_UNSAFE_Substring(text, bit.LineBeg, bit.LineLen); - if (ul) args->font.Style |= FONT_FLAG_UNDERLINE; + if (ul) args->font->Style |= FONT_FLAG_UNDERLINE; Drawer2D_DrawText(&bmp, args, x, 0); - if (ul) args->font.Style &= ~FONT_FLAG_UNDERLINE; + if (ul) args->font->Style &= ~FONT_FLAG_UNDERLINE; x += partWidths[i]; }