1 /* FreeType 2 and UTF-8 encoding support for
11 ================================================================================
12 Function definitions. Taken from the freetype2 headers.
13 ================================================================================
18 (*qFT_Init_FreeType)( FT_Library *alibrary );
20 (*qFT_Done_FreeType)( FT_Library library );
22 (*qFT_New_Face)( FT_Library library,
23 const char* filepathname,
27 (*qFT_New_Memory_Face)( FT_Library library,
28 const FT_Byte* file_base,
33 (*qFT_Done_Face)( FT_Face face );
35 (*qFT_Select_Size)( FT_Face face,
36 FT_Int strike_index );
38 (*qFT_Request_Size)( FT_Face face,
39 FT_Size_Request req );
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 );
47 (*qFT_Set_Pixel_Sizes)( FT_Face face,
49 FT_UInt pixel_height );
51 (*qFT_Load_Glyph)( FT_Face face,
53 FT_Int32 load_flags );
55 (*qFT_Load_Char)( FT_Face face,
57 FT_Int32 load_flags );
59 (*qFT_Get_Char_Index)( FT_Face face,
62 (*qFT_Render_Glyph)( FT_GlyphSlot slot,
63 FT_Render_Mode render_mode );
65 (*qFT_Get_Kerning)( FT_Face face,
69 FT_Vector *akerning );
72 ================================================================================
73 Support for dynamically loading the FreeType2 library
74 ================================================================================
77 static dllfunction_t ft2funcs[] =
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},
96 /// Handle for FreeType2 DLL
97 static dllhandle_t ft2_dll = NULL;
99 /// Memory pool for fonts
100 static mempool_t *font_mempool= NULL;
101 static rtexturepool_t *font_texturepool = NULL;
103 /// FreeType library handle
104 static FT_Library font_ft2lib = NULL;
110 Unload the FreeType2 DLL
113 void Font_CloseLibrary (void)
116 Mem_FreePool(&font_mempool);
117 if (font_texturepool)
118 R_FreeTexturePool(&font_texturepool);
119 if (font_ft2lib && qFT_Done_FreeType)
121 qFT_Done_FreeType(font_ft2lib);
124 Sys_UnloadLibrary (&ft2_dll);
131 Try to load the FreeType2 DLL
134 qboolean Font_OpenLibrary (void)
136 const char* dllnames [] =
139 #error path for freetype 2 dll
141 #error path for freetype 2 dll
142 #elif defined(MACOSX)
156 if (!Sys_LoadLibrary (dllnames, &ft2_dll, ft2funcs))
165 Initialize the freetype2 font subsystem
169 static ft2_font_t test_font;
171 void font_start(void)
173 if (!Font_OpenLibrary())
176 if (qFT_Init_FreeType(&font_ft2lib))
178 Con_Print("ERROR: Failed to initialize the FreeType2 library!\n");
183 font_mempool = Mem_AllocPool("FONT", 0, NULL);
186 Con_Print("ERROR: Failed to allocate FONT memory pool!\n");
191 font_texturepool = R_AllocTexturePool();
192 if (!font_texturepool)
194 Con_Print("ERROR: Failed to allocate FONT texture pool!\n");
199 if (!Font_LoadFont("gfx/test", 16, &test_font))
201 Con_Print("ERROR: Failed to load test font!\n");
207 void font_shutdown(void)
212 void font_newmap(void)
218 // dummy this for now
219 // R_RegisterModule("Font_FreeType2", font_start, font_shutdown, font_newmap);
223 ================================================================================
224 UTF-8 encoding and decoding functions follow.
225 ================================================================================
228 /** Get the number of characters in in an UTF-8 string.
229 * @param _s An utf-8 encoded null-terminated string.
230 * @return The number of unicode characters in the string.
232 size_t u8_strlen(const char *_s)
235 unsigned char *s = (unsigned char*)_s;
246 // start of a wide character
250 for (++s; *s >= 0x80 && *s <= 0xC0; ++s);
254 // part of a wide character, we ignore that one
255 if (*s <= 0xBF) // 10111111
264 /** Fetch a character from an utf-8 encoded string.
265 * @param _s The start of an utf-8 encoded multi-byte character.
266 * @param _end Will point to after the first multi-byte character.
267 * @return The 32-bit integer representation of the first multi-byte character.
269 Uchar u8_getchar(const char *_s, const char **_end)
271 const unsigned char *s = (unsigned char*)_s;
285 // starting within a wide character - skip it and retrieve the one after it
286 for (++s; *s >= 0x80 && *s < 0xC0; ++s);
287 // or we could return '?' here?
289 // for a little speedup:
290 if ( (*s & 0xE0) == 0xC0 )
293 u = ( (s[0] & 0x1F) << 6 ) | (s[1] & 0x3F);
298 if ( (*s & 0xF0) == 0xE0 )
301 u = ( (s[0] & 0x0F) << 12 ) | ( (s[1] & 0x3F) << 6 ) | (s[2] & 0x3F);
310 for (mask >>= 1; v > (*s & mask); mask >>= 1)
312 u = (Uchar)(*s & mask);
313 for (++s; *s >= 0x80 && *s < 0xC0; ++s)
314 u = (u << 6) | (*s & 0x3F);
317 *_end = (const char*)s;
322 /** Encode a wide-character into utf-8.
323 * @param w The wide character to encode.
324 * @param to The target buffer the utf-8 encoded string is stored to.
325 * @param maxlen The maximum number of bytes that fit into the target buffer.
326 * @return Number of bytes written to the buffer, or less or equal to 0 if the buffer is too small.
328 int u8_fromchar(Uchar w, char *to, size_t maxlen)
345 // for a little speedup
354 to[1] = 0x80 | (w & 0x3F); w >>= 6;
366 to[2] = 0x80 | (w & 0x3F); w >>= 6;
367 to[1] = 0x80 | (w & 0x3F); w >>= 6;
372 // "more general" version:
374 // check how much space we need and store data into a
375 // temp buffer - this is faster than recalculating again
380 tmp[i++] = 0x80 | (w & 0x3F);
381 bt = (bt >> 1) | 0x80;
383 // see if we still fit into the target buffer
384 if (i+1 >= maxlen) // +1 for the \0
387 // there are no characters which take up that much space yet
388 // and there won't be for the next many many years, still... let's be safe
389 if (i >= sizeof(tmp))
393 for (j = 0; j < i; ++j)
402 /** Convert a utf-8 multibyte string to a wide character string.
403 * @param wcs The target wide-character buffer.
404 * @param mb The utf-8 encoded multibyte string to convert.
405 * @param maxlen The maximum number of wide-characters that fit into the target buffer.
406 * @return The number of characters written to the target buffer.
408 size_t u8_mbstowcs(Uchar *wcs, const char *mb, size_t maxlen)
411 for (i = 0; *mb && i < maxlen; ++i)
412 *wcs++ = u8_getchar(mb, &mb);
418 /** Convert a wide-character string to a utf-8 multibyte string.
419 * @param mb The target buffer the utf-8 string is written to.
420 * @param wcs The wide-character string to convert.
421 * @param maxlen The number bytes that fit into the multibyte target buffer.
422 * @return The number of bytes written, not including the terminating \0
424 size_t u8_wcstombs(char *mb, const Uchar *wcs, size_t maxlen)
427 const char *start = mb;
428 for (i = 0; *wcs && i < maxlen; ++i)
431 if ( (len = u8_fromchar(*wcs++, mb, maxlen - i)) < 0)
441 ================================================================================
442 Implementation of a more or less lazy font loading and rendering code.
443 ================================================================================
446 #include "ft2_fontdefs.h"
448 ft2_font_t *Font_Alloc(void)
450 return Mem_Alloc(font_mempool, sizeof(ft2_font_t));
453 qboolean Font_LoadFont(const char *name, int size, ft2_font_t *font)
456 char filename[PATH_MAX];
459 memset(font, 0, sizeof(*font));
461 if (!Font_OpenLibrary())
463 Con_Printf("WARNING: can't open load font %s\n"
464 "You need the FreeType2 DLL to load font files\n",
469 namelen = strlen(name);
471 memcpy(filename, name, namelen);
472 memcpy(filename + namelen, ".ttf", 5);
474 font->data = FS_LoadFile(filename, font_mempool, false, &font->datasize);
477 // FS_LoadFile being not-quiet should print an error :)
478 Con_Printf("Failed to load TTF version of font %s\n", name);
481 Con_Printf("Loading font %s face 0 size %i...\n", filename, size);
483 status = qFT_New_Memory_Face(font_ft2lib, (FT_Bytes)font->data, font->datasize, 0, (FT_Face*)&font->face);
486 Con_Printf("ERROR: can't create face for %s\n"
487 "Error %i\n", // TODO: error strings
489 Mem_Free(font->data);
493 memcpy(font->name, name, namelen+1);
495 font->glyphSize = font->size * 2;
496 font->has_kerning = !!(((FT_Face)(font->face))->face_flags & FT_FACE_FLAG_KERNING);
498 status = qFT_Set_Pixel_Sizes((FT_Face)font->face, size, size);
501 Con_Printf("ERROR: can't size pixel sizes for face of font %s\n"
502 "Error %i\n", // TODO: error strings
504 Mem_Free(font->data);
508 if (!Font_LoadMapForIndex(font, 0, NULL))
510 Con_Printf("ERROR: can't load the first character map for %s\n"
513 Mem_Free(font->data);
520 void Font_UnloadFont(ft2_font_t *font)
523 Mem_Free(font->data);
528 qFT_Done_Face((FT_Face)font->face);
534 qboolean Font_LoadMapForIndex(ft2_font_t *font, Uchar _ch, ft2_font_map_t **outmap)
536 char map_identifier[PATH_MAX];
537 unsigned long mapidx = _ch / FONT_CHARS_PER_MAP;
543 FT_Face face = font->face;
546 int gR, gC; // glyph position: row and column
550 int bytesPerPixel = 4; // change the conversion loop too if you change this!
555 map = Mem_Alloc(font_mempool, sizeof(ft2_font_map_t));
558 Con_Printf("ERROR: Out of memory when loading fontmap for %s\n", font->name);
562 pitch = font->glyphSize * FONT_CHARS_PER_LINE * bytesPerPixel;
563 data = Mem_Alloc(font_mempool, (FONT_CHAR_LINES * font->glyphSize) * pitch);
567 Con_Printf("ERROR: Failed to allocate memory for glyph data for font %s\n", font->name);
571 // initialize as white texture with zero alpha
573 while (tp < (FONT_CHAR_LINES * font->glyphSize) * pitch)
581 map->start = mapidx * FONT_CHARS_PER_MAP;
583 font->font_map = map;
586 // insert the map at the right place
587 ft2_font_map_t *next = font->font_map;
588 while(next->next && next->next->start < map->start)
590 map->next = next->next;
596 for (ch = map->start;
597 ch < (FT_ULong)map->start + FONT_CHARS_PER_MAP;
604 unsigned char *imagedata, *dst, *src;
605 glyph_slot_t *mapglyph;
607 if (developer.integer)
608 Con_Print("------------- GLYPH INFO -----------------\n");
611 if (gC >= FONT_CHARS_PER_LINE)
613 gC -= FONT_CHARS_PER_LINE;
617 imagedata = data + gR * pitch * font->glyphSize + gC * font->glyphSize * bytesPerPixel;
618 glyphIndex = qFT_Get_Char_Index(face, ch);
620 status = qFT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT);
623 Con_Printf("failed to load glyph %lu for %s\n", glyphIndex, font->name);
627 status = qFT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
630 Con_Printf("failed to render glyph %lu for %s\n", glyphIndex, font->name);
635 bmp = &glyph->bitmap;
640 if (w > font->glyphSize || h > font->glyphSize)
641 Con_Printf("WARNING: Glyph %lu is too big in font %s\n", ch, font->name);
643 switch (bmp->pixel_mode)
645 case FT_PIXEL_MODE_MONO:
646 if (developer.integer)
647 Con_Print(" Pixel Mode: MONO\n");
649 case FT_PIXEL_MODE_GRAY2:
650 if (developer.integer)
651 Con_Print(" Pixel Mode: GRAY2\n");
653 case FT_PIXEL_MODE_GRAY4:
654 if (developer.integer)
655 Con_Print(" Pixel Mode: GRAY4\n");
657 case FT_PIXEL_MODE_GRAY:
658 if (developer.integer)
659 Con_Print(" Pixel Mode: GRAY\n");
662 if (developer.integer)
663 Con_Printf(" Pixel Mode: Unknown: %i\n", bmp->pixel_mode);
667 for (y = 0; y < h; ++y)
669 dst = imagedata + y * pitch;
670 src = bmp->buffer + y * bmp->pitch;
672 switch (bmp->pixel_mode)
674 case FT_PIXEL_MODE_MONO:
675 for (x = 0; x < bmp->width; x += 8)
677 unsigned char ch = *src++;
678 dst += 3; // shift to alpha byte
679 *dst = 255 * ((ch & 0x80) >> 7); dst += 4;
680 *dst = 255 * ((ch & 0x40) >> 6); dst += 4;
681 *dst = 255 * ((ch & 0x20) >> 5); dst += 4;
682 *dst = 255 * ((ch & 0x10) >> 4); dst += 4;
683 *dst = 255 * ((ch & 0x08) >> 3); dst += 4;
684 *dst = 255 * ((ch & 0x04) >> 2); dst += 4;
685 *dst = 255 * ((ch & 0x02) >> 1); dst += 4;
686 *dst = 255 * ((ch & 0x01) >> 0); dst++; // compensate the first += 3
689 case FT_PIXEL_MODE_GRAY2:
690 for (x = 0; x < bmp->width; x += 4)
692 unsigned char ch = *src++;
693 dst += 3; // shift to alpha byte
694 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
695 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
696 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst += 4;
697 *dst = ( ((ch & 0xA0) >> 6) * 0x55 ); ch <<= 2; dst++; // compensate the +=3
700 case FT_PIXEL_MODE_GRAY4:
701 for (x = 0; x < bmp->width; x += 2)
703 unsigned char ch = *src++;
704 dst += 3; // shift to alpha byte
705 *dst = ( ((ch & 0xF0) >> 4) * 0x24); dst += 4;
706 *dst = ( ((ch & 0x0F) ) * 0x24); dst++; // compensate the += 3
709 case FT_PIXEL_MODE_GRAY:
710 // in this case pitch should equal width
711 for (tp = 0; tp < bmp->pitch; ++tp)
712 dst[3 + tp*4] = src[tp]; // copy the grey value into the alpha bytes
714 //memcpy((void*)dst, (void*)src, bmp->pitch);
722 // now fill map->glyphs[ch - map->start]
723 mapch = ch - map->start;
724 mapglyph = &map->glyphs[mapch];
727 double sfx = (1.0/64.0) / (double)font->size;
728 double sfy = (1.0/64.0) / (double)font->size;
729 double bearingX = (double)glyph->metrics.horiBearingX * sfx;
730 double bearingY = (double)glyph->metrics.horiBearingY * sfy;
731 double advance = (double)glyph->metrics.horiAdvance * sfx;
732 double mWidth = (double)glyph->metrics.width * sfx;
733 double mHeight = (double)glyph->metrics.height * sfy;
734 //double tWidth = bmp->width / (double)font->size;
735 //double tHeight = bmp->rows / (double)font->size;
737 mapglyph->vxmin = bearingX;
738 mapglyph->vxmax = bearingX + mWidth;
739 mapglyph->vymin = -bearingY;
740 mapglyph->vymax = mHeight - bearingY;
741 mapglyph->txmin = ( (double)(gC * font->glyphSize) ) / ( (double)(font->glyphSize * FONT_CHARS_PER_LINE) );
742 mapglyph->txmax = mapglyph->txmin + (double)bmp->width / ( (double)(font->glyphSize * FONT_CHARS_PER_LINE) );
743 mapglyph->tymin = ( (double)(gR * font->glyphSize) ) / ( (double)(font->glyphSize * FONT_CHAR_LINES) );
744 mapglyph->tymax = mapglyph->tymin + (double)bmp->rows / ( (double)(font->glyphSize * FONT_CHAR_LINES) );
745 //mapglyph->advance_x = advance * font->size;
746 mapglyph->advance_x = advance;
747 mapglyph->advance_y = 0;
749 if (developer.integer)
751 Con_Printf(" Glyph: %lu at (%i, %i)\n", (unsigned long)ch, gC, gR);
752 if (ch >= 32 && ch <= 128)
753 Con_Printf(" Character: %c\n", (int)ch);
754 Con_Printf(" Vertex info:\n");
755 Con_Printf(" X: ( %f -- %f )\n", mapglyph->vxmin, mapglyph->vxmax);
756 Con_Printf(" Y: ( %f -- %f )\n", mapglyph->vymin, mapglyph->vymax);
757 Con_Printf(" Texture info:\n");
758 Con_Printf(" S: ( %f -- %f )\n", mapglyph->txmin, mapglyph->txmax);
759 Con_Printf(" T: ( %f -- %f )\n", mapglyph->tymin, mapglyph->tymax);
760 Con_Printf(" Advance: %f, %f\n", mapglyph->advance_x, mapglyph->advance_y);
765 // create a texture from the data now
767 if (developer.integer)
769 // view using `display -depth 8 -size 512x512 name_page.rgba` (be sure to use a correct -size parameter)
770 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u.rgba", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
771 FS_WriteFile(map_identifier, data, pitch * FONT_CHAR_LINES * font->glyphSize);
773 dpsnprintf(map_identifier, sizeof(map_identifier), "%s_%u", font->name, (unsigned)map->start/FONT_CHARS_PER_MAP);
774 map->texture = R_LoadTexture2D(font_texturepool, map_identifier,
775 font->glyphSize * FONT_CHARS_PER_LINE,
776 font->glyphSize * FONT_CHAR_LINES,
777 data, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_ALWAYSPRECACHE/* | TEXF_MIPMAP*/, NULL);
781 // if the first try isn't successful, keep it with a broken texture
782 // otherwise we retry to load it every single frame where ft2 rendering is used
783 // this would be bad...
784 // only `data' must be freed
794 extern void _DrawQ_Setup(void);
796 // TODO: If no additional stuff ends up in the following static functions
797 // use the DrawQ ones!
798 static void _Font_ProcessDrawFlag(int flags)
802 if(flags == DRAWFLAG_ADDITIVE)
803 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
804 else if(flags == DRAWFLAG_MODULATE)
805 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
806 else if(flags == DRAWFLAG_2XMODULATE)
807 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
808 else if(flags == DRAWFLAG_SCREEN)
809 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
811 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
814 extern cvar_t r_textcontrast, r_textbrightness, r_textshadow;
815 static const vec4_t string_colors[] =
818 // LordHavoc: why on earth is cyan before magenta in Quake3?
819 // LordHavoc: note: Doom3 uses white for [0] and [7]
820 {0.0, 0.0, 0.0, 1.0}, // black
821 {1.0, 0.0, 0.0, 1.0}, // red
822 {0.0, 1.0, 0.0, 1.0}, // green
823 {1.0, 1.0, 0.0, 1.0}, // yellow
824 {0.0, 0.0, 1.0, 1.0}, // blue
825 {0.0, 1.0, 1.0, 1.0}, // cyan
826 {1.0, 0.0, 1.0, 1.0}, // magenta
827 {1.0, 1.0, 1.0, 1.0}, // white
828 // [515]'s BX_COLOREDTEXT extension
829 {1.0, 1.0, 1.0, 0.5}, // half transparent
830 {0.5, 0.5, 0.5, 1.0} // half brightness
831 // Black's color table
832 //{1.0, 1.0, 1.0, 1.0},
833 //{1.0, 0.0, 0.0, 1.0},
834 //{0.0, 1.0, 0.0, 1.0},
835 //{0.0, 0.0, 1.0, 1.0},
836 //{1.0, 1.0, 0.0, 1.0},
837 //{0.0, 1.0, 1.0, 1.0},
838 //{1.0, 0.0, 1.0, 1.0},
839 //{0.1, 0.1, 0.1, 1.0}
842 static void Font_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
844 float C = r_textcontrast.value;
845 float B = r_textbrightness.value;
846 if (colorindex & 0x10000) // that bit means RGB color
848 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
849 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
850 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
851 color[3] = (colorindex & 0xf) / 15.0;
854 Vector4Copy(string_colors[colorindex], color);
855 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
858 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
859 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
863 float Font_DrawString_Font(float startx, float starty,
864 const char *text, size_t maxlen,
866 float basered, float basegreen, float baseblue, float basealpha,
867 int flags, int *outcolor, qboolean ignorecolorcodes,
870 int shadow, colorindex = STRING_COLOR_DEFAULT;
871 float x = startx, y, thisw;
875 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
876 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
877 float color4f[QUADELEMENTS_MAXQUADS*4*4];
880 const char *text_start = text;
882 ft2_font_map_t *prevmap = NULL;
888 _Font_ProcessDrawFlag(flags);
890 R_Mesh_ColorPointer(color4f, 0, 0);
891 R_Mesh_ResetTextureState();
892 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
893 R_Mesh_VertexPointer(vertex3f, 0, 0);
894 R_SetupGenericShader(true);
901 // We render onto the baseline, so move down by the intended height.
902 // Otherwise the text appears too high since the top edge would be the base line.
903 starty += (double)h * (5.0/6.0); // don't use the complete height
904 // with sane fonts it should be possible to use the font's `ascent` value
905 // but then again, is it safe?
907 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
910 if (!outcolor || *outcolor == -1)
911 colorindex = STRING_COLOR_DEFAULT;
913 colorindex = *outcolor;
915 Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
921 x += r_textshadow.value;
922 y += r_textshadow.value;
924 for (i = 0;i < maxlen && *text;i++)
926 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
931 ch = *text; // the color tag is an ASCII character!
932 if (ch == STRING_COLOR_RGB_TAG_CHAR)
934 // we need some preparation here
937 if (*text) chx[0] = u8_getchar(text, &text);
938 if (*text) chx[1] = u8_getchar(text, &text);
939 if (*text) chx[2] = u8_getchar(text, &text);
940 if ( ( (chx[0] >= 'A' && chx[0] <= 'F') || (chx[0] >= 'a' && chx[0] <= 'f') || (chx[0] >= '0' && chx[0] <= '9') ) &&
941 ( (chx[1] >= 'A' && chx[1] <= 'F') || (chx[1] >= 'a' && chx[1] <= 'f') || (chx[1] >= '0' && chx[1] <= '9') ) &&
942 ( (chx[2] >= 'A' && chx[2] <= 'F') || (chx[2] >= 'a' && chx[2] <= 'f') || (chx[2] >= '0' && chx[2] <= '9') ) )
947 text = before; // start from the first hex character
949 if (ch <= '9' && ch >= '0') // ^[0-9] found
951 colorindex = ch - '0';
952 Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
955 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < maxlen && chx[2]) // ^x found
957 // building colorindex...
958 ch = tolower(text[i+1]);
959 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
960 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
961 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
962 else tempcolorindex = 0;
965 ch = tolower(text[i+2]);
966 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
967 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
968 else tempcolorindex = 0;
971 ch = tolower(text[i+3]);
972 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
973 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
974 else tempcolorindex = 0;
977 colorindex = tempcolorindex | 0xf;
978 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
979 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
980 Font_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
987 else if (ch == STRING_COLOR_TAG)
991 ch = u8_getchar(text, &text);
994 while(map && map->start + FONT_CHARS_PER_MAP < ch)
998 if (!Font_LoadMapForIndex(fnt, ch, &map))
1005 // this shouldn't happen
1011 if (map != prevmap && batchcount)
1013 // we need a different character map, render what we currently have:
1014 GL_LockArrays(0, batchcount * 4);
1015 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1016 GL_LockArrays(0, 0);
1023 // TODO: don't call Mesh_Draw all the time
1024 // call it when the texture changes or the batchcount hits the limit
1026 R_Mesh_TexBind(0, R_GetTexture(map->texture));
1027 R_SetupGenericShader(true);
1029 mapch = ch - map->start;
1030 thisw = map->glyphs[mapch].advance_x;
1032 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1033 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1034 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1035 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1036 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1037 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1038 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1039 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1040 #define PIXEL_X(x) ( (x)/vid_conwidth.value * vid.width )
1041 #define PIXEL_Y(x) ( (x)/vid_conheight.value * vid.height )
1042 av[ 0] = x + w * PIXEL_X(map->glyphs[mapch].vxmin); av[ 1] = y + h * PIXEL_Y(map->glyphs[mapch].vymin); av[ 2] = 10;
1043 av[ 3] = x + w * PIXEL_X(map->glyphs[mapch].vxmax); av[ 4] = y + h * PIXEL_Y(map->glyphs[mapch].vymin); av[ 5] = 10;
1044 av[ 6] = x + w * PIXEL_X(map->glyphs[mapch].vxmax); av[ 7] = y + h * PIXEL_Y(map->glyphs[mapch].vymax); av[ 8] = 10;
1045 av[ 9] = x + w * PIXEL_X(map->glyphs[mapch].vxmin); av[10] = y + h * PIXEL_Y(map->glyphs[mapch].vymax); av[11] = 10;
1047 x += PIXEL_X(thisw * w);
1052 if (batchcount >= QUADELEMENTS_MAXQUADS)
1054 GL_LockArrays(0, batchcount * 4);
1055 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1056 GL_LockArrays(0, 0);
1063 GL_LockArrays(0, 4);
1064 R_Mesh_Draw(0, 4, 0, 2, NULL, quadelements, 0, 0);
1065 GL_LockArrays(0, 0);
1073 GL_LockArrays(0, batchcount * 4);
1074 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1075 GL_LockArrays(0, 0);
1079 *outcolor = colorindex;
1081 // note: this relies on the proper text (not shadow) being drawn last
1085 float Font_DrawString(float startx, float starty,
1086 const char *text, size_t maxlen,
1087 float width, float height,
1088 float basered, float basegreen, float baseblue, float basealpha,
1089 int flags, int *outcolor, qboolean ignorecolorcodes)
1091 return Font_DrawString_Font(startx, starty, text, maxlen,
1093 basered, basegreen, baseblue, basealpha,
1094 flags, outcolor, ignorecolorcodes,