# fontcache.cpp

Go to the documentation of this file.

    00001 /* $Id: fontcache.cpp 13871 2008-07-29 22:37:54Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "debug.h"
    00008 #include "spritecache.h"
    00009 #include "fontcache.h"
    00010 #include "spriteloader/spriteloader.hpp"
    00011 #include "blitter/factory.hpp"
    00012 #include "gfx_func.h"
    00013 #include "core/alloc_func.hpp"
    00014 #include "core/math_func.hpp"
    00015
    00016 #include "table/sprites.h"
    00017 #include "table/control_codes.h"
    00018
    00019 #ifdef WITH_FREETYPE
    00020 #include <ft2build.h>
    00021 #include FT_FREETYPE_H
    00022 #include FT_GLYPH_H
    00023
    00024 #ifdef WITH_FONTCONFIG
    00025 #include <fontconfig/fontconfig.h>
    00026 #endif
    00027
    00028 static FT_Library _library = NULL;
    00029 static FT_Face _face_small = NULL;
    00030 static FT_Face _face_medium = NULL;
    00031 static FT_Face _face_large = NULL;
    00032
    00033 FreeTypeSettings _freetype;
    00034
    00035 enum {
    00036   FACE_COLOUR = 1,
    00037   SHADOW_COLOUR = 2,
    00038 };
    00039
    00042 #ifdef WIN32
    00043 #include <windows.h>
    00044 #include <tchar.h>
    00045 #include <shlobj.h> // SHGetFolderPath
    00046 #include "win32.h"
    00047
    00048 /* Get the font file to be loaded into Freetype by looping the registry
    00049  * location where windows lists all installed fonts. Not very nice, will
    00050  * surely break if the registry path changes, but it works. Much better
    00051  * solution would be to use CreateFont, and extract the font data from it
    00052  * by GetFontData. The problem with this is that the font file needs to be
    00053  * kept in memory then until the font is no longer needed. This could mean
    00054  * an additional memory usage of 30MB (just for fonts!) when using an eastern
    00055  * font for all font sizes */
    00056 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
    00057 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
    00058 static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
    00059 {
    00060   FT_Error err = FT_Err_Cannot_Open_Resource;
    00061   HKEY hKey;
    00062   LONG ret;
    00063   TCHAR vbuffer[MAX_PATH], dbuffer[256];
    00064   TCHAR *font_namep;
    00065   char *font_path;
    00066   uint index;
    00067
    00068   /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
    00069    * "Windows NT" key, on Windows 9x in the Windows key. To save us having
    00070    * to retrieve the windows version, we'll just query both */
    00071   ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
    00072   if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
    00073
    00074   if (ret != ERROR_SUCCESS) {
    00075     DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
    00076     return err;
    00077   }
    00078
    00079   /* For Unicode we need some conversion between widechar and
    00080    * normal char to match the data returned by RegEnumValue,
    00081    * otherwise just use parameter */
    00082 #if defined(UNICODE)
    00083   font_namep = MallocT<TCHAR>(MAX_PATH);
    00084   MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
    00085 #else
    00086   font_namep = (char*)font_name; // only cast because in unicode pointer is not const
    00087 #endif
    00088
    00089   for (index = 0;; index++) {
    00090     TCHAR *s;
    00091     DWORD vbuflen = lengthof(vbuffer);
    00092     DWORD dbuflen = lengthof(dbuffer);
    00093
    00094     ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
    00095     if (ret != ERROR_SUCCESS) goto registry_no_font_found;
    00096
    00097     /* The font names in the registry are of the following 3 forms:
    00098      * - ADMUI3.fon
    00099      * - Book Antiqua Bold (TrueType)
    00100      * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
    00101      * We will strip the font-type '()' if any and work with the font name
    00102      * itself, which must match exactly; if...
    00103      * TTC files, font files which contain more than one font are seperated
    00104      * byt '&'. Our best bet will be to do substr match for the fontname
    00105      * and then let FreeType figure out which index to load */
    00106     s = _tcschr(vbuffer, _T('('));
    00107     if (s != NULL) s[-1] = '\0';
    00108
    00109     if (_tcschr(vbuffer, _T('&')) == NULL) {
    00110       if (_tcsicmp(vbuffer, font_namep) == 0) break;
    00111     } else {
    00112       if (_tcsstr(vbuffer, font_namep) != NULL) break;
    00113     }
    00114   }
    00115
    00116   if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
    00117     DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
    00118     goto folder_error;
    00119   }
    00120
    00121   /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
    00122    * contain multiple fonts inside this single file. GetFontData however
    00123    * returns the whole file, so we need to check each font inside to get the
    00124    * proper font.
    00125    * Also note that FreeType does not support UNICODE filesnames! */
    00126 #if defined(UNICODE)
    00127   /* We need a cast here back from wide because FreeType doesn't support
    00128    * widechar filenames. Just use the buffer we allocated before for the
    00129    * font_name search */
    00130   font_path = (char*)font_namep;
    00131   WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
    00132 #else
    00133   font_path = vbuffer;
    00134 #endif
    00135
    00136   ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
    00137   ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
    00138   index = 0;
    00139   do {
    00140     err = FT_New_Face(_library, font_path, index, face);
    00141     if (err != FT_Err_Ok) break;
    00142
    00143     if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
    00144     err = FT_Err_Cannot_Open_Resource;
    00145
    00146   } while ((FT_Long)++index != (*face)->num_faces);
    00147
    00148
    00149 folder_error:
    00150 registry_no_font_found:
    00151 #if defined(UNICODE)
    00152   free(font_namep);
    00153 #endif
    00154   RegCloseKey(hKey);
    00155   return err;
    00156 }
    00157 #else
    00158 # ifdef WITH_FONTCONFIG
    00159 static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
    00160 {
    00161   FT_Error err = FT_Err_Cannot_Open_Resource;
    00162
    00163   if (!FcInit()) {
    00164     ShowInfoF("Unable to load font configuration");
    00165   } else {
    00166     FcPattern *match;
    00167     FcPattern *pat;
    00168     FcFontSet *fs;
    00169     FcResult  result;
    00170     char *font_style;
    00171     char *font_family;
    00172
    00173     /* Split & strip the font's style */
    00174     font_family = strdup(font_name);
    00175     font_style = strchr(font_family, ',');
    00176     if (font_style != NULL) {
    00177       font_style[0] = '\0';
    00178       font_style++;
    00179       while (*font_style == ' ' || *font_style == '\t') font_style++;
    00180     }
    00181
    00182     /* Resolve the name and populate the information structure */
    00183     pat = FcNameParse((FcChar8*)font_family);
    00184     if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
    00185     FcConfigSubstitute(0, pat, FcMatchPattern);
    00186     FcDefaultSubstitute(pat);
    00187     fs = FcFontSetCreate();
    00188     match = FcFontMatch(0, pat, &result);
    00189
    00190     if (fs != NULL && match != NULL) {
    00191       int i;
    00192       FcChar8 *family;
    00193       FcChar8 *style;
    00194       FcChar8 *file;
    00195       FcFontSetAdd(fs, match);
    00196
    00197       for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
    00198         /* Try the new filename */
    00199         if (FcPatternGetString(fs->fonts[i], FC_FILE,   0, &file)   == FcResultMatch &&
    00200             FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
    00201             FcPatternGetString(fs->fonts[i], FC_STYLE,  0, &style)  == FcResultMatch) {
    00202
    00203           /* The correct style? */
    00204           if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
    00205
    00206           /* Font config takes the best shot, which, if the family name is spelled
    00207           * wrongly a 'random' font, so check whether the family name is the
    00208           * same as the supplied name */
    00209           if (strcasecmp(font_family, (char*)family) == 0) {
    00210             err = FT_New_Face(_library, (char *)file, 0, face);
    00211           }
    00212         }
    00213       }
    00214     }
    00215
    00216     free(font_family);
    00217     FcPatternDestroy(pat);
    00218     FcFontSetDestroy(fs);
    00219     FcFini();
    00220   }
    00221
    00222   return err;
    00223 }
    00224 # else
    00225 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
    00226 # endif /* WITH_FONTCONFIG */
    00227
    00228 #endif
    00229
    00236 static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
    00237 {
    00238   FT_Error error;
    00239
    00240   if (StrEmpty(font_name)) return;
    00241
    00242   error = FT_New_Face(_library, font_name, 0, face);
    00243
    00244   if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, face);
    00245
    00246   if (error == FT_Err_Ok) {
    00247     DEBUG(freetype, 2, "Requested '%s', using '%s %s'", font_name, (*face)->family_name, (*face)->style_name);
    00248
    00249     /* Attempt to select the unicode character map */
    00250     error = FT_Select_Charmap(*face, ft_encoding_unicode);
    00251     if (error == FT_Err_Ok) return; // Success
    00252
    00253     if (error == FT_Err_Invalid_CharMap_Handle) {
    00254       /* Try to pick a different character map instead. We default to
    00255        * the first map, but platform_id 0 encoding_id 0 should also
    00256        * be unicode (strange system...) */
    00257       FT_CharMap found = (*face)->charmaps[0];
    00258       int i;
    00259
    00260       for (i = 0; i < (*face)->num_charmaps; i++) {
    00261         FT_CharMap charmap = (*face)->charmaps[i];
    00262         if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
    00263           found = charmap;
    00264         }
    00265       }
    00266
    00267       if (found != NULL) {
    00268         error = FT_Set_Charmap(*face, found);
    00269         if (error == FT_Err_Ok) return;
    00270       }
    00271     }
    00272   }
    00273
    00274   FT_Done_Face(*face);
    00275   *face = NULL;
    00276
    00277   ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
    00278 }
    00279
    00280
    00281 void InitFreeType()
    00282 {
    00283   if (StrEmpty(_freetype.small_font) && StrEmpty(_freetype.medium_font) && StrEmpty(_freetype.large_font)) {
    00284     DEBUG(freetype, 1, "No font faces specified, using sprite fonts instead");
    00285     return;
    00286   }
    00287
    00288   if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
    00289     ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
    00290     return;
    00291   }
    00292
    00293   DEBUG(freetype, 2, "Initialized");
    00294
    00295   /* Load each font */
    00296   LoadFreeTypeFont(_freetype.small_font,  &_face_small,  "small");
    00297   LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
    00298   LoadFreeTypeFont(_freetype.large_font,  &_face_large,  "large");
    00299
    00300   /* Set each font size */
    00301   if (_face_small  != NULL) FT_Set_Pixel_Sizes(_face_small,  0, _freetype.small_size);
    00302   if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
    00303   if (_face_large  != NULL) FT_Set_Pixel_Sizes(_face_large,  0, _freetype.large_size);
    00304 }
    00305
    00306
    00307 static FT_Face GetFontFace(FontSize size)
    00308 {
    00309   switch (size) {
    00310     default: NOT_REACHED();
    00311     case FS_NORMAL: return _face_medium;
    00312     case FS_SMALL:  return _face_small;
    00313     case FS_LARGE:  return _face_large;
    00314   }
    00315 }
    00316
    00317
    00318 struct GlyphEntry {
    00319   Sprite *sprite;
    00320   byte width;
    00321 };
    00322
    00323
    00324 /* The glyph cache. This is structured to reduce memory consumption.
    00325  * 1) There is a 'segment' table for each font size.
    00326  * 2) Each segment table is a discrete block of characters.
    00327  * 3) Each block contains 256 (aligned) characters sequential characters.
    00328  *
    00329  * The cache is accessed in the following way:
    00330  * For character 0x0041  ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
    00331  * For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
    00332  *
    00333  * Currently only 256 segments are allocated, "limiting" us to 65536 characters.
    00334  * This can be simply changed in the two functions Get & SetGlyphPtr.
    00335  */
    00336 static GlyphEntry **_glyph_ptr[FS_END];
    00337
    00338
    00339 static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
    00340 {
    00341   if (_glyph_ptr[size] == NULL) return NULL;
    00342   if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
    00343   return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
    00344 }
    00345
    00346
    00347 static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
    00348 {
    00349   if (_glyph_ptr[size] == NULL) {
    00350     DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size);
    00351     _glyph_ptr[size] = CallocT<GlyphEntry*>(256);
    00352   }
    00353
    00354   if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
    00355     DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
    00356     _glyph_ptr[size][GB(key, 8, 8)] = CallocT<GlyphEntry>(256);
    00357   }
    00358
    00359   DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size);
    00360   _glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
    00361   _glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width  = glyph->width;
    00362 }
    00363
    00364 void *AllocateFont(size_t size)
    00365 {
    00366   return MallocT<byte>(size);
    00367 }
    00368
    00369
    00370 /* Check if a glyph should be rendered with antialiasing */
    00371 static bool GetFontAAState(FontSize size)
    00372 {
    00373   /* AA is only supported for 32 bpp */
    00374   if (BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() != 32) return false;
    00375
    00376   switch (size) {
    00377     default: NOT_REACHED();
    00378     case FS_NORMAL: return _freetype.medium_aa;
    00379     case FS_SMALL:  return _freetype.small_aa;
    00380     case FS_LARGE:  return _freetype.large_aa;
    00381   }
    00382 }
    00383
    00384
    00385 const Sprite *GetGlyph(FontSize size, WChar key)
    00386 {
    00387   FT_Face face = GetFontFace(size);
    00388   FT_GlyphSlot slot;
    00389   GlyphEntry new_glyph;
    00390   GlyphEntry *glyph;
    00391   SpriteLoader::Sprite sprite;
    00392   int width;
    00393   int height;
    00394   int x;
    00395   int y;
    00396   int y_adj;
    00397
    00398   assert(IsPrintable(key));
    00399
    00400   /* Bail out if no face loaded, or for our special characters */
    00401   if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
    00402     SpriteID sprite = GetUnicodeGlyph(size, key);
    00403     if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
    00404     return GetSprite(sprite);
    00405   }
    00406
    00407   /* Check for the glyph in our cache */
    00408   glyph = GetGlyphPtr(size, key);
    00409   if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
    00410
    00411   slot = face->glyph;
    00412
    00413   bool aa = GetFontAAState(size);
    00414
    00415   FT_Load_Char(face, key, FT_LOAD_DEFAULT);
    00416   FT_Render_Glyph(face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
    00417
    00418   /* Despite requesting a normal glyph, FreeType may have returned a bitmap */
    00419   aa = (slot->bitmap.palette_mode == FT_PIXEL_MODE_GRAY);
    00420
    00421   /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
    00422   width  = max(1, slot->bitmap.width + (size == FS_NORMAL));
    00423   height = max(1, slot->bitmap.rows  + (size == FS_NORMAL));
    00424
    00425   /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
    00426   sprite.data = CallocT<SpriteLoader::CommonPixel>(width * height);
    00427   sprite.width = width;
    00428   sprite.height = height;
    00429   sprite.x_offs = slot->bitmap_left;
    00430   // XXX 2 should be determined somehow... it's right for the normal face
    00431   y_adj = (size == FS_NORMAL) ? 2 : 0;
    00432   sprite.y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
    00433
    00434   /* Draw shadow for medium size */
    00435   if (size == FS_NORMAL) {
    00436     for (y = 0; y < slot->bitmap.rows; y++) {
    00437       for (x = 0; x < slot->bitmap.width; x++) {
    00438         if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
    00439           sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR;
    00440           sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
    00441         }
    00442       }
    00443     }
    00444   }
    00445
    00446   for (y = 0; y < slot->bitmap.rows; y++) {
    00447     for (x = 0; x < slot->bitmap.width; x++) {
    00448       if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
    00449         sprite.data[x + y * sprite.width].m = FACE_COLOUR;
    00450         sprite.data[x + y * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF;
    00451       }
    00452     }
    00453   }
    00454
    00455   new_glyph.sprite = BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, AllocateFont);
    00456   free(sprite.data);
    00457   new_glyph.width  = (slot->advance.x >> 6) + (size != FS_NORMAL);
    00458
    00459   SetGlyphPtr(size, key, &new_glyph);
    00460
    00461   return new_glyph.sprite;
    00462 }
    00463
    00464
    00465 uint GetGlyphWidth(FontSize size, WChar key)
    00466 {
    00467   FT_Face face = GetFontFace(size);
    00468   GlyphEntry *glyph;
    00469
    00470   if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
    00471     SpriteID sprite = GetUnicodeGlyph(size, key);
    00472     if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
    00473     return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
    00474   }
    00475
    00476   glyph = GetGlyphPtr(size, key);
    00477   if (glyph == NULL || glyph->sprite == NULL) {
    00478     GetGlyph(size, key);
    00479     glyph = GetGlyphPtr(size, key);
    00480   }
    00481
    00482   return glyph->width;
    00483 }
    00484
    00485
    00486 #endif /* WITH_FREETYPE */
    00487
    00488 /* Sprite based glyph mapping */
    00489
    00490 #include "table/unicode.h"
    00491
    00492 static SpriteID **_unicode_glyph_map[FS_END];
    00493
    00494
    00496 static SpriteID GetFontBase(FontSize size)
    00497 {
    00498   switch (size) {
    00499     default: NOT_REACHED();
    00500     case FS_NORMAL: return SPR_ASCII_SPACE;
    00501     case FS_SMALL:  return SPR_ASCII_SPACE_SMALL;
    00502     case FS_LARGE:  return SPR_ASCII_SPACE_BIG;
    00503   }
    00504 }
    00505
    00506
    00507 SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
    00508 {
    00509   if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
    00510   return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
    00511 }
    00512
    00513
    00514 void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
    00515 {
    00516   if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = CallocT<SpriteID*>(256);
    00517   if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = CallocT<SpriteID>(256);
    00518   _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
    00519 }
    00520
    00521
    00522 void InitializeUnicodeGlyphMap()
    00523 {
    00524   for (FontSize size = FS_NORMAL; size != FS_END; size++) {
    00525     /* Clear out existing glyph map if it exists */
    00526     if (_unicode_glyph_map[size] != NULL) {
    00527       for (uint i = 0; i < 256; i++) {
    00528         if (_unicode_glyph_map[size][i] != NULL) free(_unicode_glyph_map[size][i]);
    00529       }
    00530       free(_unicode_glyph_map[size]);
    00531       _unicode_glyph_map[size] = NULL;
    00532     }
    00533
    00534     SpriteID base = GetFontBase(size);
    00535
    00536     for (uint i = ASCII_LETTERSTART; i < 256; i++) {
    00537       SpriteID sprite = base + i - ASCII_LETTERSTART;
    00538       if (!SpriteExists(sprite)) continue;
    00539       SetUnicodeGlyph(size, i, sprite);
    00540       SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
    00541     }
    00542
    00543     for (uint i = 0; i < lengthof(_default_unicode_map); i++) {
    00544       byte key = _default_unicode_map[i].key;
    00545       if (key == CLRA || key == CLRL) {
    00546         /* Clear the glyph. This happens if the glyph at this code point
    00547          * is non-standard and should be accessed by an SCC_xxx enum
    00548          * entry only. */
    00549         if (key == CLRA || size == FS_LARGE) {
    00550           SetUnicodeGlyph(size, _default_unicode_map[i].code, 0);
    00551         }
    00552       } else {
    00553         SpriteID sprite = base + key - ASCII_LETTERSTART;
    00554         SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
    00555       }
    00556     }
    00557   }
    00558 }