diff --git a/CMakeLists.txt b/CMakeLists.txt index 5747627..f12d13d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ option(MSDFGEN_USE_CPP11 "Build with C++11 enabled" ON) option(MSDFGEN_USE_SKIA "Build with the Skia library" ON) option(MSDFGEN_INSTALL "Generate installation target" OFF) option(MSDFGEN_DYNAMIC_RUNTIME "Link dynamic runtime library instead of static" OFF) +option(MSDFGEN_DYNAMIC_FREETYPE "Build with FreeType support enabled but without statically linking to FreeTypee" ON) option(BUILD_SHARED_LIBS "Generate dynamic library files instead of static" OFF) if(MSDFGEN_CORE_ONLY AND MSDFGEN_USE_VCPKG) @@ -134,7 +135,7 @@ endif() # Extensions library if(NOT MSDFGEN_CORE_ONLY) - if(NOT TARGET Freetype::Freetype) + if(NOT TARGET Freetype::Freetype AND NOT MSDFGEN_DYNAMIC_FREETYPE) find_package(Freetype REQUIRED) endif() if(NOT MSDFGEN_DISABLE_SVG AND NOT TARGET tinyxml2::tinyxml2) @@ -152,6 +153,11 @@ if(NOT MSDFGEN_CORE_ONLY) set_target_properties(msdfgen-ext PROPERTIES PUBLIC_HEADER "${MSDFGEN_EXT_HEADERS}") set_property(TARGET msdfgen-ext PROPERTY MSVC_RUNTIME_LIBRARY "${MSDFGEN_MSVC_RUNTIME}") target_compile_definitions(msdfgen-ext INTERFACE MSDFGEN_EXTENSIONS) + if(MSDFGEN_DYNAMIC_FREETYPE) + target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_DYNAMIC_FREETYPE) + else() + target_link_libraries(msdfgen-ext PRIVATE Freetype::Freetype) + endif() if(NOT MSDFGEN_DISABLE_SVG) target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_USE_TINYXML2) target_link_libraries(msdfgen-ext PRIVATE tinyxml2::tinyxml2) @@ -167,7 +173,7 @@ if(NOT MSDFGEN_CORE_ONLY) if(MSDFGEN_DISABLE_VARIABLE_FONTS) target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_DISABLE_VARIABLE_FONTS) endif() - target_link_libraries(msdfgen-ext PRIVATE Freetype::Freetype msdfgen::msdfgen-core) + target_link_libraries(msdfgen-ext PRIVATE msdfgen::msdfgen-core) target_include_directories(msdfgen-ext PUBLIC $ diff --git a/ext/freetype-loader.cpp b/ext/freetype-loader.cpp new file mode 100644 index 0000000..ba6a20e --- /dev/null +++ b/ext/freetype-loader.cpp @@ -0,0 +1,68 @@ +#include "freetype-loader.h" + +#include +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#define MSDFGEN_WINDOWS +#else +#include +#endif + +namespace msdfgen { + +struct DynamicLibrary { + void* handle; + + explicit DynamicLibrary(const std::vector& names) + : handle(nullptr) { + for(const auto& name : names) { +#if defined(MSDFGEN_WINDOWS) + // TODO: implement Windows +#else + handle = dlopen(name.c_str(), RTLD_LAZY); +#endif + if(handle) { + break; // If we loaded one, stop trying + } + } + } + + ~DynamicLibrary() { + if(handle) { +#if defined(MSDFGEN_WINDOWS) + // TODO: implement Windows +#else + dlclose(handle); +#endif + } + } + + [[nodiscard]] void* getFunction(const char* name) { +#if defined(MSDFGEN_WINDOWS) + return nullptr; // TODO: implement Windows +#else + return dlsym(handle, name); +#endif + } +}; + +[[nodiscard]] void* loadFreetypeFunction(const char* name) { + static DynamicLibrary library{{ +#ifdef MSDFGEN_WINDOWS + "freetype.dll", + "freetype6.dll" +#else +#if defined(__APPLE__) + "libfreetype.dylib", + "libfreetype.6.dylib" +#else + "libfreetype.so", + "libfreetype.so.6" +#endif +#endif + }}; + return library.getFunction(name); +} + +} \ No newline at end of file diff --git a/ext/freetype-loader.h b/ext/freetype-loader.h new file mode 100644 index 0000000..ee8cdbb --- /dev/null +++ b/ext/freetype-loader.h @@ -0,0 +1,8 @@ + +#pragma once + +namespace msdfgen { + +void* loadFreetypeFunction(const char* name); + +} \ No newline at end of file diff --git a/ext/import-font-dyn.cpp b/ext/import-font-dyn.cpp new file mode 100644 index 0000000..1282991 --- /dev/null +++ b/ext/import-font-dyn.cpp @@ -0,0 +1,642 @@ +/** + * This implementation of the msdfgen font importer offers exactly the same API + * but doesn't need to be statically linked against FreeType. This is especially + * useful if you want to use the msdfgen FreeType integration in a very portable + * environment like Java (JNI) or C# (P/Invoke). + */ + +#ifdef MSDFGEN_DYNAMIC_FREETYPE + +#include "import-font.h" +#include "freetype-loader.h" + +#include +#include + +#define FT_FACE_FLAG_MULTIPLE_MASTERS (1L << 8) +#define FT_LOAD_NO_SCALE 1L +#define FT_KERNING_UNSCALED 2 + +namespace msdfgen { + +// Minimum FreeType2 ABI used by msdfgen rebuilt from the FreeType headers +namespace { + using FT_Byte = unsigned char; + using FT_Short = signed short; + using FT_UShort = unsigned short; + using FT_Int = signed int; + using FT_Int32 = signed int; + using FT_UInt = unsigned int; + using FT_Long = signed long; + using FT_ULong = unsigned long; + using FT_String = char; + using FT_Pos = signed long; + using FT_Fixed = signed long; + using FT_Face = struct FT_FaceRec*; + using FT_Library = void*; + using FT_Encoding = int; + using FT_CharMap = struct FT_CharMapRec*; + using FT_Generic_Finalizer = void (*)(void* object); + using FT_GlyphSlot = struct FT_GlyphSlotRec*; + using FT_Size = struct FT_SizeRec*; + using FT_Glyph_Format = int; + using FT_SubGlyph = void*; + using FT_Slot_Internal = void*; + using FT_Face_Internal = void*; + using FT_Size_Internal = void*; + using FT_ListNode = struct FT_ListNodeRec*; + using FT_Error = int; + // Types we don't need to know the implementation for + using FT_Driver = void*; + using FT_Memory = void*; + using FT_Stream = void*; + + struct FT_Vector { + FT_Pos x; + FT_Pos y; + }; + + using FT_Outline_MoveToFunc = int (*)(const FT_Vector* to, void* user); + using FT_Outline_LineToFunc = int (*)(const FT_Vector* to, void* user); + using FT_Outline_ConicToFunc = int (*)(const FT_Vector* control, const FT_Vector* to, void* user); + using FT_Outline_CubicToFunc = int (*)(const FT_Vector* control1, const FT_Vector* control2, const FT_Vector* to, void* user); + + struct FT_Bitmap_Size { + FT_Short height; + FT_Short width; + FT_Pos size; + FT_Pos x_ppem; + FT_Pos y_ppem; + }; + + struct FT_CharMapRec { + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; + }; + + struct FT_Generic { + void* data; + FT_Generic_Finalizer finalizer; + }; + + struct FT_BBox { + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; + }; + + struct FT_Glyph_Metrics { + FT_Pos width; + FT_Pos height; + FT_Pos horiBearingX; + FT_Pos horiBearingY; + FT_Pos horiAdvance; + FT_Pos vertBearingX; + FT_Pos vertBearingY; + FT_Pos vertAdvance; + }; + + struct FT_Bitmap { + unsigned int rows; + unsigned int width; + int pitch; + unsigned char* buffer; + unsigned short num_grays; + unsigned char pixel_mode; + unsigned char palette_mode; + void* palette; + }; + + struct FT_Outline { + short n_contours; + short n_points; + FT_Vector* points; + char* tags; + short* contours; + int flags; + }; + + struct FT_GlyphSlotRec { + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt glyph_index; + FT_Generic generic; + FT_Glyph_Metrics metrics; + FT_Fixed linearHoriAdvance; + FT_Fixed linearVertAdvance; + FT_Vector advance; + FT_Glyph_Format format; + FT_Bitmap bitmap; + FT_Int bitmap_left; + FT_Int bitmap_top; + FT_Outline outline; + FT_UInt num_subglyphs; + FT_SubGlyph subglyphs; + void* control_data; + long control_len; + FT_Pos lsb_delta; + FT_Pos rsb_delta; + void* other; + FT_Slot_Internal internal; + }; + + struct FT_Size_Metrics { + FT_UShort x_ppem; + FT_UShort y_ppem; + FT_Fixed x_scale; + FT_Fixed y_scale; + FT_Pos ascender; + FT_Pos descender; + FT_Pos height; + FT_Pos max_advance; + }; + + struct FT_SizeRec { + FT_Face face; + FT_Generic generic; + FT_Size_Metrics metrics; + FT_Size_Internal internal; + }; + + struct FT_ListNodeRec { + FT_ListNode prev; + FT_ListNode next; + void* data; + }; + + struct FT_ListRec { + FT_ListNode head; + FT_ListNode tail; + }; + + struct FT_FaceRec { + FT_Long num_faces; + FT_Long face_index; + FT_Long face_flags; + FT_Long style_flags; + FT_Long num_glyphs; + FT_String* family_name; + FT_String* style_name; + FT_Int num_fixed_sizes; + FT_Bitmap_Size* available_sizes; + FT_Int num_charmaps; + FT_CharMap* charmaps; + FT_Generic generic; + FT_BBox bbox; + FT_UShort units_per_EM; + FT_Short ascender; + FT_Short descender; + FT_Short height; + FT_Short max_advance_width; + FT_Short max_advance_height; + FT_Short underline_position; + FT_Short underline_thickness; + FT_GlyphSlot glyph; + FT_Size size; + FT_CharMap charmap; + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + FT_ListRec sizes_list; + FT_Generic autohint; + void* extensions; + FT_Face_Internal internal; + }; + + struct FT_Outline_Funcs { + FT_Outline_MoveToFunc move_to; + FT_Outline_LineToFunc line_to; + FT_Outline_ConicToFunc conic_to; + FT_Outline_CubicToFunc cubic_to; + int shift; + FT_Pos delta; + }; + + struct FT_Var_Axis { + FT_String* name; + FT_Fixed minimum; + FT_Fixed def; + FT_Fixed maximum; + FT_ULong tag; + FT_UInt strid; + }; + + struct FT_Var_Named_Style { + FT_Fixed* coords; + FT_UInt strid; + FT_UInt psid; + }; + + struct FT_MM_Var { + FT_UInt num_axis; + FT_UInt num_designs; + FT_UInt num_namedstyles; + FT_Var_Axis* axis; + FT_Var_Named_Style* namedstyle; + }; +} + +#define F16DOT16_TO_DOUBLE(x) (1/65536.*double(x)) +#define DOUBLE_TO_F16DOT16(x) FT_Fixed(65536.*x) + +class FreetypeHandle { + friend FreetypeHandle *initializeFreetype(); + friend void deinitializeFreetype(FreetypeHandle *library); + friend FontHandle *loadFont(FreetypeHandle *library, const char *filename); + friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length); +#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS + friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate); + friend bool listFontVariationAxes(std::vector &axes, FreetypeHandle *library, FontHandle *font); +#endif + + FT_Library library; + +}; + +class FontHandle { + friend FontHandle *adoptFreetypeFontRaw(void *ftFace); + friend FontHandle *adoptFreetypeFont(FT_Face ftFace); + friend FontHandle *loadFont(FreetypeHandle *library, const char *filename); + friend FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length); + friend void destroyFont(FontHandle *font); + friend bool getFontMetrics(FontMetrics &metrics, FontHandle *font, FontCoordinateScaling coordinateScaling); + friend bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font, FontCoordinateScaling coordinateScaling); + friend bool getGlyphCount(unsigned &output, FontHandle *font); + friend bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode); + friend bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, FontCoordinateScaling coordinateScaling, double *outAdvance); + friend bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, FontCoordinateScaling coordinateScaling, double *outAdvance); + friend bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex0, GlyphIndex glyphIndex1, FontCoordinateScaling coordinateScaling); + friend bool getKerning(double &output, FontHandle *font, unicode_t unicode0, unicode_t unicode1, FontCoordinateScaling coordinateScaling); +#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS + friend bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate); + friend bool listFontVariationAxes(std::vector &axes, FreetypeHandle *library, FontHandle *font); +#endif + + FT_Face face; + bool ownership; + +}; + +struct FtContext { + double scale; + Point2 position; + Shape *shape; + Contour *contour; +}; + +static Point2 ftPoint2(const FT_Vector &vector, double scale) { + return Point2(scale*vector.x, scale*vector.y); +} + +static int ftMoveTo(const FT_Vector *to, void *user) { + FtContext *context = static_cast(user); + if (!(context->contour && context->contour->edges.empty())) + context->contour = &context->shape->addContour(); + context->position = ftPoint2(*to, context->scale); + return 0; +} + +static int ftLineTo(const FT_Vector *to, void *user) { + FtContext *context = static_cast(user); + Point2 endpoint = ftPoint2(*to, context->scale); + if (endpoint != context->position) { + context->contour->addEdge(EdgeHolder(context->position, endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ftConicTo(const FT_Vector *control, const FT_Vector *to, void *user) { + FtContext *context = static_cast(user); + Point2 endpoint = ftPoint2(*to, context->scale); + if (endpoint != context->position) { + context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control, context->scale), endpoint)); + context->position = endpoint; + } + return 0; +} + +static int ftCubicTo(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { + FtContext *context = static_cast(user); + Point2 endpoint = ftPoint2(*to, context->scale); + if (endpoint != context->position || crossProduct(ftPoint2(*control1, context->scale)-endpoint, ftPoint2(*control2, context->scale)-endpoint)) { + context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control1, context->scale), ftPoint2(*control2, context->scale), endpoint)); + context->position = endpoint; + } + return 0; +} + +static double getFontCoordinateScale(const FT_Face &face, FontCoordinateScaling coordinateScaling) { + switch (coordinateScaling) { + case FONT_SCALING_NONE: + return 1; + case FONT_SCALING_EM_NORMALIZED: + return 1./(face->units_per_EM ? face->units_per_EM : 1); + case FONT_SCALING_LEGACY: + return MSDFGEN_LEGACY_FONT_COORDINATE_SCALE; + } + return 1; +} + +// -------------------- Begin runtime loader code -------------------- + +namespace { + using FT_Init_FreeType_Ptr = FT_Error (*)(FT_Library* library); + using FT_Done_FreeType_Ptr = FT_Error (*)(FT_Library library); + using FT_Outline_Decompose_Ptr = FT_Error (*)(FT_Outline* outline, const FT_Outline_Funcs* funcs, void* user); + using FT_New_Face_Ptr = FT_Error (*)(FT_Library library, const char* filePath, FT_Long faceIndex, FT_Face* face); + using FT_New_Memory_Face_Ptr = FT_Error (*)(FT_Library library, const FT_Byte* base, FT_Long size, FT_Long faceIndex, FT_Face* face); + using FT_Done_Face_Ptr = FT_Error (*)(FT_Face face); + using FT_Load_Char_Ptr = FT_Error (*)(FT_Face face, FT_ULong charCode, FT_Int32 loadFlags); + using FT_Get_Char_Index_Ptr = FT_Error (*)(FT_Face face, FT_ULong charCode); + using FT_Load_Glyph_Ptr = FT_Error (*)(FT_Face face, FT_UInt glyphIndex, FT_Int32 loadFlags); + using FT_Get_Kerning_Ptr = FT_Error (*)(FT_Face face, FT_UInt left, FT_UInt right, FT_UInt kernMode, FT_Vector* kerning); + using FT_Get_MM_Var_Ptr = FT_Error (*)(FT_Face face, FT_MM_Var** master); + using FT_Get_Var_Design_Coordinates_Ptr = FT_Error (*)(FT_Face face, FT_UInt numCoords, FT_Fixed* coords); + using FT_Set_Var_Design_Coordinates_Ptr = FT_Error (*)(FT_Face face, FT_UInt numCoords, FT_Fixed* coords); + using FT_Done_MM_Var_Ptr = FT_Error (*)(FT_Library library, FT_MM_Var* master); + + struct FreetypeFunctions { + bool loaded; + FT_Init_FreeType_Ptr FT_Init_FreeType; + FT_Done_FreeType_Ptr FT_Done_FreeType; + FT_Outline_Decompose_Ptr FT_Outline_Decompose; + FT_New_Face_Ptr FT_New_Face; + FT_New_Memory_Face_Ptr FT_New_Memory_Face; + FT_Done_Face_Ptr FT_Done_Face; + FT_Load_Char_Ptr FT_Load_Char; + FT_Get_Char_Index_Ptr FT_Get_Char_Index; + FT_Load_Glyph_Ptr FT_Load_Glyph; + FT_Get_Kerning_Ptr FT_Get_Kerning; + FT_Get_MM_Var_Ptr FT_Get_MM_Var; + FT_Get_Var_Design_Coordinates_Ptr FT_Get_Var_Design_Coordinates; + FT_Set_Var_Design_Coordinates_Ptr FT_Set_Var_Design_Coordinates; + FT_Done_MM_Var_Ptr FT_Done_MM_Var; + }; + + FreetypeLoadCallback g_loadCallback = &loadFreetypeFunction; + FreetypeFunctions g_freetype {}; + +#define MSDFGEN_LOAD_FT_FUNCTION(n) g_freetype.n = reinterpret_cast(g_loadCallback(#n)) + + void ensureFunctionsLoaded() { + if(g_freetype.loaded) { + return; // Skip loading functions if we already did that at some point + } + MSDFGEN_LOAD_FT_FUNCTION(FT_Init_FreeType); + MSDFGEN_LOAD_FT_FUNCTION(FT_Done_FreeType); + MSDFGEN_LOAD_FT_FUNCTION(FT_Outline_Decompose); + MSDFGEN_LOAD_FT_FUNCTION(FT_New_Face); + MSDFGEN_LOAD_FT_FUNCTION(FT_New_Memory_Face); + MSDFGEN_LOAD_FT_FUNCTION(FT_Done_Face); + MSDFGEN_LOAD_FT_FUNCTION(FT_Load_Char); + MSDFGEN_LOAD_FT_FUNCTION(FT_Get_Char_Index); + MSDFGEN_LOAD_FT_FUNCTION(FT_Load_Glyph); + MSDFGEN_LOAD_FT_FUNCTION(FT_Get_Kerning); + MSDFGEN_LOAD_FT_FUNCTION(FT_Get_MM_Var); + MSDFGEN_LOAD_FT_FUNCTION(FT_Get_Var_Design_Coordinates); + MSDFGEN_LOAD_FT_FUNCTION(FT_Set_Var_Design_Coordinates); + MSDFGEN_LOAD_FT_FUNCTION(FT_Done_MM_Var); + g_freetype.loaded = true; + } + +#undef MSDFGEN_LOAD_FT_FUNCTION +} + +void setFreetypeLoadCallback(const FreetypeLoadCallback callback) { + g_loadCallback = callback; +} + +FreetypeLoadCallback getFreetypeLoadCallback() { + return g_loadCallback; +} + +// -------------------- End runtime loader code -------------------- + +GlyphIndex::GlyphIndex(unsigned index) : index(index) { } + +unsigned GlyphIndex::getIndex() const { + return index; +} + +FreetypeHandle *initializeFreetype() { + ensureFunctionsLoaded(); + FreetypeHandle *handle = new FreetypeHandle; + FT_Error error = g_freetype.FT_Init_FreeType(&handle->library); + if (error) { + delete handle; + return NULL; + } + return handle; +} + +void deinitializeFreetype(FreetypeHandle *library) { + ensureFunctionsLoaded(); + g_freetype.FT_Done_FreeType(library->library); + delete library; +} + +FontHandle *adoptFreetypeFontRaw(void *ftFace) { + ensureFunctionsLoaded(); + FontHandle *handle = new FontHandle; + handle->face = static_cast(ftFace); + handle->ownership = false; + return handle; +} + +FontHandle *adoptFreetypeFont(FT_Face ftFace) { + ensureFunctionsLoaded(); + FontHandle *handle = new FontHandle; + handle->face = ftFace; + handle->ownership = false; + return handle; +} + +FT_Error readFreetypeOutline(Shape &output, FT_Outline *outline, double scale) { + ensureFunctionsLoaded(); + output.contours.clear(); + output.inverseYAxis = false; + FtContext context = { }; + context.scale = scale; + context.shape = &output; + FT_Outline_Funcs ftFunctions; + ftFunctions.move_to = &ftMoveTo; + ftFunctions.line_to = &ftLineTo; + ftFunctions.conic_to = &ftConicTo; + ftFunctions.cubic_to = &ftCubicTo; + ftFunctions.shift = 0; + ftFunctions.delta = 0; + FT_Error error = g_freetype.FT_Outline_Decompose(outline, &ftFunctions, &context); + if (!output.contours.empty() && output.contours.back().edges.empty()) + output.contours.pop_back(); + return error; +} + +FontHandle *loadFont(FreetypeHandle *library, const char *filename) { + ensureFunctionsLoaded(); + if (!library) + return NULL; + FontHandle *handle = new FontHandle; + FT_Error error = g_freetype.FT_New_Face(library->library, filename, 0, &handle->face); + if (error) { + delete handle; + return NULL; + } + handle->ownership = true; + return handle; +} + +FontHandle *loadFontData(FreetypeHandle *library, const byte *data, int length) { + ensureFunctionsLoaded(); + if (!library) + return NULL; + FontHandle *handle = new FontHandle; + FT_Error error = g_freetype.FT_New_Memory_Face(library->library, data, length, 0, &handle->face); + if (error) { + delete handle; + return NULL; + } + handle->ownership = true; + return handle; +} + +void destroyFont(FontHandle *font) { + ensureFunctionsLoaded(); + if (font->ownership) + g_freetype.FT_Done_Face(font->face); + delete font; +} + +bool getFontMetrics(FontMetrics &metrics, FontHandle *font, FontCoordinateScaling coordinateScaling) { + double scale = getFontCoordinateScale(font->face, coordinateScaling); + metrics.emSize = scale*font->face->units_per_EM; + metrics.ascenderY = scale*font->face->ascender; + metrics.descenderY = scale*font->face->descender; + metrics.lineHeight = scale*font->face->height; + metrics.underlineY = scale*font->face->underline_position; + metrics.underlineThickness = scale*font->face->underline_thickness; + return true; +} + +bool getFontWhitespaceWidth(double &spaceAdvance, double &tabAdvance, FontHandle *font, FontCoordinateScaling coordinateScaling) { + ensureFunctionsLoaded(); + double scale = getFontCoordinateScale(font->face, coordinateScaling); + FT_Error error = g_freetype.FT_Load_Char(font->face, ' ', FT_LOAD_NO_SCALE); + if (error) + return false; + spaceAdvance = scale*font->face->glyph->advance.x; + error = g_freetype.FT_Load_Char(font->face, '\t', FT_LOAD_NO_SCALE); + if (error) + return false; + tabAdvance = scale*font->face->glyph->advance.x; + return true; +} + +bool getGlyphCount(unsigned &output, FontHandle *font) { + output = (unsigned) font->face->num_glyphs; + return true; +} + +bool getGlyphIndex(GlyphIndex &glyphIndex, FontHandle *font, unicode_t unicode) { + ensureFunctionsLoaded(); + glyphIndex = GlyphIndex(g_freetype.FT_Get_Char_Index(font->face, unicode)); + return glyphIndex.getIndex() != 0; +} + +bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, FontCoordinateScaling coordinateScaling, double *outAdvance) { + ensureFunctionsLoaded(); + if (!font) + return false; + FT_Error error = g_freetype.FT_Load_Glyph(font->face, glyphIndex.getIndex(), FT_LOAD_NO_SCALE); + if (error) + return false; + double scale = getFontCoordinateScale(font->face, coordinateScaling); + if (outAdvance) + *outAdvance = scale*font->face->glyph->advance.x; + return !readFreetypeOutline(output, &font->face->glyph->outline, scale); +} + +bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, FontCoordinateScaling coordinateScaling, double *outAdvance) { + ensureFunctionsLoaded(); + return loadGlyph(output, font, GlyphIndex(g_freetype.FT_Get_Char_Index(font->face, unicode)), coordinateScaling, outAdvance); +} + +bool loadGlyph(Shape &output, FontHandle *font, GlyphIndex glyphIndex, double *outAdvance) { + return loadGlyph(output, font, glyphIndex, FONT_SCALING_LEGACY, outAdvance); +} + +bool loadGlyph(Shape &output, FontHandle *font, unicode_t unicode, double *outAdvance) { + return loadGlyph(output, font, unicode, FONT_SCALING_LEGACY, outAdvance); +} + +bool getKerning(double &output, FontHandle *font, GlyphIndex glyphIndex0, GlyphIndex glyphIndex1, FontCoordinateScaling coordinateScaling) { + ensureFunctionsLoaded(); + FT_Vector kerning; + if (g_freetype.FT_Get_Kerning(font->face, glyphIndex0.getIndex(), glyphIndex1.getIndex(), FT_KERNING_UNSCALED, &kerning)) { + output = 0; + return false; + } + output = getFontCoordinateScale(font->face, coordinateScaling)*kerning.x; + return true; +} + +bool getKerning(double &output, FontHandle *font, unicode_t unicode0, unicode_t unicode1, FontCoordinateScaling coordinateScaling) { + ensureFunctionsLoaded(); + return getKerning(output, font, GlyphIndex(g_freetype.FT_Get_Char_Index(font->face, unicode0)), GlyphIndex(g_freetype.FT_Get_Char_Index(font->face, unicode1)), coordinateScaling); +} + +#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS + +bool setFontVariationAxis(FreetypeHandle *library, FontHandle *font, const char *name, double coordinate) { + ensureFunctionsLoaded(); + bool success = false; + if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *master = NULL; + if (g_freetype.FT_Get_MM_Var(font->face, &master)) + return false; + if (master && master->num_axis) { + std::vector coords(master->num_axis); + if (!g_freetype.FT_Get_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0])) { + for (FT_UInt i = 0; i < master->num_axis; ++i) { + if (!strcmp(name, master->axis[i].name)) { + coords[i] = DOUBLE_TO_F16DOT16(coordinate); + success = true; + break; + } + } + } + if (g_freetype.FT_Set_Var_Design_Coordinates(font->face, FT_UInt(coords.size()), &coords[0])) + success = false; + } + g_freetype.FT_Done_MM_Var(library->library, master); + } + return success; +} + +bool listFontVariationAxes(std::vector &axes, FreetypeHandle *library, FontHandle *font) { + ensureFunctionsLoaded(); + if (font->face->face_flags&FT_FACE_FLAG_MULTIPLE_MASTERS) { + FT_MM_Var *master = NULL; + if (g_freetype.FT_Get_MM_Var(font->face, &master)) + return false; + axes.resize(master->num_axis); + for (FT_UInt i = 0; i < master->num_axis; ++i) { + FontVariationAxis &axis = axes[i]; + axis.name = master->axis[i].name; + axis.minValue = F16DOT16_TO_DOUBLE(master->axis[i].minimum); + axis.maxValue = F16DOT16_TO_DOUBLE(master->axis[i].maximum); + axis.defaultValue = F16DOT16_TO_DOUBLE(master->axis[i].def); + } + g_freetype.FT_Done_MM_Var(library->library, master); + return true; + } + return false; +} + +#endif + +} + +#endif//MSDFGEN_DYNAMIC_FREETYPE diff --git a/ext/import-font.cpp b/ext/import-font.cpp index f10afce..c5580e5 100644 --- a/ext/import-font.cpp +++ b/ext/import-font.cpp @@ -1,3 +1,4 @@ +#ifndef MSDFGEN_DYNAMIC_FREETYPE #include "import-font.h" @@ -65,7 +66,7 @@ static Point2 ftPoint2(const FT_Vector &vector, double scale) { } static int ftMoveTo(const FT_Vector *to, void *user) { - FtContext *context = reinterpret_cast(user); + FtContext *context = static_cast(user); if (!(context->contour && context->contour->edges.empty())) context->contour = &context->shape->addContour(); context->position = ftPoint2(*to, context->scale); @@ -73,7 +74,7 @@ static int ftMoveTo(const FT_Vector *to, void *user) { } static int ftLineTo(const FT_Vector *to, void *user) { - FtContext *context = reinterpret_cast(user); + FtContext *context = static_cast(user); Point2 endpoint = ftPoint2(*to, context->scale); if (endpoint != context->position) { context->contour->addEdge(EdgeHolder(context->position, endpoint)); @@ -83,7 +84,7 @@ static int ftLineTo(const FT_Vector *to, void *user) { } static int ftConicTo(const FT_Vector *control, const FT_Vector *to, void *user) { - FtContext *context = reinterpret_cast(user); + FtContext *context = static_cast(user); Point2 endpoint = ftPoint2(*to, context->scale); if (endpoint != context->position) { context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control, context->scale), endpoint)); @@ -93,7 +94,7 @@ static int ftConicTo(const FT_Vector *control, const FT_Vector *to, void *user) } static int ftCubicTo(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) { - FtContext *context = reinterpret_cast(user); + FtContext *context = static_cast(user); Point2 endpoint = ftPoint2(*to, context->scale); if (endpoint != context->position || crossProduct(ftPoint2(*control1, context->scale)-endpoint, ftPoint2(*control2, context->scale)-endpoint)) { context->contour->addEdge(EdgeHolder(context->position, ftPoint2(*control1, context->scale), ftPoint2(*control2, context->scale), endpoint)); @@ -321,3 +322,5 @@ bool listFontVariationAxes(std::vector &axes, FreetypeHandle #endif } + +#endif//MSDFGEN_DYNAMIC_FREETYPE diff --git a/ext/import-font.h b/ext/import-font.h index 9a7aa3a..51dc8df 100644 --- a/ext/import-font.h +++ b/ext/import-font.h @@ -57,6 +57,15 @@ enum FontCoordinateScaling { FONT_SCALING_LEGACY }; +#ifdef MSDFGEN_DYNAMIC_FREETYPE +/// A function pointer type used for resolving FreeType library functions at runtime. +using FreetypeLoadCallback = void* (*)(const char* functionName); +/// Overrides the default dynamic FreeType load callback with a user defined one. +void setFreetypeLoadCallback(FreetypeLoadCallback callback); +/// Retrieves the current FreeType load callback. +FreetypeLoadCallback getFreetypeLoadCallback(); +#endif//MSDFGEN_DYNAMIC_FREETYPE + /// Initializes the FreeType library. FreetypeHandle *initializeFreetype(); /// Deinitializes the FreeType library. diff --git a/ext/msdfgen-ext-c.cpp b/ext/msdfgen-ext-c.cpp index 2d362bd..d40b549 100644 --- a/ext/msdfgen-ext-c.cpp +++ b/ext/msdfgen-ext-c.cpp @@ -26,6 +26,22 @@ extern "C" { +#ifdef MSDFGEN_DYNAMIC_FREETYPE // Runtime loader API + +MSDF_API int msdf_ft_set_load_callback(msdf_ft_load_callback callback) { + if(callback == nullptr) { + return MSDF_ERR_INVALID_ARG; + } + msdfgen::setFreetypeLoadCallback(callback); + return MSDF_SUCCESS; +} + +MSDF_API msdf_ft_load_callback msdf_ft_get_load_callback() { + return msdfgen::getFreetypeLoadCallback(); +} + +#endif + MSDF_API int msdf_ft_init(msdf_ft_handle* handle) { if(handle == nullptr) { return MSDF_ERR_INVALID_ARG; diff --git a/msdfgen-ext-c.h b/msdfgen-ext-c.h index fbbd1a4..2a21f4e 100644 --- a/msdfgen-ext-c.h +++ b/msdfgen-ext-c.h @@ -24,11 +24,30 @@ * @author Alexander Hinze */ +#ifdef __cplusplus +extern "C" { +#endif + MSDF_DEFINE_HANDLE_TYPE(msdf_ft); MSDF_DEFINE_HANDLE_TYPE(msdf_ft_font); -#ifdef __cplusplus -extern "C" { +#ifdef MSDFGEN_DYNAMIC_FREETYPE // Runtime loader API + +typedef void* (*msdf_ft_load_callback)(const char* functionName); + +/** + * Overrides the default load callback function used for resolving FreeTypee function at runtime. + * @param callback A pointer to the function to call for resolving FreeType functions at runtime. + * @returns @code MSDF_SUCCESS@endcode on success, otherwise one of the constants prefixed with @code MSDF_ERR_@endcode. + */ +MSDF_API int msdf_ft_set_load_callback(msdf_ft_load_callback callback); + +/** + * Retrieves the current FreeType load callback. + * @returns A pointer to the current FreeType load callback function. + */ +MSDF_API msdf_ft_load_callback msdf_ft_get_load_callback(); + #endif /**