Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

tr_font.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // tr_font.c
00023 // 
00024 //
00025 // The font system uses FreeType 2.x to render TrueType fonts for use within the game.
00026 // As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and 
00027 // about 90% of the cgame presentation. A few areas of the CGAME were left uses the old 
00028 // fonts since the code is shared with standard Q3A.
00029 //
00030 // If you include this font rendering code in a commercial product you MUST include the
00031 // following somewhere with your product, see www.freetype.org for specifics or changes.
00032 // The Freetype code also uses some hinting techniques that MIGHT infringe on patents 
00033 // held by apple so be aware of that also.
00034 //
00035 // As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code
00036 // disabled. This removes any potential patent issues and it keeps us from having to 
00037 // distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require
00038 // an act of god to accomplish. 
00039 //
00040 // What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType
00041 // credit in the credits ) and then saved off the glyph data and then hand touched up the 
00042 // font bitmaps so they scale a bit better in GL.
00043 //
00044 // There are limitations in the way fonts are saved and reloaded in that it is based on 
00045 // point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point
00046 // you will end up with a single 18 point data file and image set. Typically you will want to 
00047 // choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system
00048 // 
00049 // In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we
00050 // use three or four scales, most of them exactly equaling the specific rendered size. We 
00051 // rendered three sizes in Team Arena, 12, 16, and 20. 
00052 //
00053 // To generate new font data you need to go through the following steps.
00054 // 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path.
00055 // 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and 
00056 //    point size. the original TrueType fonts must exist in fonts at this point.
00057 // 3. run the game, you should see things normally.
00058 // 4. Exit the game and there will be three dat files and at least three tga files. The 
00059 //    tga's are in 256x256 pages so if it takes three images to render a 24 point font you 
00060 //    will end up with fontImage_0_24.tga through fontImage_2_24.tga
00061 // 5. You will need to flip the tga's in Photoshop as the tga output code writes them upside
00062 //    down.
00063 // 6. In future runs of the game, the system looks for these images and data files when a s
00064 //    specific point sized font is rendered and loads them for use. 
00065 // 7. Because of the original beta nature of the FreeType code you will probably want to hand
00066 //    touch the font bitmaps.
00067 // 
00068 // Currently a define in the project turns on or off the FreeType code which is currently 
00069 // defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and 
00070 // uncheck the exclude from build check box in the FreeType2 area of the Renderer project. 
00071 
00072 
00073 #include "tr_local.h"
00074 #include "../qcommon/qcommon.h"
00075 
00076 #ifdef BUILD_FREETYPE
00077 #include "../ft2/fterrors.h"
00078 #include "../ft2/ftsystem.h"
00079 #include "../ft2/ftimage.h"
00080 #include "../ft2/freetype.h"
00081 #include "../ft2/ftoutln.h"
00082 
00083 #define _FLOOR(x)  ((x) & -64)
00084 #define _CEIL(x)   (((x)+63) & -64)
00085 #define _TRUNC(x)  ((x) >> 6)
00086 
00087 FT_Library ftLibrary = NULL;  
00088 #endif
00089 
00090 #define MAX_FONTS 6
00091 static int registeredFontCount = 0;
00092 static fontInfo_t registeredFont[MAX_FONTS];
00093 
00094 #ifdef BUILD_FREETYPE
00095 void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) {
00096 
00097   *left  = _FLOOR( glyph->metrics.horiBearingX );
00098   *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width );
00099   *width = _TRUNC(*right - *left);
00100     
00101   *top    = _CEIL( glyph->metrics.horiBearingY );
00102   *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height );
00103   *height = _TRUNC( *top - *bottom );
00104   *pitch  = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 );
00105 }
00106 
00107 
00108 FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) {
00109 
00110   FT_Bitmap  *bit2;
00111   int left, right, width, top, bottom, height, pitch, size;
00112 
00113   R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch);
00114 
00115   if ( glyph->format == ft_glyph_format_outline ) {
00116     size   = pitch*height; 
00117 
00118     bit2 = Z_Malloc(sizeof(FT_Bitmap));
00119 
00120     bit2->width      = width;
00121     bit2->rows       = height;
00122     bit2->pitch      = pitch;
00123     bit2->pixel_mode = ft_pixel_mode_grays;
00124     //bit2->pixel_mode = ft_pixel_mode_mono;
00125     bit2->buffer     = Z_Malloc(pitch*height);
00126     bit2->num_grays = 256;
00127 
00128     Com_Memset( bit2->buffer, 0, size );
00129 
00130     FT_Outline_Translate( &glyph->outline, -left, -bottom );
00131 
00132     FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 );
00133 
00134     glyphOut->height = height;
00135     glyphOut->pitch = pitch;
00136     glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1;
00137     glyphOut->bottom = bottom;
00138     
00139     return bit2;
00140   }
00141   else {
00142     ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n");
00143   }
00144   return NULL;
00145 }
00146 
00147 void WriteTGA (char *filename, byte *data, int width, int height) {
00148     byte    *buffer;
00149     int     i, c;
00150 
00151     buffer = Z_Malloc(width*height*4 + 18);
00152     Com_Memset (buffer, 0, 18);
00153     buffer[2] = 2;      // uncompressed type
00154     buffer[12] = width&255;
00155     buffer[13] = width>>8;
00156     buffer[14] = height&255;
00157     buffer[15] = height>>8;
00158     buffer[16] = 32;    // pixel size
00159 
00160     // swap rgb to bgr
00161     c = 18 + width * height * 4;
00162     for (i=18 ; i<c ; i+=4)
00163     {
00164         buffer[i] = data[i-18+2];       // blue
00165         buffer[i+1] = data[i-18+1];     // green
00166         buffer[i+2] = data[i-18+0];     // red
00167         buffer[i+3] = data[i-18+3];     // alpha
00168     }
00169 
00170     ri.FS_WriteFile(filename, buffer, c);
00171 
00172     //f = fopen (filename, "wb");
00173     //fwrite (buffer, 1, c, f);
00174     //fclose (f);
00175 
00176     Z_Free (buffer);
00177 }
00178 
00179 static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, int *yOut, int *maxHeight, FT_Face face, const unsigned char c, qboolean calcHeight) {
00180   int i;
00181   static glyphInfo_t glyph;
00182   unsigned char *src, *dst;
00183   float scaled_width, scaled_height;
00184   FT_Bitmap *bitmap = NULL;
00185 
00186   Com_Memset(&glyph, 0, sizeof(glyphInfo_t));
00187   // make sure everything is here
00188   if (face != NULL) {
00189     FT_Load_Glyph(face, FT_Get_Char_Index( face, c), FT_LOAD_DEFAULT );
00190     bitmap = R_RenderGlyph(face->glyph, &glyph);
00191     if (bitmap) {
00192       glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1;
00193     } else {
00194       return &glyph;
00195     }
00196 
00197     if (glyph.height > *maxHeight) {
00198       *maxHeight = glyph.height;
00199     }
00200 
00201     if (calcHeight) {
00202       Z_Free(bitmap->buffer);
00203       Z_Free(bitmap);
00204       return &glyph;
00205     }
00206 
00207 /*
00208     // need to convert to power of 2 sizes so we do not get 
00209     // any scaling from the gl upload
00210     for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1)
00211         ;
00212     for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1)
00213         ;
00214 */
00215 
00216     scaled_width = glyph.pitch;
00217     scaled_height = glyph.height;
00218 
00219     // we need to make sure we fit
00220     if (*xOut + scaled_width + 1 >= 255) {
00221       if (*yOut + *maxHeight + 1 >= 255) {
00222         *yOut = -1;
00223         *xOut = -1;
00224         Z_Free(bitmap->buffer);
00225         Z_Free(bitmap);
00226         return &glyph;
00227       } else {
00228         *xOut = 0;
00229         *yOut += *maxHeight + 1;
00230       }
00231     } else if (*yOut + *maxHeight + 1 >= 255) {
00232       *yOut = -1;
00233       *xOut = -1;
00234       Z_Free(bitmap->buffer);
00235       Z_Free(bitmap);
00236       return &glyph;
00237     }
00238 
00239 
00240     src = bitmap->buffer;
00241     dst = imageOut + (*yOut * 256) + *xOut;
00242 
00243         if (bitmap->pixel_mode == ft_pixel_mode_mono) {
00244             for (i = 0; i < glyph.height; i++) {
00245                 int j;
00246                 unsigned char *_src = src;
00247                 unsigned char *_dst = dst;
00248                 unsigned char mask = 0x80;
00249                 unsigned char val = *_src;
00250                 for (j = 0; j < glyph.pitch; j++) {
00251                     if (mask == 0x80) {
00252                         val = *_src++;
00253                     }
00254                     if (val & mask) {
00255                         *_dst = 0xff;
00256                     }
00257                     mask >>= 1;
00258         
00259                     if ( mask == 0 ) {
00260                         mask = 0x80;
00261                     }
00262                     _dst++;
00263                 }
00264 
00265                 src += glyph.pitch;
00266                 dst += 256;
00267 
00268             }
00269         } else {
00270         for (i = 0; i < glyph.height; i++) {
00271             Com_Memcpy(dst, src, glyph.pitch);
00272               src += glyph.pitch;
00273                 dst += 256;
00274         }
00275         }
00276 
00277     // we now have an 8 bit per pixel grey scale bitmap 
00278     // that is width wide and pf->ftSize->metrics.y_ppem tall
00279 
00280     glyph.imageHeight = scaled_height;
00281     glyph.imageWidth = scaled_width;
00282     glyph.s = (float)*xOut / 256;
00283     glyph.t = (float)*yOut / 256;
00284     glyph.s2 = glyph.s + (float)scaled_width / 256;
00285     glyph.t2 = glyph.t + (float)scaled_height / 256;
00286 
00287     *xOut += scaled_width + 1;
00288   }
00289 
00290   Z_Free(bitmap->buffer);
00291   Z_Free(bitmap);
00292 
00293   return &glyph;
00294 }
00295 #endif
00296 
00297 static int fdOffset;
00298 static byte *fdFile;
00299 
00300 int readInt() {
00301     int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24);
00302     fdOffset += 4;
00303     return i;
00304 }
00305 
00306 typedef union {
00307     byte    fred[4];
00308     float   ffred;
00309 } poor;
00310 
00311 float readFloat() {
00312     poor    me;
00313 #if idppc
00314     me.fred[0] = fdFile[fdOffset+3];
00315     me.fred[1] = fdFile[fdOffset+2];
00316     me.fred[2] = fdFile[fdOffset+1];
00317     me.fred[3] = fdFile[fdOffset+0];
00318 #else
00319     me.fred[0] = fdFile[fdOffset+0];
00320     me.fred[1] = fdFile[fdOffset+1];
00321     me.fred[2] = fdFile[fdOffset+2];
00322     me.fred[3] = fdFile[fdOffset+3];
00323 #endif
00324     fdOffset += 4;
00325     return me.ffred;
00326 }
00327 
00328 void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) {
00329 #ifdef BUILD_FREETYPE
00330   FT_Face face;
00331   int j, k, xOut, yOut, lastStart, imageNumber;
00332   int scaledSize, newSize, maxHeight, left, satLevels;
00333   unsigned char *out, *imageBuff;
00334   glyphInfo_t *glyph;
00335   image_t *image;
00336   qhandle_t h;
00337     float max;
00338 #endif
00339   void *faceData;
00340     int i, len;
00341   char name[1024];
00342     float dpi = 72;                                         //
00343     float glyphScale =  72.0f / dpi;        // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 )
00344 
00345     if (pointSize <= 0) {
00346         pointSize = 12;
00347     }
00348     // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font
00349     glyphScale *= 48.0f / pointSize;
00350 
00351     // make sure the render thread is stopped
00352     R_SyncRenderThread();
00353 
00354   if (registeredFontCount >= MAX_FONTS) {
00355     ri.Printf(PRINT_ALL, "RE_RegisterFont: Too many fonts registered already.\n");
00356     return;
00357   }
00358 
00359     Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize);
00360     for (i = 0; i < registeredFontCount; i++) {
00361         if (Q_stricmp(name, registeredFont[i].name) == 0) {
00362             Com_Memcpy(font, &registeredFont[i], sizeof(fontInfo_t));
00363             return;
00364         }
00365     }
00366 
00367     len = ri.FS_ReadFile(name, NULL);
00368     if (len == sizeof(fontInfo_t)) {
00369         ri.FS_ReadFile(name, &faceData);
00370         fdOffset = 0;
00371         fdFile = faceData;
00372         for(i=0; i<GLYPHS_PER_FONT; i++) {
00373             font->glyphs[i].height      = readInt();
00374             font->glyphs[i].top         = readInt();
00375             font->glyphs[i].bottom      = readInt();
00376             font->glyphs[i].pitch       = readInt();
00377             font->glyphs[i].xSkip       = readInt();
00378             font->glyphs[i].imageWidth  = readInt();
00379             font->glyphs[i].imageHeight = readInt();
00380             font->glyphs[i].s           = readFloat();
00381             font->glyphs[i].t           = readFloat();
00382             font->glyphs[i].s2          = readFloat();
00383             font->glyphs[i].t2          = readFloat();
00384             font->glyphs[i].glyph       = readInt();
00385             Com_Memcpy(font->glyphs[i].shaderName, &fdFile[fdOffset], 32);
00386             fdOffset += 32;
00387         }
00388         font->glyphScale = readFloat();
00389         Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH);
00390 
00391 //      Com_Memcpy(font, faceData, sizeof(fontInfo_t));
00392         Q_strncpyz(font->name, name, sizeof(font->name));
00393         for (i = GLYPH_START; i < GLYPH_END; i++) {
00394             font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName);
00395         }
00396       Com_Memcpy(&registeredFont[registeredFontCount++], font, sizeof(fontInfo_t));
00397         return;
00398     }
00399 
00400 #ifndef BUILD_FREETYPE
00401     ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType code not available\n");
00402 #else
00403   if (ftLibrary == NULL) {
00404     ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType not initialized.\n");
00405     return;
00406   }
00407 
00408   len = ri.FS_ReadFile(fontName, &faceData);
00409   if (len <= 0) {
00410     ri.Printf(PRINT_ALL, "RE_RegisterFont: Unable to read font file\n");
00411     return;
00412   }
00413 
00414   // allocate on the stack first in case we fail
00415   if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) {
00416     ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType2, unable to allocate new face.\n");
00417     return;
00418   }
00419 
00420 
00421   if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) {
00422     ri.Printf(PRINT_ALL, "RE_RegisterFont: FreeType2, Unable to set face char size.\n");
00423     return;
00424   }
00425 
00426   //*font = &registeredFonts[registeredFontCount++];
00427 
00428   // make a 256x256 image buffer, once it is full, register it, clean it and keep going 
00429   // until all glyphs are rendered
00430 
00431   out = Z_Malloc(1024*1024);
00432   if (out == NULL) {
00433     ri.Printf(PRINT_ALL, "RE_RegisterFont: Z_Malloc failure during output image creation.\n");
00434     return;
00435   }
00436   Com_Memset(out, 0, 1024*1024);
00437 
00438   maxHeight = 0;
00439 
00440   for (i = GLYPH_START; i < GLYPH_END; i++) {
00441     glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue);
00442   }
00443 
00444   xOut = 0;
00445   yOut = 0;
00446   i = GLYPH_START;
00447   lastStart = i;
00448   imageNumber = 0;
00449 
00450   while ( i <= GLYPH_END ) {
00451 
00452     glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse);
00453 
00454     if (xOut == -1 || yOut == -1 || i == GLYPH_END)  {
00455       // ran out of room
00456       // we need to create an image from the bitmap, set all the handles in the glyphs to this point
00457       // 
00458 
00459       scaledSize = 256*256;
00460       newSize = scaledSize * 4;
00461       imageBuff = Z_Malloc(newSize);
00462       left = 0;
00463       max = 0;
00464       satLevels = 255;
00465       for ( k = 0; k < (scaledSize) ; k++ ) {
00466         if (max < out[k]) {
00467           max = out[k];
00468         }
00469       }
00470 
00471             if (max > 0) {
00472                 max = 255/max;
00473             }
00474 
00475       for ( k = 0; k < (scaledSize) ; k++ ) {
00476         imageBuff[left++] = 255;
00477         imageBuff[left++] = 255;
00478         imageBuff[left++] = 255;
00479 
00480         imageBuff[left++] = ((float)out[k] * max);
00481       }
00482 
00483             Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize);
00484             if (r_saveFontData->integer) { 
00485               WriteTGA(name, imageBuff, 256, 256);
00486             }
00487 
00488         //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize);
00489       image = R_CreateImage(name, imageBuff, 256, 256, qfalse, qfalse, GL_CLAMP);
00490       h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse);
00491       for (j = lastStart; j < i; j++) {
00492         font->glyphs[j].glyph = h;
00493                 Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName));
00494       }
00495       lastStart = i;
00496           Com_Memset(out, 0, 1024*1024);
00497       xOut = 0;
00498       yOut = 0;
00499       Z_Free(imageBuff);
00500             i++;
00501     } else {
00502       Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t));
00503       i++;
00504     }
00505   }
00506 
00507     registeredFont[registeredFontCount].glyphScale = glyphScale;
00508     font->glyphScale = glyphScale;
00509   Com_Memcpy(&registeredFont[registeredFontCount++], font, sizeof(fontInfo_t));
00510 
00511     if (r_saveFontData->integer) { 
00512         ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t));
00513     }
00514 
00515   Z_Free(out);
00516   
00517   ri.FS_FreeFile(faceData);
00518 #endif
00519 }
00520 
00521 
00522 
00523 void R_InitFreeType() {
00524 #ifdef BUILD_FREETYPE
00525   if (FT_Init_FreeType( &ftLibrary )) {
00526     ri.Printf(PRINT_ALL, "R_InitFreeType: Unable to initialize FreeType.\n");
00527   }
00528 #endif
00529   registeredFontCount = 0;
00530 }
00531 
00532 
00533 void R_DoneFreeType() {
00534 #ifdef BUILD_FREETYPE
00535   if (ftLibrary) {
00536     FT_Done_FreeType( ftLibrary );
00537     ftLibrary = NULL;
00538   }
00539 #endif
00540     registeredFontCount = 0;
00541 }
00542 

Generated on Thu Aug 25 12:37:51 2005 for Quake III Arena by  doxygen 1.3.9.1