2026-03-04 13:59:28 -06:00
|
|
|
#ifdef USE_FREETYPE2
|
|
|
|
|
#include <Mw/Milsko.h>
|
|
|
|
|
|
|
|
|
|
#include <ft2build.h>
|
|
|
|
|
#include FT_FREETYPE_H
|
|
|
|
|
|
2026-04-06 17:29:09 +09:00
|
|
|
static struct ft_table {
|
2026-03-31 12:44:32 -07:00
|
|
|
FT_Error (*FT_Init_FreeType)(FT_Library* alibrary);
|
|
|
|
|
FT_Error (*FT_New_Memory_Face)(FT_Library library,
|
|
|
|
|
const FT_Byte* file_base,
|
|
|
|
|
FT_Long file_size,
|
|
|
|
|
FT_Long face_index,
|
|
|
|
|
FT_Face* aface);
|
|
|
|
|
FT_Error (*FT_Set_Pixel_Sizes)(FT_Face face,
|
|
|
|
|
FT_UInt pixel_width,
|
|
|
|
|
FT_UInt pixel_height);
|
|
|
|
|
FT_Error (*FT_Load_Char)(FT_Face face,
|
|
|
|
|
FT_ULong char_code,
|
|
|
|
|
FT_Int32 load_flags);
|
2026-04-06 23:19:33 +09:00
|
|
|
FT_Error (*FT_Get_Kerning)(FT_Face face,
|
|
|
|
|
FT_UInt left_glyph,
|
|
|
|
|
FT_UInt right_glyph,
|
|
|
|
|
FT_UInt kern_mode,
|
|
|
|
|
FT_Vector* akerning);
|
2026-03-31 12:44:32 -07:00
|
|
|
FT_Error (*FT_Done_Face)(FT_Face face);
|
|
|
|
|
FT_Error (*FT_Done_FreeType)(FT_Library library);
|
|
|
|
|
|
|
|
|
|
} ft_table;
|
|
|
|
|
|
2026-03-04 13:59:28 -06:00
|
|
|
struct _MwFLFont {
|
|
|
|
|
FT_Library library;
|
|
|
|
|
FT_Face face;
|
|
|
|
|
void* data;
|
2026-04-06 23:27:27 +09:00
|
|
|
int px;
|
2026-03-04 13:59:28 -06:00
|
|
|
};
|
2026-04-06 17:29:09 +09:00
|
|
|
static int ft2_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color) {
|
2026-03-04 13:59:28 -06:00
|
|
|
int tw, th;
|
|
|
|
|
unsigned char* px;
|
|
|
|
|
MwLLPixmap p;
|
|
|
|
|
MwRect r;
|
|
|
|
|
int x = 0, y = 0;
|
|
|
|
|
|
2026-04-06 17:29:09 +09:00
|
|
|
tw = MwTextWidth(handle, ttf, text);
|
|
|
|
|
th = MwTextHeight(handle, ttf, text);
|
2026-03-04 13:59:28 -06:00
|
|
|
px = malloc(tw * th * 4);
|
|
|
|
|
|
|
|
|
|
memset(px, 0, tw * th * 4);
|
|
|
|
|
while(text[0] != 0) {
|
2026-04-06 23:19:33 +09:00
|
|
|
FT_Bitmap* bmp;
|
|
|
|
|
int cy, cx;
|
|
|
|
|
int c, c2;
|
|
|
|
|
FT_Vector vec;
|
|
|
|
|
const char* old_text;
|
2026-04-27 04:25:16 +09:00
|
|
|
|
|
|
|
|
text += MwUTF8ToUTF32(text, &c);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
2026-03-31 12:44:32 -07:00
|
|
|
ft_table.FT_Load_Char(ttf->face, c, FT_LOAD_RENDER);
|
2026-03-04 13:59:28 -06:00
|
|
|
bmp = &ttf->face->glyph->bitmap;
|
|
|
|
|
|
|
|
|
|
for(cy = 0; cy < bmp->rows; cy++) {
|
|
|
|
|
for(cx = 0; cx < bmp->width; cx++) {
|
2026-04-27 05:50:50 +09:00
|
|
|
int ox = x + cx + ttf->face->glyph->bitmap_left;
|
2026-04-06 23:27:27 +09:00
|
|
|
int oy = y + (ttf->face->height * ttf->px / ttf->face->units_per_EM) - ttf->face->glyph->bitmap_top + cy + (ttf->face->descender * ttf->px / ttf->face->units_per_EM);
|
2026-03-04 13:59:28 -06:00
|
|
|
unsigned char* opx = &px[(oy * tw + ox) * 4];
|
2026-04-14 22:53:20 -07:00
|
|
|
opx[0] = color->common.red;
|
|
|
|
|
opx[1] = color->common.green;
|
|
|
|
|
opx[2] = color->common.blue;
|
|
|
|
|
/* overflow check */
|
|
|
|
|
if(opx[3] + bmp->buffer[cy * bmp->pitch + cx] < opx[3]) {
|
|
|
|
|
opx[3] = 255;
|
2026-04-08 11:47:58 -07:00
|
|
|
} else {
|
|
|
|
|
opx[3] = bmp->buffer[cy * bmp->pitch + cx];
|
|
|
|
|
}
|
2026-03-04 13:59:28 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 03:39:02 +09:00
|
|
|
x += (ttf->face->glyph->advance.x >> 6);
|
2026-04-06 23:19:33 +09:00
|
|
|
|
|
|
|
|
old_text = text;
|
|
|
|
|
text += MwUTF8ToUTF32(text, &c2);
|
|
|
|
|
|
2026-04-27 04:04:04 +09:00
|
|
|
ft_table.FT_Get_Kerning(ttf->face, c, c2, FT_KERNING_DEFAULT, &vec);
|
2026-04-06 23:19:33 +09:00
|
|
|
|
2026-04-27 04:04:04 +09:00
|
|
|
x += vec.x >> 6;
|
2026-04-06 23:19:33 +09:00
|
|
|
|
|
|
|
|
text = old_text;
|
2026-03-04 13:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = MwLoadRaw(handle, px, tw, th);
|
|
|
|
|
r.x = point->x;
|
|
|
|
|
r.y = point->y - th / 2;
|
|
|
|
|
r.width = tw;
|
|
|
|
|
r.height = th;
|
|
|
|
|
|
|
|
|
|
MwLLDrawPixmap(handle->lowlevel, &r, p);
|
|
|
|
|
MwLLDestroyPixmap(p);
|
|
|
|
|
free(px);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 17:29:09 +09:00
|
|
|
static int ft2_MwTextWidth(MwFLFont ttf, const char* text) {
|
2026-04-27 11:58:53 +09:00
|
|
|
int tw = 0;
|
|
|
|
|
FT_Vector vec;
|
2026-03-04 13:59:28 -06:00
|
|
|
|
|
|
|
|
while(text[0] != 0) {
|
2026-04-06 23:19:33 +09:00
|
|
|
int c, c2;
|
|
|
|
|
const char* old_text;
|
|
|
|
|
|
|
|
|
|
text += MwUTF8ToUTF32(text, &c);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
2026-04-08 11:38:55 -07:00
|
|
|
ft_table.FT_Load_Char(ttf->face, c, FT_LOAD_DEFAULT);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
2026-04-27 03:48:15 +09:00
|
|
|
tw += ttf->face->glyph->advance.x >> 6;
|
2026-04-06 23:19:33 +09:00
|
|
|
|
|
|
|
|
old_text = text;
|
|
|
|
|
text += MwUTF8ToUTF32(text, &c2);
|
|
|
|
|
|
2026-04-27 03:39:02 +09:00
|
|
|
ft_table.FT_Get_Kerning(ttf->face, c, c2, FT_KERNING_DEFAULT, &vec);
|
2026-04-06 23:19:33 +09:00
|
|
|
|
2026-04-27 03:39:02 +09:00
|
|
|
tw += vec.x >> 6;
|
2026-04-06 23:19:33 +09:00
|
|
|
|
|
|
|
|
text = old_text;
|
2026-03-04 13:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 11:58:53 +09:00
|
|
|
if(tw > 0) {
|
|
|
|
|
tw -= vec.x >> 6;
|
|
|
|
|
tw -= ttf->face->glyph->advance.x >> 6;
|
|
|
|
|
tw += ttf->face->glyph->bitmap_left + ttf->face->glyph->bitmap.width;
|
|
|
|
|
}
|
2026-04-27 03:48:15 +09:00
|
|
|
|
2026-04-27 03:39:02 +09:00
|
|
|
return tw + 1;
|
2026-03-04 13:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-06 17:29:09 +09:00
|
|
|
static int ft2_MwTextHeight(MwFLFont ttf, int count) {
|
2026-04-06 23:27:27 +09:00
|
|
|
return ceil((ttf->face->height * ttf->px / ttf->face->units_per_EM) * count);
|
2026-03-04 13:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
2026-04-06 23:27:27 +09:00
|
|
|
static void* ft2_MwFontLoad(unsigned char* data, unsigned int size, int px) {
|
2026-03-04 13:59:28 -06:00
|
|
|
MwFLFont ttf = malloc(sizeof(*ttf));
|
|
|
|
|
ttf->data = malloc(size);
|
|
|
|
|
memcpy(ttf->data, data, size);
|
2026-03-31 12:44:32 -07:00
|
|
|
ft_table.FT_Init_FreeType(&ttf->library);
|
|
|
|
|
ft_table.FT_New_Memory_Face(ttf->library, ttf->data, size, 0, &ttf->face);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
2026-04-06 23:27:27 +09:00
|
|
|
ttf->px = px;
|
|
|
|
|
ft_table.FT_Set_Pixel_Sizes(ttf->face, 0, ttf->px);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
|
|
|
|
return ttf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ft2_MwFontFree(void* handle) {
|
|
|
|
|
MwFLFont ttf = handle;
|
|
|
|
|
|
2026-03-31 12:44:32 -07:00
|
|
|
ft_table.FT_Done_Face(ttf->face);
|
|
|
|
|
ft_table.FT_Done_FreeType(ttf->library);
|
2026-03-04 13:59:28 -06:00
|
|
|
|
|
|
|
|
free(ttf->data);
|
|
|
|
|
free(ttf);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 07:06:12 +09:00
|
|
|
int MWFL_FT2Setup(void) {
|
2026-03-31 12:44:32 -07:00
|
|
|
/* Try and load FreeType2. If it fails, return 1, signifying we default to stbtt. */
|
|
|
|
|
void* ftlib;
|
2026-04-29 13:45:32 +09:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
if(!(ftlib = MwDynamicOpen("libfreetype.dylib"))) {
|
|
|
|
|
#else
|
2026-03-31 12:44:32 -07:00
|
|
|
if(!(ftlib = MwDynamicOpen("libfreetype.so"))) {
|
2026-04-29 13:45:32 +09:00
|
|
|
#endif
|
2026-03-31 12:44:32 -07:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ft_table.FT_Init_FreeType = MwDynamicSymbol(ftlib, "FT_Init_FreeType");
|
|
|
|
|
ft_table.FT_New_Memory_Face = MwDynamicSymbol(ftlib, "FT_New_Memory_Face");
|
|
|
|
|
ft_table.FT_Set_Pixel_Sizes = MwDynamicSymbol(ftlib, "FT_Set_Pixel_Sizes");
|
|
|
|
|
ft_table.FT_Load_Char = MwDynamicSymbol(ftlib, "FT_Load_Char");
|
2026-04-06 23:19:33 +09:00
|
|
|
ft_table.FT_Get_Kerning = MwDynamicSymbol(ftlib, "FT_Get_Kerning");
|
2026-03-31 12:44:32 -07:00
|
|
|
ft_table.FT_Done_Face = MwDynamicSymbol(ftlib, "FT_Done_Face");
|
|
|
|
|
ft_table.FT_Done_FreeType = MwDynamicSymbol(ftlib, "FT_Done_FreeType");
|
|
|
|
|
|
2026-03-04 13:59:28 -06:00
|
|
|
MwFLDrawText = ft2_MwDrawText;
|
|
|
|
|
MwFLTextWidth = ft2_MwTextWidth;
|
|
|
|
|
MwFLTextHeight = ft2_MwTextHeight;
|
|
|
|
|
MwFLFontLoad = ft2_MwFontLoad;
|
|
|
|
|
MwFLFontFree = ft2_MwFontFree;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|