llfont.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "llfont.h"
00035 
00036 // Freetype stuff
00037 #if !defined(LL_LINUX) || defined(LL_STANDALONE)
00038 # include <ft2build.h>
00039 #else
00040 // I had to do some work to avoid the system-installed FreeType headers... --ryan.
00041 # include "llfreetype2/freetype/ft2build.h"
00042 #endif
00043 
00044 // For some reason, this won't work if it's not wrapped in the ifdef
00045 #ifdef FT_FREETYPE_H
00046 #include FT_FREETYPE_H
00047 #endif
00048 
00049 #include "llerror.h"
00050 #include "llimage.h"
00051 //#include "llimagej2c.h"
00052 #include "llmath.h"     // Linden math
00053 #include "llstring.h"
00054 //#include "imdebug.h"
00055 
00056 FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;
00057 
00058 LLFontManager *gFontManagerp = NULL;
00059 
00060 FT_Library gFTLibrary = NULL;
00061 
00062 //static
00063 void LLFontManager::initClass()
00064 {
00065         gFontManagerp = new LLFontManager;
00066 }
00067 
00068 //static
00069 void LLFontManager::cleanupClass()
00070 {
00071         delete gFontManagerp;
00072         gFontManagerp = NULL;
00073 }
00074 
00075 LLFontManager::LLFontManager()
00076 {
00077         int error;
00078         error = FT_Init_FreeType(&gFTLibrary);
00079         if (error)
00080         {
00081                 // Clean up freetype libs.
00082                 llerrs << "Freetype initialization failure!" << llendl;
00083                 FT_Done_FreeType(gFTLibrary);
00084         }
00085 }
00086 
00087 
00088 LLFontManager::~LLFontManager()
00089 {
00090         FT_Done_FreeType(gFTLibrary);
00091 }
00092 
00093 
00094 LLFontGlyphInfo::LLFontGlyphInfo(U32 index)
00095 {
00096         mGlyphIndex = index;
00097         mXBitmapOffset = 0; // Offset to the origin in the bitmap
00098         mYBitmapOffset = 0; // Offset to the origin in the bitmap
00099         mXBearing = 0;          // Distance from baseline to left in pixels
00100         mYBearing = 0;          // Distance from baseline to top in pixels
00101         mWidth = 0;                     // In pixels
00102         mHeight = 0;            // In pixels
00103         mXAdvance = 0.f;        // In pixels
00104         mYAdvance = 0.f;        // In pixels
00105         mIsRendered = FALSE;
00106 }
00107 
00108 LLFontList::LLFontList()
00109 {
00110 }
00111 
00112 LLFontList::~LLFontList()
00113 {
00114         LLFontList::iterator iter;
00115         for(iter = this->begin(); iter != this->end(); iter++)
00116         {
00117                 delete *iter;
00118                 // The (now dangling) pointers in the vector will be cleaned up when the vector is deleted by the superclass destructor.
00119         }
00120 }
00121 void LLFontList::addAtEnd(LLFont *font)
00122 {
00123         // Purely a convenience function
00124         this->push_back(font);
00125 }
00126 
00127 LLFont::LLFont(LLImageRaw *imagep)
00128         : mRawImagep(imagep)
00129 {
00130         mValid = FALSE;
00131         mAscender = 0.f;
00132         mDescender = 0.f;
00133         mLineHeight = 0.f;
00134         mBitmapWidth = 0;
00135         mBitmapHeight = 0;
00136         mCurrentOffsetX = 1;
00137         mCurrentOffsetY = 1;
00138         mMaxCharWidth = 0;
00139         mMaxCharHeight = 0;
00140         mNumComponents = 0;
00141         mFallbackFontp = NULL;
00142         mIsFallback = FALSE;
00143         mFTFace = NULL;
00144 }
00145 
00146 
00147 LLFont::~LLFont()
00148 {
00149         mRawImagep = NULL; // dereferences or deletes image
00150 
00151         // Clean up freetype libs.
00152         if (mFTFace)
00153                 FT_Done_Face(mFTFace);
00154         mFTFace = NULL;
00155 
00156         // Delete glyph info
00157         std::for_each(mCharGlyphInfoMap.begin(), mCharGlyphInfoMap.end(), DeletePairedPointer());
00158 }
00159 
00160 void LLFont::setRawImage(LLImageRaw *imagep)
00161 {
00162         mRawImagep = imagep; // will delete old raw image if we have one and created it
00163 }
00164 
00165 // virtual
00166 F32 LLFont::getLineHeight() const
00167 {
00168         return mLineHeight;
00169 }
00170 
00171 // virtual
00172 F32 LLFont::getAscenderHeight() const
00173 {
00174         return mAscender;
00175 }
00176 
00177 // virtual
00178 F32 LLFont::getDescenderHeight() const
00179 {
00180         return mDescender;
00181 }
00182 
00183 BOOL LLFont::loadFace(const std::string& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback)
00184 {
00185         // Don't leak face objects.  This is also needed to deal with
00186         // changed font file names.
00187         if (mFTFace)
00188         {
00189                 FT_Done_Face(mFTFace);
00190                 mFTFace = NULL;
00191         }
00192 
00193         int error;
00194 
00195         error = FT_New_Face( gFTLibrary,
00196                                                  filename.c_str(),
00197                                                  0,
00198                                                  &mFTFace );
00199 
00200     if (error)
00201         {
00202                 return FALSE;
00203         }
00204 
00205         mIsFallback = is_fallback;
00206         mNumComponents = components;
00207         F32 pixels_per_em = (point_size / 72.f)*vert_dpi; // Size in inches * dpi
00208 
00209         error = FT_Set_Char_Size(mFTFace,    /* handle to face object           */
00210                                                         0,       /* char_width in 1/64th of points  */
00211                                                         (S32)(point_size*64),   /* char_height in 1/64th of points */
00212                                                         (U32)horz_dpi,     /* horizontal device resolution    */
00213                                                         (U32)vert_dpi);   /* vertical device resolution      */
00214 
00215         if (error)
00216         {
00217                 // Clean up freetype libs.
00218                 FT_Done_Face(mFTFace);
00219                 mFTFace = NULL;
00220                 return FALSE;
00221         }
00222 
00223         F32 y_max, y_min, x_max, x_min;
00224         F32 ems_per_unit = 1.f/ mFTFace->units_per_EM;
00225         F32 pixels_per_unit = pixels_per_em * ems_per_unit;
00226 
00227         // Get size of bbox in pixels
00228         y_max = mFTFace->bbox.yMax * pixels_per_unit;
00229         y_min = mFTFace->bbox.yMin * pixels_per_unit;
00230         x_max = mFTFace->bbox.xMax * pixels_per_unit;
00231         x_min = mFTFace->bbox.xMin * pixels_per_unit;
00232         mAscender = mFTFace->ascender * pixels_per_unit;
00233         mDescender = -mFTFace->descender * pixels_per_unit;
00234         mLineHeight = mFTFace->height * pixels_per_unit;
00235 
00236         mMaxCharWidth = llround(0.5f + (x_max - x_min));
00237         mMaxCharHeight = llround(0.5f + (y_max - y_min));
00238 
00239         if (!mFTFace->charmap)
00240         {
00241                 //llinfos << " no unicode encoding, set whatever encoding there is..." << llendl;
00242                 FT_Set_Charmap(mFTFace, mFTFace->charmaps[0]);
00243         }
00244 
00245         if (mRawImagep.isNull() && !mIsFallback)
00246         {
00247                 mRawImagep = new LLImageRaw();
00248         }
00249 
00250         if (!mIsFallback)
00251         {
00252                 // Place text into bitmap, and generate all necessary positions/
00253                 // offsets for the individual characters.
00254 
00255                 // calc width and height for mRawImagep (holds all characters)
00256                 // Guess for approximately 20*20 characters
00257                 S32 image_width = mMaxCharWidth * 20;
00258                 S32 pow_iw = 2;
00259                 while (pow_iw < image_width)
00260                 {
00261                         pow_iw *= 2;
00262                 }
00263                 image_width = pow_iw;
00264                 image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
00265                 S32 image_height = image_width;
00266 
00267                 //llinfos << "Guessing texture size of " << image_width << " pixels square" << llendl;
00268 
00269                 mRawImagep->resize(image_width, image_height, components);
00270 
00271                 mBitmapWidth = image_width;
00272                 mBitmapHeight = image_height;
00273 
00274                 switch (components)
00275                 {
00276                 case 1:
00277                         mRawImagep->clear();
00278                         break;
00279                 case 2:
00280                         mRawImagep->clear(255, 0);
00281                         break;
00282                 }
00283 
00284                 mCurrentOffsetX = 1;
00285                 mCurrentOffsetY = 1;
00286 
00287                 // Add the default glyph
00288                 addGlyph(0, 0);
00289         }
00290 
00291         mName = filename;
00292 
00293         return TRUE;
00294 }
00295 
00296 
00297 void LLFont::resetBitmap()
00298 {
00299         llinfos << "Rebuilding bitmap for glyph" << llendl;
00300 
00301         // Iterate through glyphs and clear the mIsRendered flag
00302         for (char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.begin();
00303                  iter != mCharGlyphInfoMap.end(); ++iter)
00304         {
00305                 iter->second->mIsRendered = FALSE;
00306         }
00307         mRawImagep->clear(255, 0);
00308         mCurrentOffsetX = 1;
00309         mCurrentOffsetY = 1;
00310 
00311         // Add the empty glyph`5
00312         addGlyph(0, 0);
00313 }
00314 
00315 LLFontGlyphInfo* LLFont::getGlyphInfo(const llwchar wch) const
00316 {
00317         char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
00318         if (iter != mCharGlyphInfoMap.end())
00319         {
00320                 return iter->second;
00321         }
00322         return NULL;
00323 }
00324 
00325 
00326 BOOL LLFont::hasGlyph(const llwchar wch) const
00327 {
00328         llassert(!mIsFallback);
00329         const LLFontGlyphInfo* gi = getGlyphInfo(wch);
00330         if (gi && gi->mIsRendered)
00331         {
00332                 return TRUE;
00333         }
00334         else
00335         {
00336                 return FALSE;
00337         }
00338 }
00339 
00340 BOOL LLFont::addChar(const llwchar wch)
00341 {
00342         if (mFTFace == NULL)
00343                 return FALSE;
00344 
00345         llassert(!mIsFallback);
00346         //lldebugs << "Adding new glyph for " << wch << " to font" << llendl;
00347 
00348         FT_UInt glyph_index;
00349 
00350         // Initialize char to glyph map
00351         glyph_index = FT_Get_Char_Index(mFTFace, wch);
00352         if (glyph_index == 0)
00353         {
00354                 // Try looking it up in the backup Unicode font
00355                 if (mFallbackFontp)
00356                 {
00357                         //llinfos << "Trying to add glyph from fallback font!" << llendl
00358                         LLFontList::iterator iter;
00359                         for(iter = mFallbackFontp->begin(); iter != mFallbackFontp->end(); iter++)
00360                         {
00361                                 glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch);
00362                                 if (glyph_index)
00363                                 {
00364                                         addGlyphFromFont(*iter, wch, glyph_index);
00365                                         return TRUE;
00366                                 }
00367                         }
00368                 }
00369         }
00370         
00371         char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
00372         if (iter == mCharGlyphInfoMap.end() || !(iter->second->mIsRendered))
00373         {
00374                 BOOL result = addGlyph(wch, glyph_index);
00375                 //imdebug("luma b=8 w=%d h=%d t=%s %p", mRawImagep->getWidth(), mRawImagep->getHeight(), mName.c_str(), mRawImagep->getData());
00376                 return result;
00377         }
00378         return FALSE;
00379 }
00380 
00381 void LLFont::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
00382 {
00383         char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
00384         if (iter != mCharGlyphInfoMap.end())
00385         {
00386                 delete iter->second;
00387                 iter->second = gi;
00388         }
00389         else
00390         {
00391                 mCharGlyphInfoMap[wch] = gi;
00392         }
00393 }
00394 
00395 BOOL LLFont::addGlyphFromFont(LLFont *fontp, const llwchar wch, const U32 glyph_index)
00396 {
00397         if (mFTFace == NULL)
00398                 return FALSE;
00399 
00400         llassert(!mIsFallback);
00401         fontp->renderGlyph(glyph_index);
00402         S32 width = fontp->mFTFace->glyph->bitmap.width;
00403         S32 height = fontp->mFTFace->glyph->bitmap.rows;
00404 
00405         if ((mCurrentOffsetX + width + 1) > mRawImagep->getWidth())
00406         {
00407                 if ((mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight)
00408                 {
00409                         // We're out of space in this texture - clear it an all of the glyphs
00410                         // and start over again.  Easier than LRU and should work just as well
00411                         // (just slightly slower on the rebuild).  As long as the texture has
00412                         // enough room to hold all glyphs needed for a particular frame this
00413                         // shouldn't be too slow.
00414 
00415                         resetBitmap();
00416 
00417                         // Need to rerender the glyph, as it's been overwritten by the default glyph.
00418                         fontp->renderGlyph(glyph_index);
00419                         width = fontp->mFTFace->glyph->bitmap.width;
00420                         height = fontp->mFTFace->glyph->bitmap.rows;
00421 
00422                         // We should have a reasonable offset for x and y, no need to check that it's in range
00423                 }
00424                 else
00425                 {
00426                         mCurrentOffsetX = 1;
00427                         mCurrentOffsetY += mMaxCharHeight + 1;
00428                 }
00429         }
00430 
00431         LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index);
00432         gi->mXBitmapOffset = mCurrentOffsetX;
00433         gi->mYBitmapOffset = mCurrentOffsetY;
00434         gi->mWidth = width;
00435         gi->mHeight = height;
00436         gi->mXBearing = fontp->mFTFace->glyph->bitmap_left;
00437         gi->mYBearing = fontp->mFTFace->glyph->bitmap_top;
00438         // Convert these from 26.6 units to float pixels.
00439         gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f;
00440         gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f;
00441         gi->mIsRendered = TRUE;
00442 
00443         insertGlyphInfo(wch, gi);
00444 
00445         llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
00446             || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
00447 
00448         if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
00449             || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
00450         {
00451                 U8 *buffer_data = fontp->mFTFace->glyph->bitmap.buffer;
00452                 S32 buffer_row_stride = fontp->mFTFace->glyph->bitmap.pitch;
00453                 U8 *tmp_graydata = NULL;
00454 
00455                 if (fontp->mFTFace->glyph->bitmap.pixel_mode
00456                     == FT_PIXEL_MODE_MONO)
00457                 {
00458                         // need to expand 1-bit bitmap to 8-bit graymap.
00459                         tmp_graydata = new U8[width * height];
00460                         S32 xpos, ypos;
00461                         for (ypos = 0; ypos < height; ++ypos)
00462                         {
00463                                 S32 bm_row_offset = buffer_row_stride * ypos;
00464                                 for (xpos = 0; xpos < width; ++xpos)
00465                                 {
00466                                         U32 bm_col_offsetbyte = xpos / 8;
00467                                         U32 bm_col_offsetbit = 7 - (xpos % 8);
00468                                         U32 bit =
00469                                         !!(buffer_data[bm_row_offset
00470                                                        + bm_col_offsetbyte
00471                                            ] & (1 << bm_col_offsetbit) );
00472                                         tmp_graydata[width*ypos + xpos] =
00473                                                 255 * bit;
00474                                 }
00475                         }
00476                         // use newly-built graymap.
00477                         buffer_data = tmp_graydata;
00478                         buffer_row_stride = width;
00479                 }
00480 
00481                 switch (mNumComponents)
00482                 {
00483                 case 1:
00484                         mRawImagep->setSubImage(mCurrentOffsetX,
00485                                                 mCurrentOffsetY,
00486                                                 width,
00487                                                 height,
00488                                                 buffer_data,
00489                                                 buffer_row_stride,
00490                                                 TRUE);
00491                         break;
00492                 case 2:
00493                         setSubImageLuminanceAlpha(mCurrentOffsetX,
00494                                                   mCurrentOffsetY,
00495                                                   width,
00496                                                   height,
00497                                                   buffer_data,
00498                                                   buffer_row_stride);
00499                         break;
00500                 default:
00501                         break;
00502                 }
00503 
00504                 if (tmp_graydata)
00505                         delete[] tmp_graydata;
00506         } else {
00507                 // we don't know how to handle this pixel format from FreeType;
00508                 // omit it from the font-image.
00509         }
00510         
00511         mCurrentOffsetX += width + 1;
00512         return TRUE;
00513 }
00514 
00515 BOOL LLFont::addGlyph(const llwchar wch, const U32 glyph_index)
00516 {
00517         return addGlyphFromFont(this, wch, glyph_index);
00518 }
00519 
00520 
00521 F32 LLFont::getXAdvance(const llwchar wch) const
00522 {
00523         if (mFTFace == NULL)
00524                 return 0.0;
00525 
00526         llassert(!mIsFallback);
00527         U32 glyph_index;
00528 
00529         // Return existing info only if it is current
00530         LLFontGlyphInfo* gi = getGlyphInfo(wch);
00531         if (gi && gi->mIsRendered)
00532         {
00533                 return gi->mXAdvance;
00534         }
00535 
00536         const LLFont* fontp = this;
00537         
00538         // Initialize char to glyph map
00539         glyph_index = FT_Get_Char_Index(mFTFace, wch);
00540         if (glyph_index == 0 && mFallbackFontp)
00541         {
00542                 LLFontList::iterator iter;
00543                 for(iter = mFallbackFontp->begin(); (iter != mFallbackFontp->end()) && (glyph_index == 0); iter++)
00544                 {
00545                         glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch);
00546                         if(glyph_index)
00547                         {
00548                                 fontp = *iter;
00549                         }
00550                 }
00551         }
00552         
00553         if (glyph_index)
00554         {
00555                 // This font has this glyph
00556                 (const_cast<LLFont *>(fontp))->renderGlyph(glyph_index);
00557 
00558                 // Create the entry if it's not there
00559                 char_glyph_info_map_t::iterator iter2 = mCharGlyphInfoMap.find(wch);
00560                 if (iter2 == mCharGlyphInfoMap.end())
00561                 {
00562                         gi = new LLFontGlyphInfo(glyph_index);
00563                         insertGlyphInfo(wch, gi);
00564                 }
00565                 else
00566                 {
00567                         gi = iter2->second;
00568                 }
00569                 
00570                 gi->mWidth = fontp->mFTFace->glyph->bitmap.width;
00571                 gi->mHeight = fontp->mFTFace->glyph->bitmap.rows;
00572 
00573                 // Convert these from 26.6 units to float pixels.
00574                 gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f;
00575                 gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f;
00576                 return gi->mXAdvance;
00577         }
00578         else
00579         {
00580                 gi = get_if_there(mCharGlyphInfoMap, (llwchar)0, (LLFontGlyphInfo*)NULL);
00581                 if (gi)
00582                 {
00583                         return gi->mXAdvance;
00584                 }
00585         }
00586 
00587         // Last ditch fallback - no glyphs defined at all.
00588         return (F32)mMaxCharWidth;
00589 }
00590 
00591 
00592 void LLFont::renderGlyph(const U32 glyph_index)
00593 {
00594         if (mFTFace == NULL)
00595                 return;
00596 
00597         int error = FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_DEFAULT );
00598         llassert(!error);
00599 
00600         error = FT_Render_Glyph(mFTFace->glyph, gFontRenderMode);
00601         llassert(!error);
00602 }
00603 
00604 
00605 F32 LLFont::getXKerning(const llwchar char_left, const llwchar char_right) const
00606 {
00607         if (mFTFace == NULL)
00608                 return 0.0;
00609 
00610         llassert(!mIsFallback);
00611         LLFontGlyphInfo* left_glyph_info = get_if_there(mCharGlyphInfoMap, char_left, (LLFontGlyphInfo*)NULL);
00612         U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
00613         // Kern this puppy.
00614         LLFontGlyphInfo* right_glyph_info = get_if_there(mCharGlyphInfoMap, char_right, (LLFontGlyphInfo*)NULL);
00615         U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;
00616 
00617         FT_Vector  delta;
00618 
00619         llverify(!FT_Get_Kerning(mFTFace, left_glyph, right_glyph, ft_kerning_unfitted, &delta));
00620 
00621         return delta.x*(1.f/64.f);
00622 }
00623 
00624 void LLFont::setSubImageLuminanceAlpha(const U32 x,
00625                                        const U32 y,
00626                                        const U32 width,
00627                                        const U32 height,
00628                                        const U8 *data,
00629                                        S32 stride)
00630 {
00631         llassert(!mIsFallback);
00632         llassert(mRawImagep->getComponents() == 2);
00633 
00634         U8 *target = mRawImagep->getData();
00635 
00636         if (!data)
00637         {
00638                 return;
00639         }
00640 
00641         if (0 == stride)
00642                 stride = width;
00643 
00644         U32 i, j;
00645         U32 to_offset;
00646         U32 from_offset;
00647         U32 target_width = mRawImagep->getWidth();
00648         for (i = 0; i < height; i++)
00649         {
00650                 to_offset = (y + i)*target_width + x;
00651                 from_offset = (height - 1 - i)*stride;
00652                 for (j = 0; j < width; j++)
00653                 {
00654                         *(target + to_offset*2 + 1) = *(data + from_offset);
00655                         to_offset++;
00656                         from_offset++;
00657                 }
00658         }
00659 }

Generated on Fri May 16 08:32:48 2008 for SecondLife by  doxygen 1.5.5