diff --git a/src/SConstruct b/src/SConstruct index 1663720..5cd207b 100644 --- a/src/SConstruct +++ b/src/SConstruct @@ -18,7 +18,7 @@ opts.Save(name + '.conf', env) # config -libs = ['gtk+-3.0', 'glib-2.0', 'cairo', 'SDL2_mixer'] +libs = ['gtk+-3.0', 'glib-2.0', 'cairo', 'cairo-ft', 'freetype2', 'SDL2_mixer'] # compute build options diff --git a/src/drawing.c b/src/drawing.c index bd4c12e..f2e244a 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -1,9 +1,29 @@ +#include +#include FT_FREETYPE_H + #include +#include #include "private.h" #include "drawing.h" + +typedef struct _HeliFontInstance { + char *path; + cairo_font_face_t *font_face; + int refcount; +} HeliFontInstance; + +struct _Font { + HeliFontInstance *instance; +}; + + +static int ftInitialized; +static FT_Library ftLibrary; +static HeliArray fontCache; + static double colorBack[4] = {1, 1, 1, 1}; static double colorFill[4] = {0.5, 0.5, 0.5, 1}; static double colorStroke[4] = {0, 0, 0, 1}; @@ -14,7 +34,8 @@ static size_t pathAllocated; static HAlign horAlign = HALIGN_LEFT; static VAlign vertAlign = VALIGN_TOP; -static char *font; +static char *fontFamily; +static Font font; static double fontSize = 24; @@ -142,21 +163,84 @@ void moveTo(double x, double y) { resetPath(); lineTo(x, y); } +static HeliFontInstance* loadFont(const char *path) { + static cairo_user_data_key_t key = {}; + + HeliFontInstance *fi = calloc(1, sizeof(*fi)); + fi->path = heliStringCopy(path); + + if (!ftInitialized) { + if (FT_Init_FreeType(&ftLibrary)) + printf("Cannot initialize FreeType library"); + ftInitialized = TRUE; + } + if (ftLibrary) { + FT_Face face; + if (FT_New_Face(ftLibrary, path, 0, &face)) { + printf("Cannot load font from file: %s", path); + } else { + fi->font_face = cairo_ft_font_face_create_for_ft_face(face, 0); + cairo_font_face_set_user_data(fi->font_face, &key, face, (cairo_destroy_func_t)FT_Done_Face); + } + } + + return fi; +} + +static void unloadFont(HeliFontInstance *fi) { + assert(!fi->refcount); + free(fi->path); + if (fi->font_face) cairo_font_face_destroy(fi->font_face); + free(fi); +} + +Font createFont(const char *path) { + Font f = calloc(1, sizeof(*f)); + f->instance = (HeliFontInstance*)heliStringmapGet(&fontCache, path); + if (!f->instance) { + f->instance = loadFont(path); + heliStringmapAdd(&fontCache, path, f->instance, (HeliFreeCallback)&unloadFont); + } + ++f->instance->refcount; + heliObjectRegister(f, (HeliFreeCallback)&fontDestroy); + return f; +} + +void fontDestroy(Font f) { + heliObjectUnregister(f); + if (font == f) font = NULL; + --f->instance->refcount; + if (f->instance->refcount <= 0) + heliStringmapRemove(&fontCache, f->instance->path); + free(f); +} + void textAlign(HAlign hor, VAlign vert) { horAlign = hor; vertAlign = vert; } -void textFont(const char *f) - { free(font); font = heliStringCopy(f); } +void textFontFamily(const char *family) + { font = NULL; free(fontFamily); fontFamily = heliStringCopy(family); } +void textFontDefault() + { font = NULL; free(fontFamily); fontFamily = NULL; } +void textFont(Font f) + { font = f; } void textSize(double size) { fontSize = size; } void text(const char *text, double x, double y) { resetPath(); - + cairo_t *cr = heliCairo; if (!cr) return; cairo_save(cr); - - if (font) cairo_select_font_face(cr, font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + if (font && font->instance->font_face) { + cairo_set_font_face(cr, font->instance->font_face); + } else + if (fontFamily) { + cairo_select_font_face(cr, fontFamily, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + } else { + cairo_set_font_face(cr, NULL); + } cairo_set_font_size(cr, fontSize); cairo_text_extents_t extents; @@ -172,6 +256,9 @@ void text(const char *text, double x, double y) { cairo_move_to(cr, x - extents.x_bearing, y - extents.y_bearing); cairo_show_text(cr, text); + if (font && font->instance->font_face) + cairo_set_font_face(cr, NULL); + cairo_restore(cr); } @@ -192,6 +279,10 @@ void heliDrawingFinish() { free(path); path = NULL; pathAllocated = 0; - free(font); - font = NULL; + free(fontFamily); + fontFamily = NULL; + heliArrayDestroy(&fontCache); + if (ftInitialized && ftLibrary) FT_Done_FreeType(ftLibrary); + ftLibrary = NULL; + ftInitialized = FALSE; } diff --git a/src/drawing.h b/src/drawing.h index 3fa6f4a..8eb30dc 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -4,6 +4,10 @@ #include "common.h" + +typedef struct _Font *Font; + + typedef enum _HAlign { HALIGN_LEFT, HALIGN_CENTER, @@ -27,10 +31,6 @@ char* rgb(double r, double g, double b); char* rgba(double r, double g, double b, double a); void rect(double x, double y, double width, double height); void ellipse(double x, double y, double width, double height); -void text(const char *text, double x, double y); -void textAlign(HAlign hor, VAlign vert); -void textFont(const char *font); -void textSize(double size); void arc(double x, double y, double w, double h, double start, double stop); void arcPath(double x, double y, double w, double h, double start, double stop); void line(double x1, double y1, double x2, double y2); @@ -43,6 +43,16 @@ void resetPath(); void closePath(); void strokePath(); +void text(const char *text, double x, double y); +void textAlign(HAlign hor, VAlign vert); +void textFontFamily(const char *family); +void textFontDefault(); +void textSize(double size); + +Font createFont(const char *path); +void fontDestroy(Font font); +void textFont(Font font); + #endif