]> icculus.org git repositories - divverent/darkplaces.git/blob - ft2.c
remove my working-comments
[divverent/darkplaces.git] / ft2.c
1 /* FreeType 2 and UTF-8 encoding support for
2  * DarkPlaces
3  */
4 #include "quakedef.h"
5
6 #include "ft2.h"
7
8 #include "ft2_defs.h"
9
10 /*
11 ================================================================================
12 Function definitions. Taken from the freetype2 headers.
13 ================================================================================
14 */
15
16
17 FT_EXPORT( FT_Error )
18 (*qFT_Init_FreeType)( FT_Library  *alibrary );
19 FT_EXPORT( FT_Error )
20 (*qFT_Done_FreeType)( FT_Library  library );
21 FT_EXPORT( FT_Error )
22 (*qFT_New_Face)( FT_Library   library,
23                  const char*  filepathname,
24                  FT_Long      face_index,
25                  FT_Face     *aface );
26 FT_EXPORT( FT_Error )
27 (*qFT_New_Memory_Face)( FT_Library      library,
28                         const FT_Byte*  file_base,
29                         FT_Long         file_size,
30                         FT_Long         face_index,
31                         FT_Face        *aface );
32 FT_EXPORT( FT_Error )
33 (*qFT_Done_Face)( FT_Face  face );
34 FT_EXPORT( FT_Error )
35 (*qFT_Select_Size)( FT_Face  face,
36                     FT_Int   strike_index );
37 FT_EXPORT( FT_Error )
38 (*qFT_Request_Size)( FT_Face          face,
39                      FT_Size_Request  req );
40 FT_EXPORT( FT_Error )
41 (*qFT_Set_Char_Size)( FT_Face     face,
42                       FT_F26Dot6  char_width,
43                       FT_F26Dot6  char_height,
44                       FT_UInt     horz_resolution,
45                       FT_UInt     vert_resolution );
46 FT_EXPORT( FT_Error )
47 (*qFT_Set_Pixel_Sizes)( FT_Face  face,
48                         FT_UInt  pixel_width,
49                         FT_UInt  pixel_height );
50 FT_EXPORT( FT_Error )
51 (*qFT_Load_Glyph)( FT_Face   face,
52                    FT_UInt   glyph_index,
53                    FT_Int32  load_flags );
54 FT_EXPORT( FT_Error )
55 (*qFT_Load_Char)( FT_Face   face,
56                   FT_ULong  char_code,
57                   FT_Int32  load_flags );
58 FT_EXPORT( FT_UInt )
59 (*qFT_Get_Char_Index)( FT_Face   face,
60                        FT_ULong  charcode );
61 FT_EXPORT( FT_Error )
62 (*qFT_Render_Glyph)( FT_GlyphSlot    slot,
63                      FT_Render_Mode  render_mode );
64 FT_EXPORT( FT_Error )
65 (*qFT_Get_Kerning)( FT_Face     face,
66                     FT_UInt     left_glyph,
67                     FT_UInt     right_glyph,
68                     FT_UInt     kern_mode,
69                     FT_Vector  *akerning );
70
71 /*
72 ================================================================================
73 Support for dynamically loading the FreeType2 library
74 ================================================================================
75 */
76
77 static dllfunction_t ft2funcs[] =
78 {
79         {"FT_Init_FreeType",            (void **) &qFT_Init_FreeType},
80         {"FT_Done_FreeType",            (void **) &qFT_Done_FreeType},
81         {"FT_New_Face",                 (void **) &qFT_New_Face},
82         {"FT_New_Memory_Face",          (void **) &qFT_New_Memory_Face},
83         {"FT_Done_Face",                (void **) &qFT_Done_Face},
84         {"FT_Select_Size",              (void **) &qFT_Select_Size},
85         {"FT_Request_Size",             (void **) &qFT_Request_Size},
86         {"FT_Set_Char_Size",            (void **) &qFT_Set_Char_Size},
87         {"FT_Set_Pixel_Sizes",          (void **) &qFT_Set_Pixel_Sizes},
88         {"FT_Load_Glyph",               (void **) &qFT_Load_Glyph},
89         {"FT_Load_Char",                (void **) &qFT_Load_Char},
90         {"FT_Get_Char_Index",           (void **) &qFT_Get_Char_Index},
91         {"FT_Render_Glyph",             (void **) &qFT_Render_Glyph},
92         {"FT_Get_Kerning",              (void **) &qFT_Get_Kerning},
93         {NULL, NULL}
94 };
95
96 /// Handle for FreeType2 DLL
97 static dllhandle_t ft2_dll = NULL;
98
99 /// Memory pool for fonts
100 static mempool_t *font_mempool= NULL;
101 static rtexturepool_t *font_texturepool = NULL;
102
103 /// FreeType library handle
104 static FT_Library font_ft2lib = NULL;
105
106 /*
107 ====================
108 Font_CloseLibrary
109
110 Unload the FreeType2 DLL
111 ====================
112 */
113 void Font_CloseLibrary (void)
114 {
115         if (font_mempool)
116                 Mem_FreePool(&font_mempool);
117         if (font_texturepool)
118                 R_FreeTexturePool(&font_texturepool);
119         if (font_ft2lib && qFT_Done_FreeType)
120         {
121                 qFT_Done_FreeType(font_ft2lib);
122                 font_ft2lib = NULL;
123         }
124         Sys_UnloadLibrary (&ft2_dll);
125 }
126
127 /*
128 ====================
129 Font_OpenLibrary
130
131 Try to load the FreeType2 DLL
132 ====================
133 */
134 qboolean Font_OpenLibrary (void)
135 {
136         const char* dllnames [] =
137         {
138 #if defined(WIN64)
139                 #error path for freetype 2 dll
140 #elif defined(WIN32)
141                 #error path for freetype 2 dll
142 #elif defined(MACOSX)
143                 "libfreetype.dylib",
144 #else
145                 "libfreetype.so.6",
146                 "libfreetype.so",
147 #endif
148                 NULL
149         };
150
151         // Already loaded?
152         if (ft2_dll)
153                 return true;
154
155         // Load the DLL
156         if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
157                 return false;
158         return true;
159 }
160
161 /*
162 ====================
163 Font_Init
164
165 Initialize the freetype2 font subsystem
166 ====================
167 */
168
169 static font_t test_font;
170
171 static void font_start(void)
172 {
173         if (!Font_OpenLibrary())
174                 return;
175
176         if (qFT_Init_FreeType(&font_ft2lib))
177         {
178                 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
179                 Font_CloseLibrary();
180                 return;
181         }
182
183         font_mempool = Mem_AllocPool("FONT", 0, NULL);
184         if (!font_mempool)
185         {
186                 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
187                 Font_CloseLibrary();
188                 return;
189         }
190
191         font_texturepool = R_AllocTexturePool();
192         if (!font_texturepool)
193         {
194                 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
195                 Font_CloseLibrary();
196                 return;
197         }
198
199         if (!Font_LoadFont("gfx/test", 16, &test_font))
200         {
201                 Con_Print("ERROR: Failed to load test font!\n");
202                 Font_CloseLibrary();
203                 return;
204         }
205 }
206
207 static void font_shutdown(void)
208 {
209         Font_CloseLibrary();
210 }
211
212 static void font_newmap(void)
213 {
214 }
215
216 void Font_Init(void)
217 {
218         R_RegisterModule("Font_FreeType2", font_start, font_shutdown, font_newmap);
219 }
220
221 /*
222 ================================================================================
223 UTF-8 encoding and decoding functions follow.
224 ================================================================================
225 */
226
227 /** Get the number of characters in in an UTF-8 string.
228  * @param _s    An utf-8 encoded null-terminated string.
229  * @return      The number of unicode characters in the string.
230  */
231 size_t u8_strlen(const char *_s)
232 {
233         size_t len = 0;
234         unsigned char *s = (unsigned char*)_s;
235         while (*s)
236         {
237                 // ascii char
238                 if (*s <= 0x7F)
239                 {
240                         ++len;
241                         ++s;
242                         continue;
243                 }
244
245                 // start of a wide character
246                 if (*s & 0xC0)
247                 {
248                         ++len;
249                         for (++s; *s >= 0x80 && *s <= 0xC0; ++s);
250                         continue;
251                 }
252
253                 // part of a wide character, we ignore that one
254                 if (*s <= 0xBF) // 10111111
255                 {
256                         ++s;
257                         continue;
258                 }
259         }
260         return len;
261 }
262
263 /** Fetch a character from an utf-8 encoded string.
264  * @param _s      The start of an utf-8 encoded multi-byte character.
265  * @param _end    Will point to after the first multi-byte character.
266  * @return        The 32-bit integer representation of the first multi-byte character.
267  */
268 Uchar u8_getchar(const char *_s, const char **_end)
269 {
270         const unsigned char *s = (unsigned char*)_s;
271         Uchar u;
272         unsigned char mask;
273         unsigned char v;
274
275         if (*s < 0x80)
276         {
277                 if (_end)
278                         *_end = _s + 1;
279                 return (U_int32)*s;
280         }
281
282         if (*s < 0xC0)
283         {
284                 // starting within a wide character - skip it and retrieve the one after it
285                 for (++s; *s >= 0x80 && *s < 0xC0; ++s);
286                 // or we could return '?' here?
287         }
288         // for a little speedup:
289         if ( (*s & 0xE0) == 0xC0 )
290         {
291                 // 2-byte character
292                 u = ( (s[0] & 0x1F) << 6 ) | (s[1] & 0x3F);
293                 if (_end)
294                         *_end = _s + 2;
295                 return u;
296         }
297         if ( (*s & 0xF0) == 0xE0 )
298         {
299                 // 3-byte character
300                 u = ( (s[0] & 0x0F) << 12 ) | ( (s[1] & 0x3F) << 6 ) | (s[2] & 0x3F);
301                 if (_end)
302                         *_end = _s + 3;
303                 return u;
304         }
305
306         u = 0;
307         mask = 0x7F;
308         v = *s & mask;
309         for (mask >>= 1; v > (*s & mask); mask >>= 1)
310                 v = (*s & mask);
311         u = (Uchar)(*s & mask);
312         for (++s; *s >= 0x80 && *s < 0xC0; ++s)
313                 u = (u << 6) | (*s & 0x3F);
314
315         if (_end)
316                 *_end = (const char*)s;
317
318         return u;
319 }
320
321 /** Encode a wide-character into utf-8.
322  * @param w        The wide character to encode.
323  * @param to       The target buffer the utf-8 encoded string is stored to.
324  * @param maxlen   The maximum number of bytes that fit into the target buffer.
325  * @return         Number of bytes written to the buffer, or less or equal to 0 if the buffer is too small.
326  */
327 int u8_fromchar(Uchar w, char *to, size_t maxlen)
328 {
329         size_t i, j;
330         char bt;
331         char tmp[16];
332
333         if (maxlen < 1)
334                 return -2;
335
336         if (w < 0x80)
337         {
338                 to[0] = (char)w;
339                 if (maxlen < 2)
340                         return -1;
341                 to[1] = 0;
342                 return 1;
343         }
344         // for a little speedup
345         if (w < 0x800)
346         {
347                 if (maxlen < 3)
348                 {
349                         to[0] = 0;
350                         return -1;
351                 }
352                 to[2] = 0;
353                 to[1] = 0x80 | (w & 0x3F); w >>= 6;
354                 to[0] = 0xC0 | w;
355                 return 2;
356         }
357         if (w < 0x10000)
358         {
359                 if (maxlen < 4)
360                 {
361                         to[0] = 0;
362                         return -1;
363                 }
364                 to[3] = 0;
365                 to[2] = 0x80 | (w & 0x3F); w >>= 6;
366                 to[1] = 0x80 | (w & 0x3F); w >>= 6;
367                 to[0] = 0xE0 | w;
368                 return 3;
369         }
370
371         // "more general" version:
372
373         // check how much space we need and store data into a
374         // temp buffer - this is faster than recalculating again
375         i = 0;
376         bt = 0;
377         while (w)
378         {
379                 tmp[i++] = 0x80 | (w & 0x3F);
380                 bt = (bt >> 1) | 0x80;
381                 w >>= 6;
382                 // see if we still fit into the target buffer
383                 if (i+1 >= maxlen) // +1 for the \0
384                         return -i;
385
386                 // there are no characters which take up that much space yet
387                 // and there won't be for the next many many years, still... let's be safe
388                 if (i >= sizeof(tmp))
389                         return -1;
390         }
391         tmp[i-1] |= bt;
392         for (j = 0; j < i; ++j)
393         {
394                 to[i-j-1] = tmp[j];
395         }
396
397         to[i] = 0;
398         return i;
399 }
400
401 /** Convert a utf-8 multibyte string to a wide character string.
402  * @param wcs       The target wide-character buffer.
403  * @param mb        The utf-8 encoded multibyte string to convert.
404  * @param maxlen    The maximum number of wide-characters that fit into the target buffer.
405  * @return          The number of characters written to the target buffer.
406  */
407 size_t u8_mbstowcs(Uchar *wcs, const char *mb, size_t maxlen)
408 {
409         size_t i;
410         for (i = 0; *mb && i < maxlen; ++i)
411                 *wcs++ = u8_getchar(mb, &mb);
412         if (i < maxlen)
413                 *wcs = 0;
414         return i;
415 }
416
417 /** Convert a wide-character string to a utf-8 multibyte string.
418  * @param mb      The target buffer the utf-8 string is written to.
419  * @param wcs     The wide-character string to convert.
420  * @param maxlen  The number bytes that fit into the multibyte target buffer.
421  * @return        The number of bytes written, not including the terminating \0
422  */
423 size_t u8_wcstombs(char *mb, const Uchar *wcs, size_t maxlen)
424 {
425         size_t i;
426         const char *start = mb;
427         for (i = 0; *wcs && i < maxlen; ++i)
428         {
429                 int len;
430                 if ( (len = u8_fromchar(*wcs++, mb, maxlen - i)) < 0)
431                         return (mb - start);
432                 mb += len;
433         }
434         if (i < maxlen)
435                 *mb = 0;
436         return (mb - start);
437 }
438
439 /*
440 ================================================================================
441 Implementation of a more or less lazy font loading and rendering code.
442 ================================================================================
443 */
444
445 // anything should work, but I recommend multiples of 8
446 // since the texture size should be a power of 2
447 #define FONT_CHARS_PER_LINE 16
448 #define FONT_CHAR_LINES 16
449 #define FONT_CHARS_PER_MAP (FONT_CHARS_PER_LINE * FONT_CHAR_LINES)
450
451 typedef struct glyph_slot_s
452 {
453         // we keep the quad coords here only currently
454         // if you need other info, make Font_LoadMapForIndex fill it into this slot
455         double txmin; // texture coordinate in [0,1]
456         double txmax;
457         double tymin;
458         double tymax;
459         float vxmin;
460         float vxmax;
461         float vymin;
462         float vymax;
463         float advance_x;
464         float advance_y;
465 } glyph_slot_t;
466
467 struct font_map_s
468 {
469         Uchar start;
470         struct font_map_s *next;
471
472         rtexture_t *texture;
473         glyph_slot_t glyphs[FONT_CHARS_PER_MAP];
474 };
475
476 static qboolean Font_LoadMapForIndex(font_t *font, Uchar _ch, font_map_t **outmap);
477 qboolean Font_LoadFont(const char *name, int size, font_t *font)
478 {
479         size_t namelen;
480         char filename[PATH_MAX];
481         int status;
482
483         memset(font, 0, sizeof(*font));
484
485         if (!Font_OpenLibrary())
486         {
487                 Con_Printf("WARNING: can't open load font %s\n"
488                            "You need the FreeType2 DLL to load font files\n",
489                            name);
490                 return false;
491         }
492
493         namelen = strlen(name);
494
495         memcpy(filename, name, namelen);
496         memcpy(filename + namelen, ".ttf", 5);
497
498         font->data = FS_LoadFile(filename, font_mempool, false, &font->datasize);
499         if (!font->data)
500         {
501                 // FS_LoadFile being not-quiet should print an error :)
502                 return false;
503         }
504
505
506         status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)font->data, font->datasize, 0, (FT_Face*)&font->face);
507         if (status)
508         {
509                 Con_Printf("ERROR: can't create face for %s\n"
510                            "Error %i\n", // TODO: error strings
511                            name, status);
512                 Mem_Free(font->data);
513                 return false;
514         }
515
516         memcpy(font->name, name, namelen+1);
517         font->size = size;
518         font->glyphSize = font->size * 2;
519         font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
520
521         status = qFT_Set_Pixel_Sizes((FT_Face)font->face, size, size);
522         if (status)
523         {
524                 Con_Printf("ERROR: can't size pixel sizes for face of font %s\n"
525                            "Error %i\n", // TODO: error strings
526                            name, status);
527                 Mem_Free(font->data);
528                 return false;
529         }
530
531         if (!Font_LoadMapForIndex(font, 0, NULL))
532         {
533                 Con_Printf("ERROR: can't load the first character map for %s\n"
534                            "This is fatal\n",
535                            name);
536                 Mem_Free(font->data);
537                 return false;
538         }
539
540         return true;
541 }
542
543 void Font_UnloadFont(font_t *font)
544 {
545         if (font->data)
546                 Mem_Free(font->data);
547         if (ft2_dll)
548         {
549                 if (font->face)
550                 {
551                         qFT_Done_Face((FT_Face)font->face);
552                         font->face = NULL;
553                 }
554         }
555 }
556
557 static qboolean Font_LoadMapForIndex(font_t *font, Uchar _ch, font_map_t **outmap)
558 {
559         char map_identifier[PATH_MAX];
560         unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
561         unsigned char *data;
562         FT_ULong ch, mapch;
563         int status;
564         int tp;
565
566         FT_Face face = font->face;
567
568         int pitch;
569         int gR, gC; // glyph position: row and column
570
571         font_map_t *map;
572
573         int bytesPerPixel = 4; // change the conversion loop too if you change this!
574
575         if (outmap)
576                 *outmap = NULL;
577
578         map = Mem_Alloc(font_mempool, sizeof(font_map_t));
579         if (!map)
580         {
581                 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
582                 return false;
583         }
584
585         pitch = font->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
586         data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * font->glyphSize) * pitch);
587         if (!data)
588         {
589                 Mem_Free(map);
590                 Con_Printf("ERROR: Failed to allocate memory for glyph data for font %s\n", font->name);
591                 return false;
592         }
593
594         // initialize as white texture with zero alpha
595         tp = 0;
596         while (tp < (FONT_CHAR_LINES * font->glyphSize) * pitch)
597         {
598                 data[tp++] = 0xFF;
599                 data[tp++] = 0xFF;
600                 data[tp++] = 0xFF;
601                 data[tp++] = 0x00;
602         }
603
604         map->start = mapidx * FONT_CHARS_PER_MAP;
605         if (!font->font_map)
606                 font->font_map = map;
607         else
608         {
609                 // insert the map at the right place
610                 font_map_t *next = font->font_map;
611                 while(next->next && next->next->start < map->start)
612                         next = next->next;
613                 map->next = next->next;
614                 next->next = map;
615         }
616
617         gR = 0;
618         gC = -1;
619         for (ch = map->start;
620              ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
621              ++ch)
622         {
623                 FT_ULong glyphIndex;
624                 int w, h, x, y;
625                 FT_GlyphSlot glyph;
626                 FT_Bitmap *bmp;
627                 unsigned char *imagedata, *dst, *src;
628                 glyph_slot_t *mapglyph;
629
630                 fprintf(stderr, "------------- GLYPH INFO -----------------\n");
631
632                 ++gC;
633                 if (gC >= FONT_CHARS_PER_LINE)
634                 {
635                         gC -= FONT_CHARS_PER_LINE;
636                         ++gR;
637                 }
638
639                 imagedata = data + gR * pitch * font->glyphSize + gC * font->glyphSize * bytesPerPixel;
640                 glyphIndex = qFT_Get_Char_Index(face, ch);
641
642                 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT);
643                 if (status)
644                 {
645                         Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
646                         continue;
647                 }
648
649                 status = qFT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
650                 if (status)
651                 {
652                         Con_Printf("failed to render glyph %lu for %s\n", glyphIndex, font->name);
653                         continue;
654                 }
655
656                 glyph = face->glyph;
657                 bmp = &glyph->bitmap;
658
659                 w = bmp->width;
660                 h = bmp->rows;
661
662                 if (w > font->glyphSize || h > font->glyphSize)
663                         Con_Printf("WARNING: Glyph %lu is too big in font %s\n", ch, font->name);
664
665                 switch (bmp->pixel_mode)
666                 {
667                 case FT_PIXEL_MODE_MONO:
668                         fprintf(stderr, "  Pixel Mode: MONO\n");
669                         break;
670                 case FT_PIXEL_MODE_GRAY2:
671                         fprintf(stderr, "  Pixel Mode: GRAY2\n");
672                         break;
673                 case FT_PIXEL_MODE_GRAY4:
674                         fprintf(stderr, "  Pixel Mode: GRAY4\n");
675                         break;
676                 case FT_PIXEL_MODE_GRAY:
677                         fprintf(stderr, "  Pixel Mode: GRAY\n");
678                         break;
679                 default:
680                         fprintf(stderr, "  Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
681                         Mem_Free(data);
682                         return false;
683                 }
684                 for (y = 0; y < h; ++y)
685                 {
686                         dst = imagedata + y * pitch;
687                         src = bmp->buffer + y * bmp->pitch;
688
689                         switch (bmp->pixel_mode)
690                         {
691                         case FT_PIXEL_MODE_MONO:
692                                 for (x = 0; x < bmp->width; x += 8)
693                                 {
694                                         unsigned char ch = *src++;
695                                         dst += 3; // shift to alpha byte
696                                         *dst = 255 * ((ch & 0x80) >> 7); dst += 4;
697                                         *dst = 255 * ((ch & 0x40) >> 6); dst += 4;
698                                         *dst = 255 * ((ch & 0x20) >> 5); dst += 4;
699                                         *dst = 255 * ((ch & 0x10) >> 4); dst += 4;
700                                         *dst = 255 * ((ch & 0x08) >> 3); dst += 4;
701                                         *dst = 255 * ((ch & 0x04) >> 2); dst += 4;
702                                         *dst = 255 * ((ch & 0x02) >> 1); dst += 4;
703                                         *dst = 255 * ((ch & 0x01) >> 0); dst++; // compensate the first += 3
704                                 }
705                                 break;
706                         case FT_PIXEL_MODE_GRAY2:
707                                 for (x = 0; x < bmp->width; x += 4)
708                                 {
709                                         unsigned char ch = *src++;
710                                         dst += 3; // shift to alpha byte
711                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
712                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
713                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
714                                         *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst++; // compensate the +=3
715                                 }
716                                 break;
717                         case FT_PIXEL_MODE_GRAY4:
718                                 for (x = 0; x < bmp->width; x += 2)
719                                 {
720                                         unsigned char ch = *src++;
721                                         dst += 3; // shift to alpha byte
722                                         *dst = ( ((ch & 0xF0) >> 4) * 0x24); dst += 4;
723                                         *dst = ( ((ch & 0x0F) ) * 0x24); dst++; // compensate the += 3
724                                 }
725                                 break;
726                         case FT_PIXEL_MODE_GRAY:
727                                 // in this case pitch should equal width
728                                 for (tp = 0; tp < bmp->pitch; ++tp)
729                                         dst[3 + tp*4] = src[tp]; // copy the grey value into the alpha bytes
730
731                                 //memcpy((void*)dst, (void*)src, bmp->pitch);
732                                 //dst += bmp->pitch;
733                                 break;
734                         default:
735                                 break;
736                         }
737                 }
738
739                 // now fill map->glyphs[ch - map->start]
740                 mapch = ch - map->start;
741                 mapglyph = &map->glyphs[mapch];
742
743                 {
744                         double sfx = (1.0/64.0) / (double)font->size;
745                         double sfy = (1.0/64.0) / (double)font->size;
746                         double bearingX = (double)glyph->metrics.horiBearingX * sfx;
747                         double bearingY = (double)glyph->metrics.horiBearingY * sfy;
748                         double advance = (double)glyph->metrics.horiAdvance * sfx;
749                         double mWidth = (double)glyph->metrics.width * sfx;
750                         double mHeight = (double)glyph->metrics.height * sfy;
751                         //double tWidth = bmp->width / (double)font->size;
752                         //double tHeight = bmp->rows / (double)font->size;
753
754                         mapglyph->vxmin = bearingX;
755                         mapglyph->vxmax = bearingX + mWidth;
756                         mapglyph->vymin = -bearingY;
757                         mapglyph->vymax = mHeight - bearingY;
758                         mapglyph->txmin = ( (double)(gC * font->glyphSize) ) / ( (double)(font->glyphSize * FONT_CHARS_PER_LINE) );
759                         mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(font->glyphSize * FONT_CHARS_PER_LINE) );
760                         mapglyph->tymin = ( (double)(gR * font->glyphSize) ) / ( (double)(font->glyphSize * FONT_CHAR_LINES) );
761                         mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(font->glyphSize * FONT_CHAR_LINES) );
762                         //mapglyph->advance_x = advance * font->size;
763                         mapglyph->advance_x = advance;
764                         mapglyph->advance_y = 0;
765
766                         if (developer)
767                         {
768                                 fprintf(stderr, "  Glyph: %lu   at (%i, %i)\n", (unsigned long)ch, gC, gR);
769                                 if (ch >= 32 && ch <= 128)
770                                         fprintf(stderr, "  Character: %c\n", (int)ch);
771                                 fprintf(stderr, "  Vertex info:\n");
772                                 fprintf(stderr, "    X: ( %f  --  %f )\n", mapglyph->vxmin, mapglyph->vxmax);
773                                 fprintf(stderr, "    Y: ( %f  --  %f )\n", mapglyph->vymin, mapglyph->vymax);
774                                 fprintf(stderr, "  Texture info:\n");
775                                 fprintf(stderr, "    S: ( %f  --  %f )\n", mapglyph->txmin, mapglyph->txmax);
776                                 fprintf(stderr, "    T: ( %f  --  %f )\n", mapglyph->tymin, mapglyph->tymax);
777                                 fprintf(stderr, "  Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
778                         }
779                 }
780         }
781
782         // create a texture from the data now
783         dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u_%x.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP, (unsigned)map->start);
784         {
785                 FILE *f = fopen(map_identifier, "wb");
786                 fwrite(data, pitch * FONT_CHAR_LINES * font->glyphSize, 1, f);
787                 fclose(f);
788         }
789         map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
790                                        font->glyphSize * FONT_CHARS_PER_LINE,
791                                        font->glyphSize * FONT_CHAR_LINES,
792                                        data, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_ALWAYSPRECACHE | TEXF_MIPMAP, NULL);
793         Mem_Free(data);
794         if (!map->texture)
795         {
796                 // if the first try isn't successful, keep it with a broken texture
797                 // otherwise we retry to load it every single frame where ft2 rendering is used
798                 // this would be bad...
799                 // only `data' must be freed
800                 return false;
801         }
802         if (outmap)
803                 *outmap = map;
804         return true;
805 }
806
807 extern void _DrawQ_Setup(void);
808
809 // TODO: If no additional stuff ends up in the following static functions
810 // use the DrawQ ones!
811 static void _Font_ProcessDrawFlag(int flags)
812 {
813         _DrawQ_Setup();
814         CHECKGLERROR
815         if(flags == DRAWFLAG_ADDITIVE)
816                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
817         else if(flags == DRAWFLAG_MODULATE)
818                 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
819         else if(flags == DRAWFLAG_2XMODULATE)
820                 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
821         else if(flags == DRAWFLAG_SCREEN)
822                 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
823         else
824                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
825 }
826
827 extern cvar_t r_textcontrast, r_textbrightness, r_textshadow;
828 static const vec4_t string_colors[] =
829 {
830         // Quake3 colors
831         // LordHavoc: why on earth is cyan before magenta in Quake3?
832         // LordHavoc: note: Doom3 uses white for [0] and [7]
833         {0.0, 0.0, 0.0, 1.0}, // black
834         {1.0, 0.0, 0.0, 1.0}, // red
835         {0.0, 1.0, 0.0, 1.0}, // green
836         {1.0, 1.0, 0.0, 1.0}, // yellow
837         {0.0, 0.0, 1.0, 1.0}, // blue
838         {0.0, 1.0, 1.0, 1.0}, // cyan
839         {1.0, 0.0, 1.0, 1.0}, // magenta
840         {1.0, 1.0, 1.0, 1.0}, // white
841         // [515]'s BX_COLOREDTEXT extension
842         {1.0, 1.0, 1.0, 0.5}, // half transparent
843         {0.5, 0.5, 0.5, 1.0}  // half brightness
844         // Black's color table
845         //{1.0, 1.0, 1.0, 1.0},
846         //{1.0, 0.0, 0.0, 1.0},
847         //{0.0, 1.0, 0.0, 1.0},
848         //{0.0, 0.0, 1.0, 1.0},
849         //{1.0, 1.0, 0.0, 1.0},
850         //{0.0, 1.0, 1.0, 1.0},
851         //{1.0, 0.0, 1.0, 1.0},
852         //{0.1, 0.1, 0.1, 1.0}
853 };
854
855 static void Font_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
856 {
857         float C = r_textcontrast.value;
858         float B = r_textbrightness.value;
859         if (colorindex & 0x10000) // that bit means RGB color
860         {
861                 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
862                 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
863                 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
864                 color[3] = (colorindex & 0xf) / 15.0;
865         }
866         else
867                 Vector4Copy(string_colors[colorindex], color);
868         Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
869         if (shadow)
870         {
871                 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
872                 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
873         }
874 }
875
876 float Font_DrawString_Font(float startx, float starty,
877                       const char *text, size_t maxlen,
878                       float w, float h,
879                       float basered, float basegreen, float baseblue, float basealpha,
880                       int flags, int *outcolor, qboolean ignorecolorcodes,
881                       font_t *fnt)
882 {
883         int shadow, colorindex = STRING_COLOR_DEFAULT;
884         float x = startx, y, thisw;
885         float *av, *at, *ac;
886         float color[4];
887         int batchcount;
888         float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
889         float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
890         float color4f[QUADELEMENTS_MAXQUADS*4*4];
891         Uchar ch, mapch;
892         size_t i;
893         const char *text_start = text;
894         int tempcolorindex;
895         font_map_t *prevmap = NULL;
896         font_map_t *map;
897
898         if (maxlen < 1)
899                 maxlen = 1<<30;
900
901         _Font_ProcessDrawFlag(flags);
902
903         R_Mesh_ColorPointer(color4f, 0, 0);
904         R_Mesh_ResetTextureState();
905         R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
906         R_Mesh_VertexPointer(vertex3f, 0, 0);
907         R_SetupGenericShader(true);
908
909         ac = color4f;
910         at = texcoord2f;
911         av = vertex3f;
912         batchcount = 0;
913
914         // We render onto the baseline, so move down by the intended height.
915         // Otherwise the text appears too high since the top edge would be the base line.
916         starty += (double)h * (5.0/6.0); // don't use the complete height
917         // with sane fonts it should be possible to use the font's `ascent` value
918         // but then again, is it safe?
919
920         for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
921         {
922                 text = text_start;
923                 if (!outcolor || *outcolor == -1)
924                         colorindex = STRING_COLOR_DEFAULT;
925                 else
926                         colorindex = *outcolor;
927
928                 Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
929
930                 x = startx;
931                 y = starty;
932                 if (shadow)
933                 {
934                         x += r_textshadow.value;
935                         y += r_textshadow.value;
936                 }
937                 for (i = 0;i < maxlen && *text;i++)
938                 {
939                         if (*text == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
940                         {
941                                 const char *before;
942                                 Uchar chx[3];
943                                 ++text;
944                                 ch = *text; // the color tag is an ASCII character!
945                                 if (ch == STRING_COLOR_RGB_TAG_CHAR)
946                                 {
947                                         // we need some preparation here
948                                         before = text;
949                                         chx[2] = 0;
950                                         if (*text) chx[0] = u8_getchar(text, &text);
951                                         if (*text) chx[1] = u8_getchar(text, &text);
952                                         if (*text) chx[2] = u8_getchar(text, &text);
953                                         if ( ( (chx[0] >= 'A' && chx[0] <= 'F') || (chx[0] >= 'a' && chx[0] <= 'f') || (chx[0] >= '0' && chx[0] <= '9') ) &&
954                                              ( (chx[1] >= 'A' && chx[1] <= 'F') || (chx[1] >= 'a' && chx[1] <= 'f') || (chx[1] >= '0' && chx[1] <= '9') ) &&
955                                              ( (chx[2] >= 'A' && chx[2] <= 'F') || (chx[2] >= 'a' && chx[2] <= 'f') || (chx[2] >= '0' && chx[2] <= '9') ) )
956                                         {
957                                         }
958                                         else
959                                                 chx[2] = 0;
960                                         text = before; // start from the first hex character
961                                 }
962                                 if (ch <= '9' && ch >= '0') // ^[0-9] found
963                                 {
964                                         colorindex = ch - '0';
965                                         Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
966                                         continue;
967                                 }
968                                 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < maxlen && chx[2]) // ^x found
969                                 {
970                                         // building colorindex...
971                                         ch = tolower(text[i+1]);
972                                         tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
973                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
974                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
975                                         else tempcolorindex = 0;
976                                         if (tempcolorindex)
977                                         {
978                                                 ch = tolower(text[i+2]);
979                                                 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
980                                                 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
981                                                 else tempcolorindex = 0;
982                                                 if (tempcolorindex)
983                                                 {
984                                                         ch = tolower(text[i+3]);
985                                                         if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
986                                                         else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
987                                                         else tempcolorindex = 0;
988                                                         if (tempcolorindex)
989                                                         {
990                                                                 colorindex = tempcolorindex | 0xf;
991                                                                 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
992                                                                 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
993                                                                 Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
994                                                                 i+=3;
995                                                                 continue;
996                                                         }
997                                                 }
998                                         }
999                                 }
1000                                 else if (ch == STRING_COLOR_TAG)
1001                                         i++;
1002                                 i--;
1003                         }
1004                         ch = u8_getchar(text, &text);
1005
1006                         map = fnt->font_map;
1007                         while(map && map->start + FONT_CHARS_PER_MAP < ch)
1008                                 map = map->next;
1009                         if (!map)
1010                         {
1011                                 if (!Font_LoadMapForIndex(fnt, ch, &map))
1012                                 {
1013                                         shadow = -1;
1014                                         break;
1015                                 }
1016                                 if (!map)
1017                                 {
1018                                         // this shouldn't happen
1019                                         shadow = -1;
1020                                         break;
1021                                 }
1022                         }
1023
1024                         if (map != prevmap && batchcount)
1025                         {
1026                                 // we need a different character map, render what we currently have:
1027                                 GL_LockArrays(0, batchcount * 4);
1028                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1029                                 GL_LockArrays(0, 0);
1030                                 batchcount = 0;
1031                                 ac = color4f;
1032                                 at = texcoord2f;
1033                                 av = vertex3f;
1034                         }
1035
1036                         // TODO: don't call Mesh_Draw all the time
1037                         // call it when the texture changes or the batchcount hits the limit
1038
1039                         R_Mesh_TexBind(0, R_GetTexture(map->texture));
1040                         R_SetupGenericShader(true);
1041
1042                         mapch = ch - map->start;
1043                         thisw = map->glyphs[mapch].advance_x;
1044
1045                         ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1046                         ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1047                         ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1048                         ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1049                         at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1050                         at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1051                         at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1052                         at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1053 #define PIXEL_X(x) ( (x)/vid_conwidth.value * vid.width )
1054 #define PIXEL_Y(x) ( (x)/vid_conheight.value * vid.height )
1055                         av[ 0] = x + w * PIXEL_X(map->glyphs[mapch].vxmin); av[ 1] = y + h * PIXEL_Y(map->glyphs[mapch].vymin); av[ 2] = 10;
1056                         av[ 3] = x + w * PIXEL_X(map->glyphs[mapch].vxmax); av[ 4] = y + h * PIXEL_Y(map->glyphs[mapch].vymin); av[ 5] = 10;
1057                         av[ 6] = x + w * PIXEL_X(map->glyphs[mapch].vxmax); av[ 7] = y + h * PIXEL_Y(map->glyphs[mapch].vymax); av[ 8] = 10;
1058                         av[ 9] = x + w * PIXEL_X(map->glyphs[mapch].vxmin); av[10] = y + h * PIXEL_Y(map->glyphs[mapch].vymax); av[11] = 10;
1059
1060                         x += PIXEL_X(thisw * w);
1061                         ac += 16;
1062                         at += 8;
1063                         av += 12;
1064                         batchcount++;
1065                         if (batchcount >= QUADELEMENTS_MAXQUADS)
1066                         {
1067                                 GL_LockArrays(0, batchcount * 4);
1068                                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1069                                 GL_LockArrays(0, 0);
1070                                 batchcount = 0;
1071                                 ac = color4f;
1072                                 at = texcoord2f;
1073                                 av = vertex3f;
1074                         }
1075                         /*
1076                         GL_LockArrays(0, 4);
1077                         R_Mesh_Draw(0, 4, 0, 2, NULL, quadelements, 0, 0);
1078                         GL_LockArrays(0, 0);
1079                         */
1080
1081                         prevmap = map;
1082                 }
1083         }
1084         if (batchcount > 0)
1085         {
1086                 GL_LockArrays(0, batchcount * 4);
1087                 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1088                 GL_LockArrays(0, 0);
1089         }
1090
1091         if (outcolor)
1092                 *outcolor = colorindex;
1093
1094         // note: this relies on the proper text (not shadow) being drawn last
1095         return x;
1096 }
1097
1098 float Font_DrawString(float startx, float starty,
1099                       const char *text, size_t maxlen,
1100                       float width, float height,
1101                       float basered, float basegreen, float baseblue, float basealpha,
1102                       int flags, int *outcolor, qboolean ignorecolorcodes)
1103 {
1104         return Font_DrawString_Font(startx, starty, text, maxlen,
1105                                     width, height,
1106                                     basered, basegreen, baseblue, basealpha,
1107                                     flags, outcolor, ignorecolorcodes,
1108                                     &test_font);
1109 }
1110