|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "functions.h" |
|
6 #include "macros.h" |
|
7 #include "debug.h" |
|
8 #include "table/sprites.h" |
|
9 #include "table/control_codes.h" |
|
10 #include "spritecache.h" |
|
11 #include "gfx.h" |
|
12 #include "string.h" |
|
13 #include "fontcache.h" |
|
14 |
|
15 #ifdef WITH_FREETYPE |
|
16 |
|
17 #include <ft2build.h> |
|
18 #include FT_FREETYPE_H |
|
19 #include FT_GLYPH_H |
|
20 |
|
21 #ifdef WITH_FONTCONFIG |
|
22 #include <fontconfig/fontconfig.h> |
|
23 #endif |
|
24 |
|
25 static FT_Library _library = NULL; |
|
26 static FT_Face _face_small = NULL; |
|
27 static FT_Face _face_medium = NULL; |
|
28 static FT_Face _face_large = NULL; |
|
29 |
|
30 FreeTypeSettings _freetype; |
|
31 |
|
32 enum { |
|
33 FACE_COLOUR = 1, |
|
34 SHADOW_COLOUR = 2, |
|
35 }; |
|
36 |
|
37 /** Get the font loaded into a Freetype face by using a font-name. |
|
38 * If no appropiate font is found, the function returns an error */ |
|
39 #ifdef WIN32 |
|
40 #include <windows.h> |
|
41 #include <tchar.h> |
|
42 #include <shlobj.h> // SHGetFolderPath |
|
43 #include "win32.h" |
|
44 |
|
45 /* Get the font file to be loaded into Freetype by looping the registry |
|
46 * location where windows lists all installed fonts. Not very nice, will |
|
47 * surely break if the registry path changes, but it works. Much better |
|
48 * solution would be to use CreateFont, and extract the font data from it |
|
49 * by GetFontData. The problem with this is that the font file needs to be |
|
50 * kept in memory then until the font is no longer needed. This could mean |
|
51 * an additional memory usage of 30MB (just for fonts!) when using an eastern |
|
52 * font for all font sizes */ |
|
53 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" |
|
54 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" |
|
55 static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) |
|
56 { |
|
57 FT_Error err = FT_Err_Cannot_Open_Resource; |
|
58 HKEY hKey; |
|
59 LONG ret; |
|
60 TCHAR vbuffer[MAX_PATH], dbuffer[256]; |
|
61 TCHAR *font_namep; |
|
62 char *font_path; |
|
63 uint index; |
|
64 |
|
65 /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the |
|
66 * "Windows NT" key, on Windows 9x in the Windows key. To save us having |
|
67 * to retrieve the windows version, we'll just query both */ |
|
68 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey); |
|
69 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey); |
|
70 |
|
71 if (ret != ERROR_SUCCESS) { |
|
72 DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts"); |
|
73 return err; |
|
74 } |
|
75 |
|
76 /* For Unicode we need some conversion between widechar and |
|
77 * normal char to match the data returned by RegEnumValue, |
|
78 * otherwise just use parameter */ |
|
79 #if defined(UNICODE) |
|
80 font_namep = malloc(MAX_PATH * sizeof(TCHAR)); |
|
81 MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR)); |
|
82 #else |
|
83 font_namep = (char*)font_name; // only cast because in unicode pointer is not const |
|
84 #endif |
|
85 |
|
86 for (index = 0;; index++) { |
|
87 TCHAR *s; |
|
88 DWORD vbuflen = lengthof(vbuffer); |
|
89 DWORD dbuflen = lengthof(dbuffer); |
|
90 |
|
91 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen); |
|
92 if (ret != ERROR_SUCCESS) goto registry_no_font_found; |
|
93 |
|
94 /* The font names in the registry are of the following 3 forms: |
|
95 * - ADMUI3.fon |
|
96 * - Book Antiqua Bold (TrueType) |
|
97 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType) |
|
98 * We will strip the font-type '()' if any and work with the font name |
|
99 * itself, which must match exactly; if... |
|
100 * TTC files, font files which contain more than one font are seperated |
|
101 * byt '&'. Our best bet will be to do substr match for the fontname |
|
102 * and then let FreeType figure out which index to load */ |
|
103 s = _tcschr(vbuffer, _T('(')); |
|
104 if (s != NULL) s[-1] = '\0'; |
|
105 |
|
106 if (_tcschr(vbuffer, _T('&')) == NULL) { |
|
107 if (_tcsicmp(vbuffer, font_namep) == 0) break; |
|
108 } else { |
|
109 if (_tcsstr(vbuffer, font_namep) != NULL) break; |
|
110 } |
|
111 } |
|
112 |
|
113 if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) { |
|
114 DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory"); |
|
115 goto folder_error; |
|
116 } |
|
117 |
|
118 /* Some fonts are contained in .ttc files, TrueType Collection fonts. These |
|
119 * contain multiple fonts inside this single file. GetFontData however |
|
120 * returns the whole file, so we need to check each font inside to get the |
|
121 * proper font. |
|
122 * Also note that FreeType does not support UNICODE filesnames! */ |
|
123 #if defined(UNICODE) |
|
124 /* We need a cast here back from wide because FreeType doesn't support |
|
125 * widechar filenames. Just use the buffer we allocated before for the |
|
126 * font_name search */ |
|
127 font_path = (char*)font_namep; |
|
128 WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR)); |
|
129 #else |
|
130 font_path = vbuffer; |
|
131 #endif |
|
132 |
|
133 ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR)); |
|
134 ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR)); |
|
135 index = 0; |
|
136 do { |
|
137 err = FT_New_Face(_library, font_path, index, face); |
|
138 if (err != FT_Err_Ok) break; |
|
139 |
|
140 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break; |
|
141 err = FT_Err_Cannot_Open_Resource; |
|
142 |
|
143 } while ((FT_Long)++index != (*face)->num_faces); |
|
144 |
|
145 |
|
146 folder_error: |
|
147 #if defined(UNICODE) |
|
148 free(font_path); |
|
149 #endif |
|
150 registry_no_font_found: |
|
151 RegCloseKey(hKey); |
|
152 return err; |
|
153 } |
|
154 #else |
|
155 # ifdef WITH_FONTCONFIG |
|
156 static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) |
|
157 { |
|
158 FT_Error err = FT_Err_Cannot_Open_Resource; |
|
159 |
|
160 if (!FcInit()) { |
|
161 ShowInfoF("Unable to load font configuration"); |
|
162 } else { |
|
163 FcPattern *match; |
|
164 FcPattern *pat; |
|
165 FcFontSet *fs; |
|
166 FcResult result; |
|
167 char *font_style; |
|
168 char *font_family; |
|
169 |
|
170 /* Split & strip the font's style */ |
|
171 font_family = strdup(font_name); |
|
172 font_style = strchr(font_family, ','); |
|
173 if (font_style != NULL) { |
|
174 font_style[0] = '\0'; |
|
175 font_style++; |
|
176 while (*font_style == ' ' || *font_style == '\t') font_style++; |
|
177 } |
|
178 |
|
179 /* Resolve the name and populate the information structure */ |
|
180 pat = FcNameParse((FcChar8*)font_family); |
|
181 if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style); |
|
182 FcConfigSubstitute(0, pat, FcMatchPattern); |
|
183 FcDefaultSubstitute(pat); |
|
184 fs = FcFontSetCreate(); |
|
185 match = FcFontMatch(0, pat, &result); |
|
186 |
|
187 if (fs != NULL && match != NULL) { |
|
188 int i; |
|
189 FcChar8 *family; |
|
190 FcChar8 *style; |
|
191 FcChar8 *file; |
|
192 FcFontSetAdd(fs, match); |
|
193 |
|
194 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) { |
|
195 /* Try the new filename */ |
|
196 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch && |
|
197 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch && |
|
198 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) { |
|
199 |
|
200 /* The correct style? */ |
|
201 if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue; |
|
202 |
|
203 /* Font config takes the best shot, which, if the family name is spelled |
|
204 * wrongly a 'random' font, so check whether the family name is the |
|
205 * same as the supplied name */ |
|
206 if (strcasecmp(font_family, (char*)family) == 0) { |
|
207 err = FT_New_Face(_library, (char *)file, 0, face); |
|
208 } |
|
209 } |
|
210 } |
|
211 } |
|
212 |
|
213 free(font_family); |
|
214 FcPatternDestroy(pat); |
|
215 FcFontSetDestroy(fs); |
|
216 FcFini(); |
|
217 } |
|
218 |
|
219 return err; |
|
220 } |
|
221 # else |
|
222 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;} |
|
223 # endif /* WITH_FONTCONFIG */ |
|
224 |
|
225 #endif |
|
226 |
|
227 /** |
|
228 * Loads the freetype font. |
|
229 * First type to load the fontname as if it were a path. If that fails, |
|
230 * try to resolve the filename of the font using fontconfig, where the |
|
231 * format is 'font family name' or 'font family name, font style'. |
|
232 */ |
|
233 static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type) |
|
234 { |
|
235 FT_Error error; |
|
236 |
|
237 if (strlen(font_name) == 0) return; |
|
238 |
|
239 error = FT_New_Face(_library, font_name, 0, face); |
|
240 |
|
241 if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, face); |
|
242 |
|
243 if (error == FT_Err_Ok) { |
|
244 DEBUG(freetype, 2, "Requested '%s', using '%s %s'", font_name, (*face)->family_name, (*face)->style_name); |
|
245 |
|
246 /* Attempt to select the unicode character map */ |
|
247 error = FT_Select_Charmap(*face, ft_encoding_unicode); |
|
248 if (error == FT_Err_Ok) return; // Success |
|
249 |
|
250 if (error == FT_Err_Invalid_CharMap_Handle) { |
|
251 /* Try to pick a different character map instead. We default to |
|
252 * the first map, but platform_id 0 encoding_id 0 should also |
|
253 * be unicode (strange system...) */ |
|
254 FT_CharMap found = (*face)->charmaps[0]; |
|
255 int i; |
|
256 |
|
257 for (i = 0; i < (*face)->num_charmaps; i++) { |
|
258 FT_CharMap charmap = (*face)->charmaps[i]; |
|
259 if (charmap->platform_id == 0 && charmap->encoding_id == 0) { |
|
260 found = charmap; |
|
261 } |
|
262 } |
|
263 |
|
264 if (found != NULL) { |
|
265 error = FT_Set_Charmap(*face, found); |
|
266 if (error == FT_Err_Ok) return; |
|
267 } |
|
268 } |
|
269 } |
|
270 |
|
271 FT_Done_Face(*face); |
|
272 *face = NULL; |
|
273 |
|
274 ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error); |
|
275 } |
|
276 |
|
277 |
|
278 void InitFreeType(void) |
|
279 { |
|
280 if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) { |
|
281 DEBUG(freetype, 1, "No font faces specified, using sprite fonts instead"); |
|
282 return; |
|
283 } |
|
284 |
|
285 if (FT_Init_FreeType(&_library) != FT_Err_Ok) { |
|
286 ShowInfoF("Unable to initialize FreeType, using sprite fonts instead"); |
|
287 return; |
|
288 } |
|
289 |
|
290 DEBUG(freetype, 2, "Initialized"); |
|
291 |
|
292 /* Load each font */ |
|
293 LoadFreeTypeFont(_freetype.small_font, &_face_small, "small"); |
|
294 LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium"); |
|
295 LoadFreeTypeFont(_freetype.large_font, &_face_large, "large"); |
|
296 |
|
297 /* Set each font size */ |
|
298 if (_face_small != NULL) FT_Set_Pixel_Sizes(_face_small, 0, _freetype.small_size); |
|
299 if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size); |
|
300 if (_face_large != NULL) FT_Set_Pixel_Sizes(_face_large, 0, _freetype.large_size); |
|
301 } |
|
302 |
|
303 |
|
304 static FT_Face GetFontFace(FontSize size) |
|
305 { |
|
306 switch (size) { |
|
307 default: NOT_REACHED(); |
|
308 case FS_NORMAL: return _face_medium; |
|
309 case FS_SMALL: return _face_small; |
|
310 case FS_LARGE: return _face_large; |
|
311 } |
|
312 } |
|
313 |
|
314 |
|
315 typedef struct GlyphEntry { |
|
316 Sprite *sprite; |
|
317 byte width; |
|
318 } GlyphEntry; |
|
319 |
|
320 |
|
321 /* The glyph cache. This is structured to reduce memory consumption. |
|
322 * 1) There is a 'segment' table for each font size. |
|
323 * 2) Each segment table is a discrete block of characters. |
|
324 * 3) Each block contains 256 (aligned) characters sequential characters. |
|
325 * |
|
326 * The cache is accessed in the following way: |
|
327 * For character 0x0041 ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41] |
|
328 * For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC] |
|
329 * |
|
330 * Currently only 256 segments are allocated, "limiting" us to 65536 characters. |
|
331 * This can be simply changed in the two functions Get & SetGlyphPtr. |
|
332 */ |
|
333 static GlyphEntry **_glyph_ptr[FS_END]; |
|
334 |
|
335 |
|
336 static GlyphEntry *GetGlyphPtr(FontSize size, WChar key) |
|
337 { |
|
338 if (_glyph_ptr[size] == NULL) return NULL; |
|
339 if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL; |
|
340 return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)]; |
|
341 } |
|
342 |
|
343 |
|
344 static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph) |
|
345 { |
|
346 if (_glyph_ptr[size] == NULL) { |
|
347 DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size); |
|
348 _glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr)); |
|
349 } |
|
350 |
|
351 if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) { |
|
352 DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size); |
|
353 _glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr)); |
|
354 } |
|
355 |
|
356 DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size); |
|
357 _glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite; |
|
358 _glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width; |
|
359 } |
|
360 |
|
361 |
|
362 const Sprite *GetGlyph(FontSize size, WChar key) |
|
363 { |
|
364 FT_Face face = GetFontFace(size); |
|
365 FT_GlyphSlot slot; |
|
366 GlyphEntry new_glyph; |
|
367 GlyphEntry *glyph; |
|
368 Sprite *sprite; |
|
369 int width; |
|
370 int height; |
|
371 int x; |
|
372 int y; |
|
373 int y_adj; |
|
374 |
|
375 assert(IsPrintable(key)); |
|
376 |
|
377 /* Bail out if no face loaded, or for our special characters */ |
|
378 if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) { |
|
379 SpriteID sprite = GetUnicodeGlyph(size, key); |
|
380 if (sprite == 0) sprite = GetUnicodeGlyph(size, '?'); |
|
381 return GetSprite(sprite); |
|
382 } |
|
383 |
|
384 /* Check for the glyph in our cache */ |
|
385 glyph = GetGlyphPtr(size, key); |
|
386 if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite; |
|
387 |
|
388 slot = face->glyph; |
|
389 |
|
390 FT_Load_Char(face, key, FT_LOAD_DEFAULT); |
|
391 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); |
|
392 |
|
393 /* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */ |
|
394 width = max(1, slot->bitmap.width + (size == FS_NORMAL)); |
|
395 height = max(1, slot->bitmap.rows + (size == FS_NORMAL)); |
|
396 |
|
397 /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */ |
|
398 sprite = calloc(width * height + 8, 1); |
|
399 sprite->info = 1; |
|
400 sprite->width = width; |
|
401 sprite->height = height; |
|
402 sprite->x_offs = slot->bitmap_left; |
|
403 // XXX 2 should be determined somehow... it's right for the normal face |
|
404 y_adj = (size == FS_NORMAL) ? 2 : 0; |
|
405 sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj; |
|
406 |
|
407 /* Draw shadow for medium size */ |
|
408 if (size == FS_NORMAL) { |
|
409 for (y = 0; y < slot->bitmap.rows; y++) { |
|
410 for (x = 0; x < slot->bitmap.width; x++) { |
|
411 if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) { |
|
412 sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR; |
|
413 } |
|
414 } |
|
415 } |
|
416 } |
|
417 |
|
418 for (y = 0; y < slot->bitmap.rows; y++) { |
|
419 for (x = 0; x < slot->bitmap.width; x++) { |
|
420 if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) { |
|
421 sprite->data[x + y * sprite->width] = FACE_COLOUR; |
|
422 } |
|
423 } |
|
424 } |
|
425 |
|
426 new_glyph.sprite = sprite; |
|
427 new_glyph.width = (slot->advance.x >> 6) + (size != FS_NORMAL); |
|
428 |
|
429 SetGlyphPtr(size, key, &new_glyph); |
|
430 |
|
431 return sprite; |
|
432 } |
|
433 |
|
434 |
|
435 uint GetGlyphWidth(FontSize size, WChar key) |
|
436 { |
|
437 FT_Face face = GetFontFace(size); |
|
438 GlyphEntry *glyph; |
|
439 |
|
440 if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) { |
|
441 SpriteID sprite = GetUnicodeGlyph(size, key); |
|
442 if (sprite == 0) sprite = GetUnicodeGlyph(size, '?'); |
|
443 return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0; |
|
444 } |
|
445 |
|
446 glyph = GetGlyphPtr(size, key); |
|
447 if (glyph == NULL || glyph->sprite == NULL) { |
|
448 GetGlyph(size, key); |
|
449 glyph = GetGlyphPtr(size, key); |
|
450 } |
|
451 |
|
452 return glyph->width; |
|
453 } |
|
454 |
|
455 |
|
456 #endif /* WITH_FREETYPE */ |
|
457 |
|
458 /* Sprite based glyph mapping */ |
|
459 |
|
460 #include "table/unicode.h" |
|
461 |
|
462 static SpriteID **_unicode_glyph_map[FS_END]; |
|
463 |
|
464 |
|
465 /** Get the SpriteID of the first glyph for the given font size */ |
|
466 static SpriteID GetFontBase(FontSize size) |
|
467 { |
|
468 switch (size) { |
|
469 default: NOT_REACHED(); |
|
470 case FS_NORMAL: return SPR_ASCII_SPACE; |
|
471 case FS_SMALL: return SPR_ASCII_SPACE_SMALL; |
|
472 case FS_LARGE: return SPR_ASCII_SPACE_BIG; |
|
473 } |
|
474 } |
|
475 |
|
476 |
|
477 SpriteID GetUnicodeGlyph(FontSize size, uint32 key) |
|
478 { |
|
479 if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0; |
|
480 return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)]; |
|
481 } |
|
482 |
|
483 |
|
484 void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite) |
|
485 { |
|
486 if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size])); |
|
487 if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size])); |
|
488 _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite; |
|
489 } |
|
490 |
|
491 |
|
492 void InitializeUnicodeGlyphMap(void) |
|
493 { |
|
494 FontSize size; |
|
495 SpriteID base; |
|
496 SpriteID sprite; |
|
497 uint i; |
|
498 |
|
499 for (size = FS_NORMAL; size != FS_END; size++) { |
|
500 /* Clear out existing glyph map if it exists */ |
|
501 if (_unicode_glyph_map[size] != NULL) { |
|
502 for (i = 0; i < 256; i++) { |
|
503 if (_unicode_glyph_map[size][i] != NULL) free(_unicode_glyph_map[size][i]); |
|
504 } |
|
505 free(_unicode_glyph_map[size]); |
|
506 _unicode_glyph_map[size] = NULL; |
|
507 } |
|
508 |
|
509 base = GetFontBase(size); |
|
510 for (i = ASCII_LETTERSTART; i < 256; i++) { |
|
511 sprite = base + i - ASCII_LETTERSTART; |
|
512 if (!SpriteExists(sprite)) continue; |
|
513 SetUnicodeGlyph(size, i, sprite); |
|
514 SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite); |
|
515 } |
|
516 for (i = 0; i < lengthof(_default_unicode_map); i++) { |
|
517 sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART; |
|
518 SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite); |
|
519 } |
|
520 } |
|
521 } |
|
522 |
|
523 |
|
524 |