From 34ac3e1a25d3b1541a7b32e89c63812b52c3edac Mon Sep 17 00:00:00 2001 From: blub Date: Wed, 23 Dec 2009 10:48:04 +0000 Subject: [PATCH] ... forgot to add the files, I'm too used to git now :P git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9642 d7cf8633-e32d-0410-b094-e92efae38249 --- ft2.c | 1114 ++++++++++++++++++++++++++++++++++++++++++++++++ ft2.h | 78 ++++ ft2_defs.h | 499 ++++++++++++++++++++++ ft2_fontdefs.h | 61 +++ utf8lib.c | 660 ++++++++++++++++++++++++++++ utf8lib.h | 46 ++ 6 files changed, 2458 insertions(+) create mode 100644 ft2.c create mode 100644 ft2.h create mode 100644 ft2_defs.h create mode 100644 ft2_fontdefs.h create mode 100644 utf8lib.c create mode 100644 utf8lib.h diff --git a/ft2.c b/ft2.c new file mode 100644 index 00000000..5e0462e6 --- /dev/null +++ b/ft2.c @@ -0,0 +1,1114 @@ +/* FreeType 2 and UTF-8 encoding support for + * DarkPlaces + */ +#include "quakedef.h" + +#include "ft2.h" +#include "ft2_defs.h" +#include "ft2_fontdefs.h" + +static int img_fontmap[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* +================================================================================ +CVars introduced with the freetype extension +================================================================================ +*/ + +cvar_t r_font_disable_freetype = {CVAR_SAVE, "r_font_disable_freetype", "1", "disable freetype support for fonts entirely"}; +cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", "use alpha-textures for font rendering, this should safe memory"}; +cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"}; + +/* +================================================================================ +Function definitions. Taken from the freetype2 headers. +================================================================================ +*/ + + +FT_EXPORT( FT_Error ) +(*qFT_Init_FreeType)( FT_Library *alibrary ); +FT_EXPORT( FT_Error ) +(*qFT_Done_FreeType)( FT_Library library ); +/* +FT_EXPORT( FT_Error ) +(*qFT_New_Face)( FT_Library library, + const char* filepathname, + FT_Long face_index, + FT_Face *aface ); +*/ +FT_EXPORT( FT_Error ) +(*qFT_New_Memory_Face)( FT_Library library, + const FT_Byte* file_base, + FT_Long file_size, + FT_Long face_index, + FT_Face *aface ); +FT_EXPORT( FT_Error ) +(*qFT_Done_Face)( FT_Face face ); +FT_EXPORT( FT_Error ) +(*qFT_Select_Size)( FT_Face face, + FT_Int strike_index ); +FT_EXPORT( FT_Error ) +(*qFT_Request_Size)( FT_Face face, + FT_Size_Request req ); +FT_EXPORT( FT_Error ) +(*qFT_Set_Char_Size)( FT_Face face, + FT_F26Dot6 char_width, + FT_F26Dot6 char_height, + FT_UInt horz_resolution, + FT_UInt vert_resolution ); +FT_EXPORT( FT_Error ) +(*qFT_Set_Pixel_Sizes)( FT_Face face, + FT_UInt pixel_width, + FT_UInt pixel_height ); +FT_EXPORT( FT_Error ) +(*qFT_Load_Glyph)( FT_Face face, + FT_UInt glyph_index, + FT_Int32 load_flags ); +FT_EXPORT( FT_Error ) +(*qFT_Load_Char)( FT_Face face, + FT_ULong char_code, + FT_Int32 load_flags ); +FT_EXPORT( FT_UInt ) +(*qFT_Get_Char_Index)( FT_Face face, + FT_ULong charcode ); +FT_EXPORT( FT_Error ) +(*qFT_Render_Glyph)( FT_GlyphSlot slot, + FT_Render_Mode render_mode ); +FT_EXPORT( FT_Error ) +(*qFT_Get_Kerning)( FT_Face face, + FT_UInt left_glyph, + FT_UInt right_glyph, + FT_UInt kern_mode, + FT_Vector *akerning ); +FT_EXPORT( FT_Error ) +(*qFT_Attach_Stream)( FT_Face face, + FT_Open_Args* parameters ); +/* +================================================================================ +Support for dynamically loading the FreeType2 library +================================================================================ +*/ + +static dllfunction_t ft2funcs[] = +{ + {"FT_Init_FreeType", (void **) &qFT_Init_FreeType}, + {"FT_Done_FreeType", (void **) &qFT_Done_FreeType}, + //{"FT_New_Face", (void **) &qFT_New_Face}, + {"FT_New_Memory_Face", (void **) &qFT_New_Memory_Face}, + {"FT_Done_Face", (void **) &qFT_Done_Face}, + {"FT_Select_Size", (void **) &qFT_Select_Size}, + {"FT_Request_Size", (void **) &qFT_Request_Size}, + {"FT_Set_Char_Size", (void **) &qFT_Set_Char_Size}, + {"FT_Set_Pixel_Sizes", (void **) &qFT_Set_Pixel_Sizes}, + {"FT_Load_Glyph", (void **) &qFT_Load_Glyph}, + {"FT_Load_Char", (void **) &qFT_Load_Char}, + {"FT_Get_Char_Index", (void **) &qFT_Get_Char_Index}, + {"FT_Render_Glyph", (void **) &qFT_Render_Glyph}, + {"FT_Get_Kerning", (void **) &qFT_Get_Kerning}, + {"FT_Attach_Stream", (void **) &qFT_Attach_Stream}, + {NULL, NULL} +}; + +/// Handle for FreeType2 DLL +static dllhandle_t ft2_dll = NULL; + +/// Memory pool for fonts +static mempool_t *font_mempool= NULL; +static rtexturepool_t *font_texturepool = NULL; + +/// FreeType library handle +static FT_Library font_ft2lib = NULL; + +/* +==================== +Font_CloseLibrary + +Unload the FreeType2 DLL +==================== +*/ +void Font_CloseLibrary (void) +{ + if (font_mempool) + Mem_FreePool(&font_mempool); + if (font_texturepool) + R_FreeTexturePool(&font_texturepool); + if (font_ft2lib && qFT_Done_FreeType) + { + qFT_Done_FreeType(font_ft2lib); + font_ft2lib = NULL; + } + Sys_UnloadLibrary (&ft2_dll); +} + +/* +==================== +Font_OpenLibrary + +Try to load the FreeType2 DLL +==================== +*/ +qboolean Font_OpenLibrary (void) +{ + const char* dllnames [] = + { +#if defined(WIN64) + #error path for freetype 2 dll +#elif defined(WIN32) + #error path for freetype 2 dll +#elif defined(MACOSX) + "libfreetype.dylib", +#else + "libfreetype.so.6", + "libfreetype.so", +#endif + NULL + }; + + if (r_font_disable_freetype.integer) + return false; + + // Already loaded? + if (ft2_dll) + return true; + + // Load the DLL + if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs)) + return false; + return true; +} + +/* +==================== +Font_Init + +Initialize the freetype2 font subsystem +==================== +*/ + +void font_start(void) +{ + if (!Font_OpenLibrary()) + return; + + if (qFT_Init_FreeType(&font_ft2lib)) + { + Con_Print("ERROR: Failed to initialize the FreeType2 library!\n"); + Font_CloseLibrary(); + return; + } + + font_mempool = Mem_AllocPool("FONT", 0, NULL); + if (!font_mempool) + { + Con_Print("ERROR: Failed to allocate FONT memory pool!\n"); + Font_CloseLibrary(); + return; + } + + font_texturepool = R_AllocTexturePool(); + if (!font_texturepool) + { + Con_Print("ERROR: Failed to allocate FONT texture pool!\n"); + Font_CloseLibrary(); + return; + } +} + +void font_shutdown(void) +{ + int i; + for (i = 0; i < MAX_FONTS; ++i) + { + if (dp_fonts[i].ft2) + { + Font_UnloadFont(dp_fonts[i].ft2); + dp_fonts[i].ft2 = NULL; + } + } + Font_CloseLibrary(); +} + +void font_newmap(void) +{ +} + +void Font_Init(void) +{ + Cvar_RegisterVariable(&r_font_disable_freetype); + Cvar_RegisterVariable(&r_font_use_alpha_textures); + Cvar_RegisterVariable(&r_font_size_snapping); +} + +/* +================================================================================ +Implementation of a more or less lazy font loading and rendering code. +================================================================================ +*/ + +#include "ft2_fontdefs.h" + +ft2_font_t *Font_Alloc(void) +{ + if (!ft2_dll) + return NULL; + return Mem_Alloc(font_mempool, sizeof(ft2_font_t)); +} + +qboolean Font_Attach(ft2_font_t *font, ft2_attachment_t *attachment) +{ + ft2_attachment_t *na; + + font->attachmentcount++; + na = (ft2_attachment_t*)Mem_Alloc(font_mempool, sizeof(font->attachments[0]) * font->attachmentcount); + if (na == NULL) + return false; + if (font->attachments && font->attachmentcount > 1) + { + memcpy(na, font->attachments, sizeof(font->attachments[0]) * (font->attachmentcount - 1)); + Mem_Free(font->attachments); + } + memcpy(na + sizeof(font->attachments[0]) * (font->attachmentcount - 1), attachment, sizeof(*attachment)); + font->attachments = na; + return true; +} + +static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font); +static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning); +qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt) +{ + int s, count, i; + ft2_font_t *ft2, *fbfont, *fb; + + ft2 = Font_Alloc(); + if (!ft2) + { + dpfnt->ft2 = NULL; + return false; + } + + // check if a fallback font has been specified, if it has been, and the + // font fails to load, use the image font as main font + for (i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + if (dpfnt->fallbacks[i][0]) + break; + } + + if (!Font_LoadFile(name, dpfnt->req_face, ft2)) + { + if (i >= MAX_FONT_FALLBACKS) + { + dpfnt->ft2 = NULL; + Mem_Free(ft2); + return false; + } + strlcpy(ft2->name, name, sizeof(ft2->name)); + ft2->image_font = true; + ft2->has_kerning = false; + } + else + { + ft2->image_font = false; + } + + // attempt to load fallback fonts: + fbfont = ft2; + for (i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + if (!dpfnt->fallbacks[i][0]) + break; + if (! (fb = Font_Alloc()) ) + { + Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name); + break; + } + if (!Font_LoadFile(dpfnt->fallbacks[i], dpfnt->fallback_faces[i], fb)) + { + Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name); + Mem_Free(fb); + break; + } + count = 0; + for (s = 0; s < MAX_FONT_SIZES; ++s) + { + if (Font_LoadSize(fb, dpfnt->req_sizes[s], true, false)) + ++count; + } + if (!count) + { + Con_Printf("Failed to allocate font for fallback %i of font %s\n", i, name); + Font_UnloadFont(fb); + Mem_Free(fb); + break; + } + // at least one size of the fallback font loaded successfully + // link it: + fbfont->next = fb; + fbfont = fb; + } + + if (fbfont == ft2 && ft2->image_font) + { + // no fallbacks were loaded successfully: + dpfnt->ft2 = NULL; + Mem_Free(ft2); + return false; + } + + count = 0; + for (s = 0; s < MAX_FONT_SIZES; ++s) + { + if (Font_LoadSize(ft2, dpfnt->req_sizes[s], false, false)) + ++count; + } + if (!count) + { + // loading failed for every requested size + Font_UnloadFont(ft2); + Mem_Free(ft2); + dpfnt->ft2 = NULL; + return false; + } + + //Con_Printf("%i sizes loaded\n", count); + dpfnt->ft2 = ft2; + return true; +} + +static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font) +{ + size_t namelen; + char filename[PATH_MAX]; + int status; + size_t i; + unsigned char *data; + fs_offset_t datasize; + + memset(font, 0, sizeof(*font)); + + if (!Font_OpenLibrary()) + { + if (!r_font_disable_freetype.integer) + { + Con_Printf("WARNING: can't open load font %s\n" + "You need the FreeType2 DLL to load font files\n", + name); + } + return false; + } + + namelen = strlen(name); + + memcpy(filename, name, namelen); + memcpy(filename + namelen, ".ttf", 5); + data = FS_LoadFile(filename, font_mempool, false, &datasize); + if (!data) + { + memcpy(filename + namelen, ".otf", 5); + data = FS_LoadFile(filename, font_mempool, false, &datasize); + } + if (!data) + { + ft2_attachment_t afm; + + memcpy(filename + namelen, ".pfb", 5); + data = FS_LoadFile(filename, font_mempool, false, &datasize); + + if (data) + { + memcpy(filename + namelen, ".afm", 5); + afm.data = FS_LoadFile(filename, font_mempool, false, &afm.size); + + if (afm.data) + Font_Attach(font, &afm); + } + } + + if (!data) + { + // FS_LoadFile being not-quiet should print an error :) + return false; + } + Con_Printf("Loading font %s face %i...\n", filename, _face); + + status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, _face, (FT_Face*)&font->face); + if (status && _face != 0) + { + Con_Printf("Failed to load face %i of %s. Falling back to face 0\n", _face, name); + _face = 0; + status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)data, datasize, 0, (FT_Face*)&font->face); + } + if (status) + { + Con_Printf("ERROR: can't create face for %s\n" + "Error %i\n", // TODO: error strings + name, status); + Font_UnloadFont(font); + return false; + } + + // add the attachments + for (i = 0; i < font->attachmentcount; ++i) + { + FT_Open_Args args; + memset(&args, 0, sizeof(args)); + args.flags = FT_OPEN_MEMORY; + args.memory_base = (const FT_Byte*)font->attachments[i].data; + args.memory_size = font->attachments[i].size; + if (qFT_Attach_Stream(font->face, &args)) + Con_Printf("Failed to add attachment %u to %s\n", (unsigned)i, font->name); + } + + memcpy(font->name, name, namelen+1); + font->image_font = false; + font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING); + return true; +} + +static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap); +static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning) +{ + int map_index; + ft2_font_map_t *fmap, temp; + + if (IS_NAN(size)) + size = 0; + + if (!size) + size = 16; + if (size < 2) // bogus sizes are not allowed - and they screw up our allocations + return false; + + if (!no_texture) + { + for (map_index = 0; map_index < MAX_FONT_SIZES; ++map_index) + { + if (!font->font_maps[map_index]) + break; + // if a similar size has already been loaded, ignore this one + //abs(font->font_maps[map_index]->size - size) < 4 + if (font->font_maps[map_index]->size == size) + return true; + } + + if (map_index >= MAX_FONT_SIZES) + return false; + + memset(&temp, 0, sizeof(temp)); + temp.size = size; + temp.glyphSize = CeilPowerOf2(size*2); + temp.sfx = (1.0/64.0)/(double)size; + temp.sfy = (1.0/64.0)/(double)size; + temp.intSize = -1; // negative value: LoadMap must search now :) + if (!Font_LoadMap(font, &temp, 0, &fmap)) + { + Con_Printf("ERROR: can't load the first character map for %s\n" + "This is fatal\n", + font->name); + Font_UnloadFont(font); + return false; + } + font->font_maps[map_index] = temp.next; + + fmap->sfx = temp.sfx; + fmap->sfy = temp.sfy; + } + if (!no_kerning) + { + // load the default kerning vector: + if (font->has_kerning) + { + Uchar l, r; + FT_Vector kernvec; + for (l = 0; l < 256; ++l) + { + for (r = 0; r < 256; ++r) + { + FT_ULong ul, ur; + ul = qFT_Get_Char_Index(font->face, l); + ur = qFT_Get_Char_Index(font->face, r); + if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec)) + { + fmap->kerning.kerning[l][r][0] = 0; + fmap->kerning.kerning[l][r][1] = 0; + } + else + { + fmap->kerning.kerning[l][r][0] = (kernvec.x >> 6) / fmap->size; + fmap->kerning.kerning[l][r][1] = (kernvec.y >> 6) / fmap->size; + } + } + } + } + } + + return true; +} + +int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) +{ + int match = -1; + int value = 1000000; + int nval; + int matchsize = -10000; + int m; + int size; + float fsize; + ft2_font_map_t **maps = font->font_maps; + + fsize = _fsize * vid.height / vid_conheight.value; + + if (fsize < 0) + size = 16; + else + { + // round up + size = (int)fsize; + if (fsize - (float)size >= 0.49) + ++size; + } + + for (m = 0; m < MAX_FONT_SIZES; ++m) + { + if (!maps[m]) + continue; + // "round up" to the bigger size if two equally-valued matches exist + nval = abs(maps[m]->size - size); + if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size)) + { + value = nval; + match = m; + matchsize = maps[m]->size; + if (value == 0) // there is no better match + break; + } + } + if (r_font_size_snapping.integer && value <= 1) + { + if (outw && outh) + { + if (!*outh) *outh = *outw; + if (!*outw) *outw = *outh; + } + // keep the aspect + if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height; + if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width * *outw / _fsize; + } + return match; +} + +ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index) +{ + if (index < 0 || index >= MAX_FONT_SIZES) + return NULL; + return font->font_maps[index]; +} + +static qboolean Font_SetSize(ft2_font_t *font, float w, float h) +{ + if (font->currenth == h && + ((!w && (!font->currentw || font->currentw == font->currenth)) || // check if w==h when w is not set + font->currentw == w)) // same size has been requested + { + return true; + } + // sorry, but freetype doesn't seem to care about other sizes + w = (int)w; + h = (int)h; + if (font->image_font) + { + if (qFT_Set_Char_Size((FT_Face)font->next->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72)) + return false; + } + else + { + if (qFT_Set_Char_Size((FT_Face)font->face, (FT_F26Dot6)(w*64), (FT_F26Dot6)(h*64), 72, 72)) + return false; + } + font->currentw = w; + font->currenth = h; + return true; +} + +qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy) +{ + ft2_font_map_t *fmap; + if (!font->has_kerning) + return false; + if (map_index < 0 || map_index >= MAX_FONT_SIZES) + return false; + fmap = font->font_maps[map_index]; + if (!fmap) + return false; + if (left < 256 && right < 256) + { + // quick-kerning, be aware of the size: scale it + if (outx) *outx = fmap->kerning.kerning[left][right][0] * w / (float)fmap->size; + if (outy) *outy = fmap->kerning.kerning[left][right][1] * h / (float)fmap->size; + return true; + } + else + { + FT_Vector kernvec; + FT_ULong ul, ur; + + //if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size)) + if (!Font_SetSize(font, w, h)) + { + // this deserves an error message + Con_Printf("Failed to get kerning for %s\n", font->name); + return false; + } + ul = qFT_Get_Char_Index(font->face, left); + ur = qFT_Get_Char_Index(font->face, right); + if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec)) + { + if (outx) *outx = kernvec.x * fmap->sfx; + if (outy) *outy = kernvec.y * fmap->sfy; + return true; + } + return false; + } +} + +qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy) +{ + return Font_GetKerningForMap(font, Font_IndexForSize(font, h, NULL, NULL), w, h, left, right, outx, outy); +} + +static void UnloadMapRec(ft2_font_map_t *map) +{ + if (map->texture) + { + R_FreeTexture(map->texture); + map->texture = NULL; + } + if (map->next) + UnloadMapRec(map->next); + Mem_Free(map); +} + +void Font_UnloadFont(ft2_font_t *font) +{ + int i; + if (font->attachments && font->attachmentcount) + { + Mem_Free(font->attachments); + font->attachmentcount = 0; + font->attachments = NULL; + } + for (i = 0; i < MAX_FONT_SIZES; ++i) + { + if (font->font_maps[i]) + { + UnloadMapRec(font->font_maps[i]); + font->font_maps[i] = NULL; + } + } + if (ft2_dll) + { + if (font->face) + { + qFT_Done_Face((FT_Face)font->face); + font->face = NULL; + } + } +} + +static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ch, ft2_font_map_t **outmap) +{ + char map_identifier[PATH_MAX]; + unsigned long mapidx = _ch / FONT_CHARS_PER_MAP; + unsigned char *data; + FT_ULong ch, mapch; + int status; + int tp; + + int pitch; + int gR, gC; // glyph position: row and column + + ft2_font_map_t *map, *next; + ft2_font_t *usefont; + + FT_Face fontface; + + int bytesPerPixel = 4; // change the conversion loop too if you change this! + + if (outmap) + *outmap = NULL; + + if (r_font_use_alpha_textures.integer) + bytesPerPixel = 1; + + if (font->image_font) + fontface = (FT_Face)font->next->face; + else + fontface = (FT_Face)font->face; + + //status = qFT_Set_Pixel_Sizes((FT_Face)font->face, /*size*/0, mapstart->size); + //if (status) + if (font->image_font && mapstart->intSize < 0) + mapstart->intSize = mapstart->size; + if (mapstart->intSize < 0) + { + mapstart->intSize = mapstart->size; + while (1) + { + if (!Font_SetSize(font, mapstart->intSize, mapstart->intSize)) + { + Con_Printf("ERROR: can't set size for font %s: %f ((%f))\n", font->name, mapstart->size, mapstart->intSize); + return false; + } + if ((fontface->size->metrics.height>>6) <= mapstart->size) + break; + if (mapstart->intSize < 2) + { + Con_Printf("ERROR: no appropriate size found for font %s: %f\n", font->name, mapstart->size); + return false; + } + --mapstart->intSize; + } + if (developer.integer) + Con_Printf("Using size: %f for requested size %f\n", mapstart->intSize, mapstart->size); + } + + if (!font->image_font && !Font_SetSize(font, mapstart->intSize, mapstart->intSize)) + { + Con_Printf("ERROR: can't set sizes for font %s: %f\n", font->name, mapstart->size); + return false; + } + + map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t)); + if (!map) + { + Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name); + return false; + } + + // copy over the information + map->size = mapstart->size; + map->intSize = mapstart->intSize; + map->glyphSize = mapstart->glyphSize; + map->sfx = mapstart->sfx; + map->sfy = mapstart->sfy; + + pitch = map->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel; + data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * map->glyphSize) * pitch); + if (!data) + { + Con_Printf("ERROR: Failed to allocate memory for font %s size %g\n", font->name, map->size); + Mem_Free(map); + return false; + } + + // initialize as white texture with zero alpha + tp = 0; + while (tp < (FONT_CHAR_LINES * map->glyphSize) * pitch) + { + if (bytesPerPixel == 4) + { + data[tp++] = 0xFF; + data[tp++] = 0xFF; + data[tp++] = 0xFF; + } + data[tp++] = 0x00; + } + + // insert the map + map->start = mapidx * FONT_CHARS_PER_MAP; + next = mapstart; + while(next->next && next->next->start < map->start) + next = next->next; + map->next = next->next; + next->next = map; + + gR = 0; + gC = -1; + for (ch = map->start; + ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP; + ++ch) + { + FT_ULong glyphIndex; + int w, h, x, y; + FT_GlyphSlot glyph; + FT_Bitmap *bmp; + unsigned char *imagedata, *dst, *src; + glyph_slot_t *mapglyph; + FT_Face face; + + mapch = ch - map->start; + + if (developer.integer) + Con_Print("glyphinfo: ------------- GLYPH INFO -----------------\n"); + + ++gC; + if (gC >= FONT_CHARS_PER_LINE) + { + gC -= FONT_CHARS_PER_LINE; + ++gR; + } + + imagedata = data + gR * pitch * map->glyphSize + gC * map->glyphSize * bytesPerPixel; + //status = qFT_Load_Char(face, ch, FT_LOAD_RENDER); + // we need the glyphIndex + face = font->face; + usefont = NULL; + if (font->image_font && mapch == ch && img_fontmap[mapch]) + { + map->glyphs[mapch].image = true; + continue; + } + glyphIndex = qFT_Get_Char_Index(face, ch); + if (glyphIndex == 0) + { + // by convention, 0 is the "missing-glyph"-glyph + // try to load from a fallback font + for(usefont = font->next; usefont != NULL; usefont = usefont->next) + { + if (!Font_SetSize(usefont, mapstart->intSize, mapstart->intSize)) + continue; + // try that glyph + face = usefont->face; + glyphIndex = qFT_Get_Char_Index(face, ch); + if (glyphIndex == 0) + continue; + status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER); + if (status) + continue; + break; + } + if (!usefont) + { + //Con_Printf("failed to load fallback glyph for char %lx from font %s\n", (unsigned long)ch, font->name); + // now we let it use the "missing-glyph"-glyph + face = font->face; + glyphIndex = 0; + } + } + + if (!usefont) + { + usefont = font; + face = font->face; + status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER); + if (status) + { + //Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name); + Con_Printf("failed to load glyph for char %lx from font %s\n", (unsigned long)ch, font->name); + continue; + } + } + + glyph = face->glyph; + bmp = &glyph->bitmap; + + w = bmp->width; + h = bmp->rows; + + if (w > map->glyphSize || h > map->glyphSize) { + Con_Printf("WARNING: Glyph %lu is too big in font %s, size %g: %i x %i\n", ch, font->name, map->size, w, h); + if (w > map->glyphSize) + w = map->glyphSize; + if (h > map->glyphSize) + h = map->glyphSize; + } + + switch (bmp->pixel_mode) + { + case FT_PIXEL_MODE_MONO: + if (developer.integer) + Con_Print("glyphinfo: Pixel Mode: MONO\n"); + break; + case FT_PIXEL_MODE_GRAY2: + if (developer.integer) + Con_Print("glyphinfo: Pixel Mode: GRAY2\n"); + break; + case FT_PIXEL_MODE_GRAY4: + if (developer.integer) + Con_Print("glyphinfo: Pixel Mode: GRAY4\n"); + break; + case FT_PIXEL_MODE_GRAY: + if (developer.integer) + Con_Print("glyphinfo: Pixel Mode: GRAY\n"); + break; + default: + if (developer.integer) + Con_Printf("glyphinfo: Pixel Mode: Unknown: %i\n", bmp->pixel_mode); + Mem_Free(data); + Con_Printf("ERROR: Unrecognized pixel mode for font %s size %f: %i\n", font->name, mapstart->size, bmp->pixel_mode); + return false; + } + for (y = 0; y < h; ++y) + { + dst = imagedata + y * pitch; + src = bmp->buffer + y * bmp->pitch; + + switch (bmp->pixel_mode) + { + case FT_PIXEL_MODE_MONO: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 8) + { + unsigned char ch = *src++; + *dst = 255 * ((ch & 0x80) >> 7); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x40) >> 6); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x20) >> 5); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x10) >> 4); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x08) >> 3); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x04) >> 2); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x02) >> 1); dst += bytesPerPixel; + *dst = 255 * ((ch & 0x01) >> 0); dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY2: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 4) + { + unsigned char ch = *src++; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY4: + dst += bytesPerPixel - 1; // shift to alpha byte + for (x = 0; x < bmp->width; x += 2) + { + unsigned char ch = *src++; + *dst = ( ((ch & 0xF0) >> 4) * 0x24); dst += bytesPerPixel; + *dst = ( ((ch & 0x0F) ) * 0x24); dst += bytesPerPixel; + } + break; + case FT_PIXEL_MODE_GRAY: + // in this case pitch should equal width + for (tp = 0; tp < bmp->pitch; ++tp) + dst[(bytesPerPixel - 1) + tp*bytesPerPixel] = src[tp]; // copy the grey value into the alpha bytes + + //memcpy((void*)dst, (void*)src, bmp->pitch); + //dst += bmp->pitch; + break; + default: + break; + } + } + + // now fill map->glyphs[ch - map->start] + mapglyph = &map->glyphs[mapch]; + + { + // old way + // double advance = (double)glyph->metrics.horiAdvance * map->sfx; + + double bearingX = (glyph->metrics.horiBearingX >> 6) / map->size; + double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size; + double advance = (glyph->advance.x >> 6) / map->size; + double mWidth = (glyph->metrics.width >> 6) / map->size; + double mHeight = (glyph->metrics.height >> 6) / map->size; + + mapglyph->vxmin = bearingX; + mapglyph->vxmax = bearingX + mWidth; + mapglyph->vymin = -bearingY; + mapglyph->vymax = mHeight - bearingY; + mapglyph->txmin = ( (double)(gC * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) ); + mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(map->glyphSize * FONT_CHARS_PER_LINE) ); + mapglyph->tymin = ( (double)(gR * map->glyphSize) ) / ( (double)(map->glyphSize * FONT_CHAR_LINES) ); + mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(map->glyphSize * FONT_CHAR_LINES) ); + //mapglyph->advance_x = advance * usefont->size; + mapglyph->advance_x = advance; + mapglyph->advance_y = 0; + + if (developer.integer) + { + Con_Printf("glyphinfo: Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR); + Con_Printf("glyphinfo: %f, %f, %lu\n", bearingX, map->sfx, (unsigned long)glyph->metrics.horiBearingX); + if (ch >= 32 && ch <= 128) + Con_Printf("glyphinfo: Character: %c\n", (int)ch); + Con_Printf("glyphinfo: Vertex info:\n"); + Con_Printf("glyphinfo: X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax); + Con_Printf("glyphinfo: Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax); + Con_Printf("glyphinfo: Texture info:\n"); + Con_Printf("glyphinfo: S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax); + Con_Printf("glyphinfo: T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax); + Con_Printf("glyphinfo: Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y); + } + } + map->glyphs[mapch].image = false; + } + + // create a texture from the data now + + if (developer.integer) + { + // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter) + dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP); + FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * map->glyphSize); + } + dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP); + + // probably use bytesPerPixel here instead? + if (r_font_use_alpha_textures.integer) + { + map->texture = R_LoadTexture2D(font_texturepool, map_identifier, + map->glyphSize * FONT_CHARS_PER_LINE, + map->glyphSize * FONT_CHAR_LINES, + data, TEXTYPE_ALPHA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL); + } else { + map->texture = R_LoadTexture2D(font_texturepool, map_identifier, + map->glyphSize * FONT_CHARS_PER_LINE, + map->glyphSize * FONT_CHAR_LINES, + data, TEXTYPE_RGBA, TEXF_ALPHA /*gone: | TEXF_ALWAYSPRECACHE*/ /* | TEXF_MIPMAP*/, NULL); + } + + Mem_Free(data); + if (!map->texture) + { + // if the first try isn't successful, keep it with a broken texture + // otherwise we retry to load it every single frame where ft2 rendering is used + // this would be bad... + // only `data' must be freed + Con_Printf("ERROR: Failed to generate texture for font %s size %f map %lu\n", + font->name, mapstart->size, mapidx); + return false; + } + if (outmap) + *outmap = map; + return true; +} + +qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap) +{ + if (map_index < 0 || map_index >= MAX_FONT_SIZES) + return false; + // the first map must have been loaded already + if (!font->font_maps[map_index]) + return false; + return Font_LoadMap(font, font->font_maps[map_index], _ch, outmap); +} + +ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch) +{ + while (start && start->start + FONT_CHARS_PER_MAP < ch) + start = start->next; + if (start && start->start > ch) + return NULL; + return start; +} diff --git a/ft2.h b/ft2.h new file mode 100644 index 00000000..99de79a7 --- /dev/null +++ b/ft2.h @@ -0,0 +1,78 @@ +/* Header for FreeType 2 and UTF-8 encoding support for + * DarkPlaces + */ + +#ifndef DP_FREETYPE2_H__ +#define DP_FREETYPE2_H__ + +//#include + +#include "utf8lib.h" + +/* + * From http://www.unicode.org/Public/UNIDATA/Blocks.txt + * + * E000..F8FF; Private Use Area + * F0000..FFFFF; Supplementary Private Use Area-A + * + * We use: + * Range E000 - E0FF + * Contains the non-FreeType2 version of characters. + */ + +typedef struct ft2_font_map_s ft2_font_map_t; +typedef struct ft2_attachment_s ft2_attachment_t; +#define ft2_oldstyle_map ((ft2_font_map_t*)-1) + +typedef float ft2_kernvec[2]; +typedef struct ft2_kerning_s +{ + ft2_kernvec kerning[256][256]; /* kerning[left char][right char] */ +} ft2_kerning_t; + +typedef struct ft2_font_s +{ + char name[64]; + qboolean has_kerning; + // last requested size loaded using Font_SetSize + float currentw; + float currenth; + float ascend; + float descend; + qboolean image_font; // only fallbacks are freetype fonts + + // TODO: clean this up and do not expose everything. + + //unsigned char *data; + //fs_offset_t datasize; + void *face; + + // an unordered array of ordered linked lists of glyph maps for a specific size + ft2_font_map_t *font_maps[MAX_FONT_SIZES]; + int num_sizes; + + // attachments + size_t attachmentcount; + ft2_attachment_t *attachments; + + // fallback mechanism + struct ft2_font_s *next; +} ft2_font_t; + +void Font_CloseLibrary(void); +void Font_Init(void); +qboolean Font_OpenLibrary(void); +ft2_font_t* Font_Alloc(void); +void Font_UnloadFont(ft2_font_t *font); +// IndexForSize suggests to change the width and height if a font size is in a reasonable range +// for example, you render at a size of 12.4, and a font of size 12 has been loaded +// in such a case, *outw and *outh are set to 12, which is often a good alternative size +int Font_IndexForSize(ft2_font_t *font, float size, float *outw, float *outh); +ft2_font_map_t *Font_MapForIndex(ft2_font_t *font, int index); +qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt); +qboolean Font_GetKerningForSize(ft2_font_t *font, float w, float h, Uchar left, Uchar right, float *outx, float *outy); +qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy); + +// since this is used on a font_map_t, let's name it FontMap_* +ft2_font_map_t *FontMap_FindForChar(ft2_font_map_t *start, Uchar ch); +#endif // DP_FREETYPE2_H__ diff --git a/ft2_defs.h b/ft2_defs.h new file mode 100644 index 00000000..fd15998d --- /dev/null +++ b/ft2_defs.h @@ -0,0 +1,499 @@ +/* FreeType 2 definitions from the freetype header mostly. + */ + +#ifndef FT2_DEFS_H_H__ +#define FT2_DEFS_H_H__ + +#ifdef _MSC_VER +typedef __int32 FT_Int32; +typedef __uint32 FT_UInt32; +#else +typedef int32_t FT_Int32; +typedef u_int32_t FT_UInt32; +#endif + +typedef int FT_Error; + +typedef signed char FT_Char; +typedef unsigned char FT_Byte; +typedef const FT_Byte *FT_Bytes; +typedef char FT_String; +typedef signed short FT_Short; +typedef unsigned short FT_UShort; +typedef signed int FT_Int; +typedef unsigned int FT_UInt; +typedef signed long FT_Long; +typedef signed long FT_Fixed; +typedef unsigned long FT_ULong; +typedef void *FT_Pointer; +typedef size_t FT_Offset; +typedef signed long FT_F26Dot6; + +typedef void *FT_Stream; +typedef void *FT_Module; +typedef void *FT_Library; +typedef struct FT_FaceRec_ *FT_Face; +typedef struct FT_CharMapRec_* FT_CharMap; +typedef struct FT_SizeRec_* FT_Size; +typedef struct FT_Size_InternalRec_* FT_Size_Internal; +typedef struct FT_GlyphSlotRec_* FT_GlyphSlot; +typedef struct FT_SubGlyphRec_* FT_SubGlyph; +typedef struct FT_Slot_InternalRec_* FT_Slot_Internal; + +// Taken from the freetype headers: +typedef signed long FT_Pos; +typedef struct FT_Vector_ +{ + FT_Pos x; + FT_Pos y; +} FT_Vector; + +typedef struct FT_BBox_ +{ + FT_Pos xMin, yMin; + FT_Pos xMax, yMax; +} FT_BBox; + +typedef enum FT_Pixel_Mode_ +{ + FT_PIXEL_MODE_NONE = 0, + FT_PIXEL_MODE_MONO, + FT_PIXEL_MODE_GRAY, + FT_PIXEL_MODE_GRAY2, + FT_PIXEL_MODE_GRAY4, + FT_PIXEL_MODE_LCD, + FT_PIXEL_MODE_LCD_V, + FT_PIXEL_MODE_MAX /* do not remove */ +} FT_Pixel_Mode; +typedef enum FT_Render_Mode_ +{ + FT_RENDER_MODE_NORMAL = 0, + FT_RENDER_MODE_LIGHT, + FT_RENDER_MODE_MONO, + FT_RENDER_MODE_LCD, + FT_RENDER_MODE_LCD_V, + + FT_RENDER_MODE_MAX +} FT_Render_Mode; + +#define ft_pixel_mode_none FT_PIXEL_MODE_NONE +#define ft_pixel_mode_mono FT_PIXEL_MODE_MONO +#define ft_pixel_mode_grays FT_PIXEL_MODE_GRAY +#define ft_pixel_mode_pal2 FT_PIXEL_MODE_GRAY2 +#define ft_pixel_mode_pal4 FT_PIXEL_MODE_GRAY4 + +typedef struct FT_Bitmap_ +{ + int rows; + int width; + int pitch; + unsigned char* buffer; + short num_grays; + char pixel_mode; + char palette_mode; + void* palette; +} FT_Bitmap; + +typedef struct FT_Outline_ +{ + short n_contours; /* number of contours in glyph */ + short n_points; /* number of points in the glyph */ + + FT_Vector* points; /* the outline's points */ + char* tags; /* the points flags */ + short* contours; /* the contour end points */ + + int flags; /* outline masks */ +} FT_Outline; + +#define FT_OUTLINE_NONE 0x0 +#define FT_OUTLINE_OWNER 0x1 +#define FT_OUTLINE_EVEN_ODD_FILL 0x2 +#define FT_OUTLINE_REVERSE_FILL 0x4 +#define FT_OUTLINE_IGNORE_DROPOUTS 0x8 +#define FT_OUTLINE_SMART_DROPOUTS 0x10 +#define FT_OUTLINE_INCLUDE_STUBS 0x20 + +#define FT_OUTLINE_HIGH_PRECISION 0x100 +#define FT_OUTLINE_SINGLE_PASS 0x200 + +#define ft_outline_none FT_OUTLINE_NONE +#define ft_outline_owner FT_OUTLINE_OWNER +#define ft_outline_even_odd_fill FT_OUTLINE_EVEN_ODD_FILL +#define ft_outline_reverse_fill FT_OUTLINE_REVERSE_FILL +#define ft_outline_ignore_dropouts FT_OUTLINE_IGNORE_DROPOUTS +#define ft_outline_high_precision FT_OUTLINE_HIGH_PRECISION +#define ft_outline_single_pass FT_OUTLINE_SINGLE_PASS + +#define FT_CURVE_TAG( flag ) ( flag & 3 ) + +#define FT_CURVE_TAG_ON 1 +#define FT_CURVE_TAG_CONIC 0 +#define FT_CURVE_TAG_CUBIC 2 + +#define FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */ +#define FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */ + +#define FT_CURVE_TAG_TOUCH_BOTH ( FT_CURVE_TAG_TOUCH_X | \ + FT_CURVE_TAG_TOUCH_Y ) + +#define FT_Curve_Tag_On FT_CURVE_TAG_ON +#define FT_Curve_Tag_Conic FT_CURVE_TAG_CONIC +#define FT_Curve_Tag_Cubic FT_CURVE_TAG_CUBIC +#define FT_Curve_Tag_Touch_X FT_CURVE_TAG_TOUCH_X +#define FT_Curve_Tag_Touch_Y FT_CURVE_TAG_TOUCH_Y + +typedef int +(*FT_Outline_MoveToFunc)( const FT_Vector* to, + void* user ); +#define FT_Outline_MoveTo_Func FT_Outline_MoveToFunc + +typedef int +(*FT_Outline_LineToFunc)( const FT_Vector* to, + void* user ); +#define FT_Outline_LineTo_Func FT_Outline_LineToFunc + +typedef int +(*FT_Outline_ConicToFunc)( const FT_Vector* control, + const FT_Vector* to, + void* user ); +#define FT_Outline_ConicTo_Func FT_Outline_ConicToFunc + +typedef int +(*FT_Outline_CubicToFunc)( const FT_Vector* control1, + const FT_Vector* control2, + const FT_Vector* to, + void* user ); +#define FT_Outline_CubicTo_Func FT_Outline_CubicToFunc + +typedef 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; +} FT_Outline_Funcs; + +#ifndef FT_IMAGE_TAG +#define FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \ + value = ( ( (unsigned long)_x1 << 24 ) | \ + ( (unsigned long)_x2 << 16 ) | \ + ( (unsigned long)_x3 << 8 ) | \ + (unsigned long)_x4 ) +#endif /* FT_IMAGE_TAG */ + +typedef enum FT_Glyph_Format_ +{ + FT_IMAGE_TAG( FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ), + + FT_IMAGE_TAG( FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ), + FT_IMAGE_TAG( FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' ) +} FT_Glyph_Format; +#define ft_glyph_format_none FT_GLYPH_FORMAT_NONE +#define ft_glyph_format_composite FT_GLYPH_FORMAT_COMPOSITE +#define ft_glyph_format_bitmap FT_GLYPH_FORMAT_BITMAP +#define ft_glyph_format_outline FT_GLYPH_FORMAT_OUTLINE +#define ft_glyph_format_plotter FT_GLYPH_FORMAT_PLOTTER + +typedef 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; +} FT_Glyph_Metrics; + +#define FT_EXPORT( x ) x + +#define FT_OPEN_MEMORY 0x1 +#define FT_OPEN_STREAM 0x2 +#define FT_OPEN_PATHNAME 0x4 +#define FT_OPEN_DRIVER 0x8 +#define FT_OPEN_PARAMS 0x10 + +typedef struct FT_Parameter_ +{ + FT_ULong tag; + FT_Pointer data; +} FT_Parameter; + +typedef struct FT_Open_Args_ +{ + FT_UInt flags; + const FT_Byte* memory_base; + FT_Long memory_size; + FT_String* pathname; + FT_Stream stream; + FT_Module driver; + FT_Int num_params; + FT_Parameter* params; +} FT_Open_Args; +typedef enum FT_Size_Request_Type_ +{ + FT_SIZE_REQUEST_TYPE_NOMINAL, + FT_SIZE_REQUEST_TYPE_REAL_DIM, + FT_SIZE_REQUEST_TYPE_BBOX, + FT_SIZE_REQUEST_TYPE_CELL, + FT_SIZE_REQUEST_TYPE_SCALES, + + FT_SIZE_REQUEST_TYPE_MAX + +} FT_Size_Request_Type; +typedef struct FT_Size_RequestRec_ +{ + FT_Size_Request_Type type; + FT_Long width; + FT_Long height; + FT_UInt horiResolution; + FT_UInt vertResolution; +} FT_Size_RequestRec; +typedef struct FT_Size_RequestRec_ *FT_Size_Request; + +#define FT_LOAD_DEFAULT 0x0 +#define FT_LOAD_NO_SCALE 0x1 +#define FT_LOAD_NO_HINTING 0x2 +#define FT_LOAD_RENDER 0x4 +#define FT_LOAD_NO_BITMAP 0x8 +#define FT_LOAD_VERTICAL_LAYOUT 0x10 +#define FT_LOAD_FORCE_AUTOHINT 0x20 +#define FT_LOAD_CROP_BITMAP 0x40 +#define FT_LOAD_PEDANTIC 0x80 +#define FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 0x200 +#define FT_LOAD_NO_RECURSE 0x400 +#define FT_LOAD_IGNORE_TRANSFORM 0x800 +#define FT_LOAD_MONOCHROME 0x1000 +#define FT_LOAD_LINEAR_DESIGN 0x2000 +#define FT_LOAD_NO_AUTOHINT 0x8000U + +#define FT_LOAD_TARGET_( x ) ( (FT_Int32)( (x) & 15 ) << 16 ) + +#define FT_LOAD_TARGET_NORMAL FT_LOAD_TARGET_( FT_RENDER_MODE_NORMAL ) +#define FT_LOAD_TARGET_LIGHT FT_LOAD_TARGET_( FT_RENDER_MODE_LIGHT ) +#define FT_LOAD_TARGET_MONO FT_LOAD_TARGET_( FT_RENDER_MODE_MONO ) +#define FT_LOAD_TARGET_LCD FT_LOAD_TARGET_( FT_RENDER_MODE_LCD ) +#define FT_LOAD_TARGET_LCD_V FT_LOAD_TARGET_( FT_RENDER_MODE_LCD_V ) + +#define FT_ENC_TAG( value, a, b, c, d ) \ + value = ( ( (FT_UInt32)(a) << 24 ) | \ + ( (FT_UInt32)(b) << 16 ) | \ + ( (FT_UInt32)(c) << 8 ) | \ + (FT_UInt32)(d) ) + +typedef enum FT_Encoding_ +{ + FT_ENC_TAG( FT_ENCODING_NONE, 0, 0, 0, 0 ), + + FT_ENC_TAG( FT_ENCODING_MS_SYMBOL, 's', 'y', 'm', 'b' ), + FT_ENC_TAG( FT_ENCODING_UNICODE, 'u', 'n', 'i', 'c' ), + + FT_ENC_TAG( FT_ENCODING_SJIS, 's', 'j', 'i', 's' ), + FT_ENC_TAG( FT_ENCODING_GB2312, 'g', 'b', ' ', ' ' ), + FT_ENC_TAG( FT_ENCODING_BIG5, 'b', 'i', 'g', '5' ), + FT_ENC_TAG( FT_ENCODING_WANSUNG, 'w', 'a', 'n', 's' ), + FT_ENC_TAG( FT_ENCODING_JOHAB, 'j', 'o', 'h', 'a' ), + + /* for backwards compatibility */ + FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS, + FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312, + FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5, + FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG, + FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB, + + FT_ENC_TAG( FT_ENCODING_ADOBE_STANDARD, 'A', 'D', 'O', 'B' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_EXPERT, 'A', 'D', 'B', 'E' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_CUSTOM, 'A', 'D', 'B', 'C' ), + FT_ENC_TAG( FT_ENCODING_ADOBE_LATIN_1, 'l', 'a', 't', '1' ), + + FT_ENC_TAG( FT_ENCODING_OLD_LATIN_2, 'l', 'a', 't', '2' ), + + FT_ENC_TAG( FT_ENCODING_APPLE_ROMAN, 'a', 'r', 'm', 'n' ) +} FT_Encoding; + +#define ft_encoding_none FT_ENCODING_NONE +#define ft_encoding_unicode FT_ENCODING_UNICODE +#define ft_encoding_symbol FT_ENCODING_MS_SYMBOL +#define ft_encoding_latin_1 FT_ENCODING_ADOBE_LATIN_1 +#define ft_encoding_latin_2 FT_ENCODING_OLD_LATIN_2 +#define ft_encoding_sjis FT_ENCODING_SJIS +#define ft_encoding_gb2312 FT_ENCODING_GB2312 +#define ft_encoding_big5 FT_ENCODING_BIG5 +#define ft_encoding_wansung FT_ENCODING_WANSUNG +#define ft_encoding_johab FT_ENCODING_JOHAB + +#define ft_encoding_adobe_standard FT_ENCODING_ADOBE_STANDARD +#define ft_encoding_adobe_expert FT_ENCODING_ADOBE_EXPERT +#define ft_encoding_adobe_custom FT_ENCODING_ADOBE_CUSTOM +#define ft_encoding_apple_roman FT_ENCODING_APPLE_ROMAN + +typedef struct FT_Bitmap_Size_ +{ + FT_Short height; + FT_Short width; + + FT_Pos size; + + FT_Pos x_ppem; + FT_Pos y_ppem; +} FT_Bitmap_Size; + +typedef struct FT_CharMapRec_ +{ + FT_Face face; + FT_Encoding encoding; + FT_UShort platform_id; + FT_UShort encoding_id; +} FT_CharMapRec; + +typedef void (*FT_Generic_Finalizer)(void* object); +typedef struct FT_Generic_ +{ + void* data; + FT_Generic_Finalizer finalizer; +} FT_Generic; + +typedef struct FT_Size_Metrics_ +{ + FT_UShort x_ppem; /* horizontal pixels per EM */ + FT_UShort y_ppem; /* vertical pixels per EM */ + + FT_Fixed x_scale; /* scaling values used to convert font */ + FT_Fixed y_scale; /* units to 26.6 fractional pixels */ + + FT_Pos ascender; /* ascender in 26.6 frac. pixels */ + FT_Pos descender; /* descender in 26.6 frac. pixels */ + FT_Pos height; /* text height in 26.6 frac. pixels */ + FT_Pos max_advance; /* max horizontal advance, in 26.6 pixels */ +} FT_Size_Metrics; + +typedef struct FT_SizeRec_ +{ + FT_Face face; /* parent face object */ + FT_Generic generic; /* generic pointer for client uses */ + FT_Size_Metrics metrics; /* size metrics */ + FT_Size_Internal internal; +} FT_SizeRec; + +typedef 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; + + /*# The following member variables (down to `underline_thickness') */ + /*# are only relevant to scalable outlines; cf. @FT_Bitmap_Size */ + /*# for bitmap fonts. */ + 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; + + /* ft2 private + FT_Driver driver; + FT_Memory memory; + FT_Stream stream; + + FT_ListRec sizes_list; + + FT_Generic autohint; + void* extensions; + + FT_Face_Internal internal; + */ +} FT_FaceRec; + +typedef struct FT_GlyphSlotRec_ +{ + FT_Library library; + FT_Face face; + FT_GlyphSlot next; + FT_UInt reserved; /* retained for binary compatibility */ + 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; +} FT_GlyphSlotRec; + +#define FT_FACE_FLAG_SCALABLE ( 1L << 0 ) +#define FT_FACE_FLAG_FIXED_SIZES ( 1L << 1 ) +#define FT_FACE_FLAG_FIXED_WIDTH ( 1L << 2 ) +#define FT_FACE_FLAG_SFNT ( 1L << 3 ) +#define FT_FACE_FLAG_HORIZONTAL ( 1L << 4 ) +#define FT_FACE_FLAG_VERTICAL ( 1L << 5 ) +#define FT_FACE_FLAG_KERNING ( 1L << 6 ) +#define FT_FACE_FLAG_FAST_GLYPHS ( 1L << 7 ) +#define FT_FACE_FLAG_MULTIPLE_MASTERS ( 1L << 8 ) +#define FT_FACE_FLAG_GLYPH_NAMES ( 1L << 9 ) +#define FT_FACE_FLAG_EXTERNAL_STREAM ( 1L << 10 ) +#define FT_FACE_FLAG_HINTER ( 1L << 11 ) +#define FT_FACE_FLAG_CID_KEYED ( 1L << 12 ) +#define FT_FACE_FLAG_TRICKY ( 1L << 13 ) + +typedef enum FT_Kerning_Mode_ +{ + FT_KERNING_DEFAULT = 0, + FT_KERNING_UNFITTED, + FT_KERNING_UNSCALED +} FT_Kerning_Mode; + +#endif // FT2_DEFS_H_H__ diff --git a/ft2_fontdefs.h b/ft2_fontdefs.h new file mode 100644 index 00000000..20726d03 --- /dev/null +++ b/ft2_fontdefs.h @@ -0,0 +1,61 @@ +#ifndef FT2_PRIVATE_H__ +#define FT2_PRIVATE_H__ + +// anything should work, but I recommend multiples of 8 +// since the texture size should be a power of 2 +#define FONT_CHARS_PER_LINE 16 +#define FONT_CHAR_LINES 16 +#define FONT_CHARS_PER_MAP (FONT_CHARS_PER_LINE * FONT_CHAR_LINES) + +typedef struct glyph_slot_s +{ + qboolean image; + // we keep the quad coords here only currently + // if you need other info, make Font_LoadMapForIndex fill it into this slot + float txmin; // texture coordinate in [0,1] + float txmax; + float tymin; + float tymax; + float vxmin; + float vxmax; + float vymin; + float vymax; + float advance_x; + float advance_y; +} glyph_slot_t; + +struct ft2_font_map_s +{ + Uchar start; + struct ft2_font_map_s *next; + float size; + // the actual size used in the freetype code + // by convention, the requested size is the height of the font's bounding box. + float intSize; + int glyphSize; + + rtexture_t *texture; + qboolean static_tex; + glyph_slot_t glyphs[FONT_CHARS_PER_MAP]; + + // contains the kerning information for the first 256 characters + // for the other characters, we will lookup the kerning information + ft2_kerning_t kerning; + // safes us the trouble of calculating these over and over again + double sfx, sfy; +}; + +struct ft2_attachment_s +{ + unsigned char *data; + fs_offset_t size; +}; + +//qboolean Font_LoadMapForIndex(ft2_font_t *font, Uchar _ch, ft2_font_map_t **outmap); +qboolean Font_LoadMapForIndex(ft2_font_t *font, int map_index, Uchar _ch, ft2_font_map_t **outmap); + +void font_start(void); +void font_shutdown(void); +void font_newmap(void); + +#endif // FT2_PRIVATE_H__ diff --git a/utf8lib.c b/utf8lib.c new file mode 100644 index 00000000..8b50e753 --- /dev/null +++ b/utf8lib.c @@ -0,0 +1,660 @@ +#include "quakedef.h" +#include "utf8lib.h" + +/* +================================================================================ +Initialization of UTF-8 support and new cvars. +================================================================================ +*/ +// for compatibility this defaults to 0 +cvar_t utf8_enable = {CVAR_SAVE, "utf8_enable", "0", "Enable UTF-8 support. For compatibility, this is disabled by default in most games."}; + +void u8_Init(void) +{ + Cvar_RegisterVariable(&utf8_enable); +} + +/* +================================================================================ +UTF-8 encoding and decoding functions follow. +================================================================================ +*/ + +/** Analyze the next character and return various information if requested. + * @param _s An utf-8 string. + * @param _start Filled with the start byte-offset of the next valid character + * @param _len Fileed with the length of the next valid character + * @param _ch Filled with the unicode value of the next character + * @return Whether or not another valid character is in the string + */ +static qboolean u8_analyze(const char *_s, size_t *_start, size_t *_len, Uchar *_ch) +{ + const unsigned char *s = (const unsigned char*)_s; + unsigned char bt, bc; + size_t i; + size_t bits, j; + Uchar ch; + + i = 0; +findchar: + + // <0xC2 is always an overlong encoding, they're invalid, thus skipped + while (s[i] && s[i] >= 0x80 && s[i] <= 0xC2) { + //fprintf(stderr, "skipping\n"); + ++i; + } + //fprintf(stderr, "checking\n"); + + // If we hit the end, well, we're out and invalid + if (!s[i]) + return false; + //fprintf(stderr, "checking ascii\n"); + + // ascii characters + if (s[i] < 0x80) + { + if (_start) *_start = i; + if (_len) *_len = 1; + if (_ch) *_ch = (Uchar)s[i]; + //fprintf(stderr, "valid ascii\n"); + return true; + } + //fprintf(stderr, "checking length\n"); + + // Figure out the next char's length + bc = s[i]; + bits = 1; + // count the 1 bits, they're the # of bytes + for (bt = 0x40; bt && (bc & bt); bt >>= 1, ++bits); + if (!bt) + { + //fprintf(stderr, "superlong\n"); + ++i; + goto findchar; + } + // turn bt into a mask and give ch a starting value + --bt; + ch = (s[i] & bt); + // check the byte sequence for invalid bytes + for (j = 1; j < bits; ++j) + { + // valid bit value: 10xx xxxx + //if (s[i+j] < 0x80 || s[i+j] >= 0xC0) + if ( (s[i+j] & 0xC0) != 0x80 ) + { + //fprintf(stderr, "sequence of %i f'd at %i by %x\n", bits, j, (unsigned int)s[i+j]); + // this byte sequence is invalid, skip it + i += j; + // find a character after it + goto findchar; + } + // at the same time, decode the character + ch = (ch << 6) | (s[i+j] & 0x3F); + } + + // Now check the decoded byte for an overlong encoding + if ( (bits >= 2 && ch < 0x80) || + (bits >= 3 && ch < 0x800) || + (bits >= 4 && ch < 0x10000) || + ch >= 0x10FFFF // RFC 3629 + ) + { + i += bits; + //fprintf(stderr, "overlong: %i bytes for %x\n", bits, ch); + goto findchar; + } + + if (_start) + *_start = i; + if (_len) + *_len = bits; + if (_ch) + *_ch = ch; + //fprintf(stderr, "valid utf8\n"); + return true; +} + +/** Get the number of characters in an UTF-8 string. + * @param _s An utf-8 encoded null-terminated string. + * @return The number of unicode characters in the string. + */ +size_t u8_strlen(const char *_s) +{ + size_t st, ln; + size_t len = 0; + const unsigned char *s = (const unsigned char*)_s; + + if (!utf8_enable.integer) + return strlen(_s); + + while (*s) + { + // ascii char, skip u8_analyze + if (*s < 0x80) + { + ++len; + ++s; + continue; + } + + // invalid, skip u8_analyze + if (*s <= 0xC2) + { + ++s; + continue; + } + + if (!u8_analyze((const char*)s, &st, &ln, NULL)) + break; + // valid character, skip after it + s += st + ln; + ++len; + } + return len; +} + +/** Get the number of characters in a part of an UTF-8 string. + * @param _s An utf-8 encoded null-terminated string. + * @param n The maximum number of bytes. + * @return The number of unicode characters in the string. + */ +size_t u8_strnlen(const char *_s, size_t n) +{ + size_t st, ln; + size_t len = 0; + const unsigned char *s = (const unsigned char*)_s; + + if (!utf8_enable.integer) + { + len = strlen(_s); + return (len < n) ? len : n; + } + + while (*s && n) + { + // ascii char, skip u8_analyze + if (*s < 0x80) + { + ++len; + ++s; + --n; + continue; + } + + // invalid, skip u8_analyze + if (*s <= 0xC2) + { + ++s; + --n; + continue; + } + + if (!u8_analyze((const char*)s, &st, &ln, NULL)) + break; + // valid character, see if it's still inside the range specified by n: + if (n < st + ln) + return len; + ++len; + n -= st + ln; + s += st + ln; + } + return len; +} + +/** Get the number of bytes used in a string to represent an amount of characters. + * @param _s An utf-8 encoded null-terminated string. + * @param n The number of characters we want to know the byte-size for. + * @return The number of bytes used to represent n characters. + */ +size_t u8_bytelen(const char *_s, size_t n) +{ + size_t st, ln; + size_t len = 0; + const unsigned char *s = (const unsigned char*)_s; + + if (!utf8_enable.integer) + return n; + + while (*s && n) + { + // ascii char, skip u8_analyze + if (*s < 0x80) + { + ++len; + ++s; + --n; + continue; + } + + // invalid, skip u8_analyze + if (*s <= 0xC2) + { + ++s; + ++len; + continue; + } + + if (!u8_analyze((const char*)s, &st, &ln, NULL)) + break; + --n; + s += st + ln; + len += st + ln; + } + return len; +} + +/** Get the byte-index for a character-index. + * @param _s An utf-8 encoded string. + * @param i The character-index for which you want the byte offset. + * @param len If not null, character's length will be stored in there. + * @return The byte-index at which the character begins, or -1 if the string is too short. + */ +int u8_byteofs(const char *_s, size_t i, size_t *len) +{ + size_t st, ln; + size_t ofs = 0; + const unsigned char *s = (const unsigned char*)_s; + + if (!utf8_enable.integer) + { + if (len) *len = 1; + return i; + } + + st = ln = 0; + do + { + ofs += ln; + if (!u8_analyze((const char*)s + ofs, &st, &ln, NULL)) + return -1; + ofs += st; + } while(i-- > 0); + if (len) + *len = ln; + return ofs; +} + +/** Get the char-index for a byte-index. + * @param _s An utf-8 encoded string. + * @param i The byte offset for which you want the character index. + * @param len If not null, the offset within the character is stored here. + * @return The character-index, or -1 if the string is too short. + */ +int u8_charidx(const char *_s, size_t i, size_t *len) +{ + size_t st, ln; + size_t ofs = 0; + size_t pofs = 0; + int idx = 0; + const unsigned char *s = (const unsigned char*)_s; + + if (!utf8_enable.integer) + { + if (len) *len = 0; + return i; + } + + while (ofs < i && s[ofs]) + { + // ascii character, skip u8_analyze + if (s[ofs] < 0x80) + { + pofs = ofs; + ++idx; + ++ofs; + continue; + } + + // invalid, skip u8_analyze + if (s[ofs] <= 0xC2) + { + ++ofs; + continue; + } + + if (!u8_analyze((const char*)s+ofs, &st, &ln, NULL)) + return -1; + // see if next char is after the bytemark + if (ofs + st > i) + { + if (len) + *len = i - pofs; + return idx; + } + ++idx; + pofs = ofs + st; + ofs += st + ln; + // see if bytemark is within the char + if (ofs > i) + { + if (len) + *len = i - pofs; + return idx; + } + } + if (len) *len = 0; + return idx; +} + +/** Get the byte offset of the previous byte. + * The result equals: + * prevchar_pos = u8_byteofs(text, u8_charidx(text, thischar_pos, NULL) - 1, NULL) + * @param _s An utf-8 encoded string. + * @param i The current byte offset. + * @return The byte offset of the previous character + */ +size_t u8_prevbyte(const char *_s, size_t i) +{ + size_t st, ln; + const unsigned char *s = (const unsigned char*)_s; + size_t lastofs = 0; + size_t ofs = 0; + + if (!utf8_enable.integer) + { + if (i > 0) + return i-1; + return 0; + } + + while (ofs < i && s[ofs]) + { + // ascii character, skip u8_analyze + if (s[ofs] < 0x80) + { + lastofs = ofs++; + continue; + } + + // invalid, skip u8_analyze + if (s[ofs] <= 0xC2) + { + ++ofs; + continue; + } + + if (!u8_analyze((const char*)s+ofs, &st, &ln, NULL)) + return lastofs; + if (ofs + st > i) + return lastofs; + if (ofs + st + ln >= i) + return ofs + st; + + lastofs = ofs; + ofs += st + ln; + } + return lastofs; +} + +static int char_usefont[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // shift+digit line + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // digits + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // caps + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // small + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // specials + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // faces + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/** Fetch a character from an utf-8 encoded string. + * @param _s The start of an utf-8 encoded multi-byte character. + * @param _end Will point to after the first multi-byte character. + * @return The 32-bit integer representation of the first multi-byte character or 0 for invalid characters. + */ +Uchar u8_getchar(const char *_s, const char **_end) +{ + size_t st, ln; + Uchar ch; + + if (!utf8_enable.integer) + { + if (_end) + *_end = _s + 1; + /* Careful: if we disable utf8 but not freetype, we wish to see freetype chars + * for normal letters. So use E000+x for special chars, but leave the freetype stuff for the + * rest: + */ + if (!char_usefont[(unsigned int)*(const unsigned char*)_s]) + return 0xE000 + (Uchar)*(const unsigned char*)_s; + return (Uchar)*(const unsigned char*)_s; + } + + if (!u8_analyze(_s, &st, &ln, &ch)) + return 0; + if (_end) + *_end = _s + st + ln; + return ch; +} + +/** Encode a wide-character into utf-8. + * @param w The wide character to encode. + * @param to The target buffer the utf-8 encoded string is stored to. + * @param maxlen The maximum number of bytes that fit into the target buffer. + * @return Number of bytes written to the buffer not including the terminating null. + * Less or equal to 0 if the buffer is too small. + */ +int u8_fromchar(Uchar w, char *to, size_t maxlen) +{ + if (maxlen < 1) + return -2; + + if (!w) + return -5; + + if (w >= 0xE000 && !utf8_enable.integer) + w -= 0xE000; + + if (w < 0x80 || !utf8_enable.integer) + { + to[0] = (char)w; + if (maxlen < 2) + return -1; + to[1] = 0; + return 1; + } + // for a little speedup + if (w < 0x800) + { + if (maxlen < 3) + { + to[0] = 0; + return -1; + } + to[2] = 0; + to[1] = 0x80 | (w & 0x3F); w >>= 6; + to[0] = 0xC0 | w; + return 2; + } + if (w < 0x10000) + { + if (maxlen < 4) + { + to[0] = 0; + return -1; + } + to[3] = 0; + to[2] = 0x80 | (w & 0x3F); w >>= 6; + to[1] = 0x80 | (w & 0x3F); w >>= 6; + to[0] = 0xE0 | w; + return 3; + } + + // RFC 3629 + if (w <= 0x10FFFF) + { + if (maxlen < 5) + { + to[0] = 0; + return -1; + } + to[4] = 0; + to[3] = 0x80 | (w & 0x3F); w >>= 6; + to[2] = 0x80 | (w & 0x3F); w >>= 6; + to[1] = 0x80 | (w & 0x3F); w >>= 6; + to[0] = 0xE0 | w; + return 4; + } + return -1; +} + +/** uses u8_fromchar on a static buffer + * @param ch The unicode character to convert to encode + * @param l The number of bytes without the terminating null. + * @return A statically allocated buffer containing the character's utf8 representation, or NULL if it fails. + */ +char *u8_encodech(Uchar ch, size_t *l) +{ + static char buf[16]; + size_t len; + len = u8_fromchar(ch, buf, sizeof(buf)); + if (len > 0) + { + if (l) *l = len; + return buf; + } + return NULL; +} + +/** Convert a utf-8 multibyte string to a wide character string. + * @param wcs The target wide-character buffer. + * @param mb The utf-8 encoded multibyte string to convert. + * @param maxlen The maximum number of wide-characters that fit into the target buffer. + * @return The number of characters written to the target buffer. + */ +size_t u8_mbstowcs(Uchar *wcs, const char *mb, size_t maxlen) +{ + size_t i; + Uchar ch; + if (maxlen < 1) + return 0; + for (i = 0; *mb && i < maxlen-1; ++i) + { + ch = u8_getchar(mb, &mb); + if (!ch) + break; + wcs[i] = ch; + } + wcs[i] = 0; + return i; +} + +/** Convert a wide-character string to a utf-8 multibyte string. + * @param mb The target buffer the utf-8 string is written to. + * @param wcs The wide-character string to convert. + * @param maxlen The number bytes that fit into the multibyte target buffer. + * @return The number of bytes written, not including the terminating \0 + */ +size_t u8_wcstombs(char *mb, const Uchar *wcs, size_t maxlen) +{ + size_t i; + const char *start = mb; + if (maxlen < 2) + return 0; + for (i = 0; wcs[i] && i < maxlen-1; ++i) + { + int len; + if ( (len = u8_fromchar(wcs[i], mb, maxlen - i)) < 0) + return (mb - start); + mb += len; + } + *mb = 0; + return (mb - start); +} + +/* +============ +UTF-8 aware COM_StringLengthNoColors + +calculates the visible width of a color coded string. + +*valid is filled with TRUE if the string is a valid colored string (that is, if +it does not end with an unfinished color code). If it gets filled with FALSE, a +fix would be adding a STRING_COLOR_TAG at the end of the string. + +valid can be set to NULL if the caller doesn't care. + +For size_s, specify the maximum number of characters from s to use, or 0 to use +all characters until the zero terminator. +============ +*/ +size_t +COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid); +size_t +u8_COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid) +{ + const char *end; + size_t len = 0; + + if (!utf8_enable.integer) + return COM_StringLengthNoColors(s, size_s, valid); + + end = size_s ? (s + size_s) : NULL; + + for(;;) + { + switch((s == end) ? 0 : *s) + { + case 0: + if(valid) + *valid = TRUE; + return len; + case STRING_COLOR_TAG: + ++s; + switch((s == end) ? 0 : *s) + { + case STRING_COLOR_RGB_TAG_CHAR: + if (s+1 != end && isxdigit(s[1]) && + s+2 != end && isxdigit(s[2]) && + s+3 != end && isxdigit(s[3]) ) + { + s+=3; + break; + } + ++len; // STRING_COLOR_TAG + ++len; // STRING_COLOR_RGB_TAG_CHAR + break; + case 0: // ends with unfinished color code! + ++len; + if(valid) + *valid = FALSE; + return len; + case STRING_COLOR_TAG: // escaped ^ + ++len; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': // color code + break; + default: // not a color code + ++len; // STRING_COLOR_TAG + ++len; // the character + break; + } + break; + default: + ++len; + break; + } + + // start of a wide character + if (*s & 0xC0) + { + for (++s; *s >= 0x80 && *s <= 0xC0; ++s); + continue; + } + // part of a wide character, we ignore that one + if (*s <= 0xBF) + --len; + ++s; + } + // never get here +} diff --git a/utf8lib.h b/utf8lib.h new file mode 100644 index 00000000..f435bbdf --- /dev/null +++ b/utf8lib.h @@ -0,0 +1,46 @@ +/* + * UTF-8 utility functions for DarkPlaces + */ +#ifndef UTF8LIB_H__ +#define UTF8LIB_H__ + +#include "qtypes.h" + +// types for unicode strings +// let them be 32 bit for now +// normally, whcar_t is 16 or 32 bit, 16 on linux I think, 32 on haiku and maybe windows +#ifdef _MSC_VER +#include +typedef __int32 U_int32; +#else +#include +typedef int32_t U_int32; +#endif + +// Uchar, a wide character +typedef U_int32 Uchar; + +// Initialize UTF8, this registers cvars which allows for UTF8 to be disabled +// completely. +// When UTF8 is disabled, every u8_ function will work exactly as you'd expect +// a non-utf8 version to work: u8_strlen() will wrap to strlen() +// u8_byteofs() and u8_charidx() will simply return whatever is passed as index parameter +// u8_getchar() will will just return the next byte, u8_fromchar will write one byte, ... +extern cvar_t utf8_enable; +void u8_Init(void); + +size_t u8_strlen(const char*); +size_t u8_strnlen(const char*, size_t); +int u8_byteofs(const char*, size_t, size_t*); +int u8_charidx(const char*, size_t, size_t*); +size_t u8_bytelen(const char*, size_t); +size_t u8_prevbyte(const char*, size_t); +Uchar u8_getchar(const char*, const char**); +int u8_fromchar(Uchar, char*, size_t); +size_t u8_wcstombs(char*, const Uchar*, size_t); +size_t u8_COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid); + +// returns a static buffer, use this for inlining +char *u8_encodech(Uchar ch, size_t*); + +#endif // UTF8LIB_H__ -- 2.39.2