Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

CEGUIFont.cpp

Go to the documentation of this file.
00001 /************************************************************************
00002         filename:       CEGUIFont.cpp
00003         created:        21/2/2004
00004         author:         Paul D Turner
00005         
00006         purpose:        Implements Font class
00007 *************************************************************************/
00008 /*************************************************************************
00009     Crazy Eddie's GUI System (http://www.cegui.org.uk)
00010     Copyright (C)2004 - 2005 Paul D Turner (paul@cegui.org.uk)
00011 
00012     This library is free software; you can redistribute it and/or
00013     modify it under the terms of the GNU Lesser General Public
00014     License as published by the Free Software Foundation; either
00015     version 2.1 of the License, or (at your option) any later version.
00016 
00017     This library is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020     Lesser General Public License for more details.
00021 
00022     You should have received a copy of the GNU Lesser General Public
00023     License along with this library; if not, write to the Free Software
00024     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 *************************************************************************/
00026 #include "CEGUIFont.h"
00027 #include "CEGUIExceptions.h"
00028 #include "CEGUISystem.h"
00029 #include "CEGUIFontManager.h"
00030 #include "CEGUIImagesetManager.h"
00031 #include "CEGUIImageset.h"
00032 #include "CEGUITexture.h"
00033 #include "CEGUILogger.h"
00034 #include "CEGUITextUtils.h"
00035 #include "CEGUIFont_xmlHandler.h"
00036 #include "CEGUIFont_implData.h"
00037 #include "CEGUIResourceProvider.h"
00038 #include "CEGUIXMLParser.h"
00039 
00040 #include <ft2build.h>
00041 #include FT_FREETYPE_H
00042 
00043 #include <algorithm>
00044 #include <sstream>
00045 
00046 // Start of CEGUI namespace section
00047 namespace CEGUI
00048 {
00049 /*************************************************************************
00050         static data definitions
00051 *************************************************************************/
00052 const argb_t Font::DefaultColour                                        = 0xFFFFFFFF;
00053 const uint      Font::InterGlyphPadSpace                        = 2;
00054 
00055 // XML related strings
00056 const char      Font::FontSchemaName[]                          = "Font.xsd";
00057 
00058 
00059 /*************************************************************************
00060         Constructs a new Font object from a font definition file
00061 *************************************************************************/
00062 Font::Font(const String& filename, const String& resourceGroup, FontImplData* dat)
00063 {
00064         d_antiAliased = false;
00065         d_impldat = dat;
00066         d_freetype = false;
00067         d_glyph_images = NULL;
00068 
00069         // defaults for scaling options
00070         d_autoScale = false;
00071         setNativeResolution(Size(DefaultNativeHorzRes, DefaultNativeVertRes));
00072 
00073         load(filename, resourceGroup);
00074 
00075         // complete y spacing set-up for bitmap / static fonts
00076         calculateStaticVertSpacing();
00077 }
00078 
00079 
00080 /*************************************************************************
00081         Constructs a new Font object (via FreeType & a true-type font file)
00082         'glyph-set' describes the set of code points to be available via
00083         this font
00084 *************************************************************************/
00085 Font::Font(const String& name, const String& fontname, const String& resourceGroup, uint size, uint flags, const String& glyph_set, FontImplData* dat)
00086 {
00087         d_impldat = dat;
00088         d_freetype = false;
00089         d_glyph_images = NULL;
00090 
00091         // defaults for scaling options
00092         d_autoScale = false;
00093         setNativeResolution(Size(DefaultNativeHorzRes, DefaultNativeVertRes));
00094 
00095         constructor_impl(name, fontname, resourceGroup, size, flags, glyph_set);
00096 }
00097 
00098 
00099 /*************************************************************************
00100         Constructs a new Font object (via FreeType & a true-type font file)
00101         [first_code_point, last_code_point] describes the range of code
00102         points to be available via this font
00103 *************************************************************************/
00104 Font::Font(const String& name, const String& fontname, const String& resourceGroup, uint size, uint flags, utf32 first_code_point, utf32 last_code_point, FontImplData* dat)
00105 {
00106         d_impldat = dat;
00107         d_freetype = false;
00108         d_glyph_images = NULL;
00109 
00110         // defaults for scaling options
00111         d_autoScale = false;
00112         setNativeResolution(Size(DefaultNativeHorzRes, DefaultNativeVertRes));
00113 
00114         String tmp;
00115 
00116         for (utf32 cp = first_code_point; cp <= last_code_point; ++cp)
00117         {
00118                 tmp += cp;
00119         }
00120 
00121         constructor_impl(name, fontname, resourceGroup, size, flags, tmp);
00122 }
00123 
00124 
00125 /*************************************************************************
00126         Constructs a new Font object (via FreeType & a true-type font file)
00127         The font file will provide support for 7-bit ASCII characters only
00128 *************************************************************************/
00129 Font::Font(const String& name, const String& fontname, const String& resourceGroup, uint size, uint flags, FontImplData* dat)
00130 {
00131         d_impldat = dat;
00132         d_freetype = false;
00133         d_glyph_images = NULL;
00134 
00135         // defaults for scaling options
00136         d_autoScale = false;
00137         setNativeResolution(Size(DefaultNativeHorzRes, DefaultNativeVertRes));
00138 
00139         String tmp;
00140 
00141         for (utf32 cp = 32; cp <= 127; ++cp)
00142         {
00143                 tmp += cp;
00144         }
00145 
00146         constructor_impl(name, fontname, resourceGroup, size, flags, tmp);
00147 }
00148 
00149 
00150 /*************************************************************************
00151         Destroys a Font object
00152 *************************************************************************/
00153 Font::~Font(void)
00154 {
00155         unload();
00156         delete d_impldat;
00157 }
00158 
00159 
00160 /*************************************************************************
00161         Return the pixel width of the specified text if rendered with this Font.
00162 *************************************************************************/
00163 float Font::getTextExtent(const String& text, float x_scale) const
00164 {
00165     float cur_extent = 0, adv_extent = 0, width;
00166 
00167     size_t char_count = text.length();
00168     CodepointMap::const_iterator pos, end = d_cp_map.end();
00169 
00170     for (size_t c = 0; c < char_count; ++c)
00171     {
00172         pos = d_cp_map.find(text[c]);
00173 
00174         if (pos != end)
00175         {
00176             width = (pos->second.d_image->getWidth() + pos->second.d_image->getOffsetX()) * x_scale;
00177             
00178             if (adv_extent + width > cur_extent)
00179             {
00180                 cur_extent = adv_extent + width;
00181             }
00182             
00183             adv_extent += (float)pos->second.d_horz_advance * x_scale;
00184         }
00185 
00186     }
00187 
00188     return ceguimax(adv_extent, cur_extent);
00189 }
00190 
00191 
00192 /*************************************************************************
00193         Return the index of the closest text character in String 'text' that
00194         corresponds to pixel location 'pixel' if the text were rendered.
00195 *************************************************************************/
00196 size_t Font::getCharAtPixel(const String& text, size_t start_char, float pixel, float x_scale) const
00197 {
00198         float cur_extent = 0;
00199         size_t char_count = text.length();
00200 
00201         // handle simple cases
00202         if ((pixel <= 0) || (char_count <= start_char))
00203         {
00204                 return start_char;
00205         }
00206 
00207         CodepointMap::const_iterator    pos, end = d_cp_map.end();
00208 
00209         for (size_t c = start_char; c < char_count; ++c)
00210         {
00211                 pos = d_cp_map.find(text[c]);
00212 
00213                 if (pos != end)
00214                 {
00215                         cur_extent += (float)pos->second.d_horz_advance * x_scale;
00216 
00217                         if (pixel < cur_extent)
00218                         {
00219                                 return c;
00220                         }
00221 
00222                 }
00223 
00224         }
00225 
00226         return char_count;
00227 }
00228 
00229 
00230 /*************************************************************************
00231         Renders text on the display.  Return number of lines output.
00232 *************************************************************************/
00233 size_t Font::drawText(const String& text, const Rect& draw_area, float z, const Rect& clip_rect, TextFormatting fmt, const ColourRect& colours, float x_scale, float y_scale) const
00234 {
00235         size_t thisCount;
00236         size_t lineCount = 0;
00237 
00238         float   y_base = draw_area.d_top + getBaseline(y_scale);
00239 
00240         Rect tmpDrawArea(
00241                 PixelAligned(draw_area.d_left),
00242                 PixelAligned(draw_area.d_top),
00243                 PixelAligned(draw_area.d_right),
00244                 PixelAligned(draw_area.d_bottom)
00245                 );
00246 
00247         size_t lineStart = 0, lineEnd = 0;
00248         String  currLine;
00249 
00250         while (lineEnd < text.length())
00251         {
00252                 if ((lineEnd = text.find_first_of('\n', lineStart)) == String::npos)
00253                 {
00254                         lineEnd = text.length();
00255                 }
00256 
00257                 currLine = text.substr(lineStart, lineEnd - lineStart);
00258                 lineStart = lineEnd + 1;        // +1 to skip \n char
00259 
00260                 switch(fmt)
00261                 {
00262                 case LeftAligned:
00263                         drawTextLine(currLine, Vector3(tmpDrawArea.d_left, y_base, z), clip_rect, colours, x_scale, y_scale);
00264                         thisCount = 1;
00265                         y_base += getLineSpacing(y_scale);
00266                         break;
00267 
00268                 case RightAligned:
00269                         drawTextLine(currLine, Vector3(tmpDrawArea.d_right - getTextExtent(currLine, x_scale), y_base, z), clip_rect, colours, x_scale, y_scale);
00270                         thisCount = 1;
00271                         y_base += getLineSpacing(y_scale);
00272                         break;
00273 
00274                 case Centred:
00275                         drawTextLine(currLine, Vector3(PixelAligned(tmpDrawArea.d_left + ((tmpDrawArea.getWidth() - getTextExtent(currLine, x_scale)) / 2.0f)), y_base, z), clip_rect, colours, x_scale, y_scale);
00276                         thisCount = 1;
00277                         y_base += getLineSpacing(y_scale);
00278                         break;
00279 
00280                 case Justified:
00281                         // new function in order to keep drawTextLine's signature unchanged
00282                         drawTextLineJustified(currLine, draw_area, Vector3(tmpDrawArea.d_left, y_base, z), clip_rect, colours, x_scale, y_scale);
00283                         thisCount = 1;
00284                         y_base += getLineSpacing(y_scale);
00285                         break;
00286 
00287                 case WordWrapLeftAligned:
00288                         thisCount = drawWrappedText(currLine, tmpDrawArea, z, clip_rect, LeftAligned, colours, x_scale, y_scale);
00289                         tmpDrawArea.d_top += thisCount * getLineSpacing(y_scale);
00290                         break;
00291 
00292                 case WordWrapRightAligned:
00293                         thisCount = drawWrappedText(currLine, tmpDrawArea, z, clip_rect, RightAligned, colours, x_scale, y_scale);
00294                         tmpDrawArea.d_top += thisCount * getLineSpacing(y_scale);
00295                         break;
00296 
00297                 case WordWrapCentred:
00298                         thisCount = drawWrappedText(currLine, tmpDrawArea, z, clip_rect, Centred, colours, x_scale, y_scale);
00299                         tmpDrawArea.d_top += thisCount * getLineSpacing(y_scale);
00300                         break;
00301 
00302                 case WordWrapJustified:
00303                         // no change needed
00304                         thisCount = drawWrappedText(currLine, tmpDrawArea, z, clip_rect, Justified, colours, x_scale, y_scale);
00305                         tmpDrawArea.d_top += thisCount * getLineSpacing(y_scale);
00306                         break;
00307 
00308                 default:
00309                         throw InvalidRequestException((utf8*)"Font::drawText - Unknown or unsupported TextFormatting value specified.");
00310                 }
00311 
00312                 lineCount += thisCount;
00313 
00314         }
00315 
00316     // should not return 0
00317     return ceguimax(lineCount, (size_t)1);
00318 }
00319 
00320 
00321 /*************************************************************************
00322         Define the set of glyphs available for this font
00323 *************************************************************************/
00324 void Font::defineFontGlyphs(const String& glyph_set)
00325 {
00326         d_glyphset = glyph_set;
00327         defineFontGlyphs_impl();
00328 
00329         Logger::getSingleton().logEvent("Font '" + d_name + "' now has the following glyphs defined: '" + d_glyphset + "'.", Informative);
00330 }
00331 
00332 
00333 /*************************************************************************
00334         Return the required texture size required to store imagery for the
00335         glyphs specified in 'glyph_set'
00336 *************************************************************************/
00337 uint Font::getRequiredTextureSize(const String& glyph_set)
00338 {
00339         d_maxGlyphHeight = 0;
00340 
00341         uint    texSize = 32;                   // start with a texture this size
00342         uint    width;
00343 
00344         uint    cur_x = 0;
00345         uint    cur_y = d_maxGlyphHeight;
00346 
00347         uint    glyph_set_length = static_cast<uint>(glyph_set.length());
00348 
00349         for (uint i = 0; i < glyph_set_length; ++i)
00350         {
00351                 // load-up required glyph
00352                 if (FT_Load_Char(d_impldat->fontFace, glyph_set[i], FT_LOAD_RENDER|(d_antiAliased ? 0 : FT_LOAD_MONOCHROME)))
00353                 {
00354                         // skip errors
00355                         continue;
00356                 }
00357 
00358                 // if this glyph is taller than all others so far, update height and re-calculate cur_y
00359                 if ((uint)d_impldat->fontFace->glyph->bitmap.rows + InterGlyphPadSpace > d_maxGlyphHeight)
00360                 {
00361                         d_maxGlyphHeight = d_impldat->fontFace->glyph->bitmap.rows + InterGlyphPadSpace;
00362                         cur_y = (i + 1) * d_maxGlyphHeight;
00363                 }
00364 
00365                 width = d_impldat->fontFace->glyph->bitmap.width + InterGlyphPadSpace;
00366                 cur_x += width;
00367 
00368                 // check for fit
00369                 if (cur_x >= texSize)
00370                 {
00371                         cur_x = width;
00372                         cur_y += d_maxGlyphHeight;
00373 
00374                         if (cur_y >= texSize)
00375                         {
00376                                 // texture is too small, set-up to start again...
00377                                 texSize *= 2;
00378                                 cur_x = 0;
00379                                 cur_y = d_maxGlyphHeight;
00380                                 i = (uint)-1;
00381                         }
00382 
00383                 }
00384 
00385         }
00386 
00387         return texSize;
00388 }
00389 
00390 
00391 /*************************************************************************
00392         Render a set of glyph images into the given memory buffer.
00393         pixels will be in A8R8G8B8 format
00394 *************************************************************************/
00395 void Font::createFontGlyphSet(const String& glyph_set, uint size, argb_t* buffer)
00396 {
00397         String  imageName;
00398         Rect    rect;
00399         Point   offset;
00400 
00401         FT_GlyphSlot glyph = d_impldat->fontFace->glyph;
00402 
00403         d_max_bearingY = 0;
00404 
00405         size_t  glyph_set_length = glyph_set.length();
00406         uint    cur_x = 0;
00407         uint    cur_y = 0;
00408         uint    width;
00409 
00410         for (uint i = 0; i < glyph_set_length; ++i)
00411         {
00412                 // load-up required glyph
00413                 if (FT_Load_Char(d_impldat->fontFace, glyph_set[i], FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT|
00414             (d_antiAliased ? FT_LOAD_TARGET_NORMAL : FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO)))
00415                 {
00416                         // skip errors (but now we log them!)
00417                         std::stringstream err;
00418                         err << "Font::createFontGlyphSet - Failed to load glyph for codepoint: ";
00419                         err << static_cast<unsigned int>(glyph_set[i]);
00420                         Logger::getSingleton().logEvent(err.str(), Errors);
00421 
00422                         continue;
00423                 }
00424 
00425                 width = glyph->bitmap.width + InterGlyphPadSpace;
00426 
00427                 // see if we need to wrap to next row
00428                 if (cur_x + width >= size)
00429                 {
00430                         cur_x = 0;
00431                         cur_y += d_maxGlyphHeight;
00432                 }
00433 
00434                 // calculate offset into buffer for this glyph
00435                 argb_t* dest_buff = buffer + (cur_y * size) + cur_x;
00436 
00437                 // draw glyph into buffer
00438                 drawGlyphToBuffer(dest_buff, size);
00439 
00440                 // define Image on Imageset for this glyph to save re-rendering glyph later
00441                 imageName               = glyph_set[i];
00442                 rect.d_left             = (float)cur_x;
00443                 rect.d_top              = (float)cur_y;
00444                 rect.d_right    = (float)(cur_x + width - InterGlyphPadSpace);
00445                 rect.d_bottom   = (float)(cur_y + d_maxGlyphHeight - InterGlyphPadSpace);
00446 
00447                 offset.d_x              = (float)(glyph->metrics.horiBearingX >> 6);
00448                 offset.d_y              = -(float)(glyph->metrics.horiBearingY >> 6);
00449 
00450                 d_glyph_images->defineImage(imageName, rect, offset);
00451 
00452                 cur_x += width;
00453 
00454 //              // check and update maximum bearingY value
00455 //              if (static_cast<float>(glyph->metrics.horiBearingY >> 6) > d_max_bearingY)
00456 //              {
00457 //                      d_max_bearingY = static_cast<float>(glyph->metrics.horiBearingY >> 6);
00458 //              }
00459 
00460                 // create entry in code-point to Image map
00461                 glyphDat        dat;
00462                 dat.d_image = &d_glyph_images->getImage(imageName);
00463                 dat.d_horz_advance = glyph->advance.x >> 6;
00464                 d_cp_map[glyph_set[i]] = dat;
00465         }
00466 
00467 }
00468 
00469 
00470 /*************************************************************************
00471         Render a range of glyph images into the given memory buffer.
00472         pixels will be in A8R8G8B8 format
00473 *************************************************************************/
00474 void Font::createFontGlyphSet(utf32 first_code_point, utf32 last_code_point, uint size, argb_t* buffer)
00475 {
00476         String tmp;
00477 
00478         for (utf32 cp = first_code_point; cp <=last_code_point; ++cp)
00479         {
00480                 tmp += cp;
00481         }
00482 
00483         createFontGlyphSet(tmp, size, buffer);
00484 }
00485 
00486 
00487 /*************************************************************************
00488         Return the required texture size required to store imagery for the
00489         glyphs specified in by the inclusive range
00490         [first_code_point, last_code_point]
00491 *************************************************************************/
00492 uint Font::getRequiredTextureSize(utf32 first_code_point, utf32 last_code_point)
00493 {
00494         String tmp;
00495 
00496         for (utf32 cp = first_code_point; cp <=last_code_point; ++cp)
00497         {
00498                 tmp += cp;
00499         }
00500 
00501         return getRequiredTextureSize(tmp);
00502 }
00503 
00504 
00505 /*************************************************************************
00506         Define the range of glyphs available for this font
00507 *************************************************************************/
00508 void Font::defineFontGlyphs(utf32 first_code_point, utf32 last_code_point)
00509 {
00510         String tmp;
00511 
00512         for (utf32 cp = first_code_point; cp <=last_code_point; ++cp)
00513         {
00514                 tmp += cp;
00515         }
00516 
00517         defineFontGlyphs(tmp);
00518 }
00519 
00520 
00521 /*************************************************************************
00522         Copy the FreeType glyph bitmap into the given memory buffer
00523 *************************************************************************/
00524 void Font::drawGlyphToBuffer(argb_t* buffer, uint buf_width)
00525 {
00526         FT_Bitmap* glyph_bitmap = &d_impldat->fontFace->glyph->bitmap;
00527 
00528         for (int i = 0; i < glyph_bitmap->rows; ++i)
00529         {
00530                 for (int j = 0; j < glyph_bitmap->width; ++j)
00531                 {
00532                         switch (glyph_bitmap->pixel_mode)
00533                         {
00534                         case FT_PIXEL_MODE_GRAY:
00535                                 {
00536                                         uchar* bytebuff = reinterpret_cast<uchar*>(&buffer[j]);
00537                                         *bytebuff++ = 0xFF;
00538                                         *bytebuff++ = 0xFF;
00539                                         *bytebuff++ = 0xFF;
00540                                         *bytebuff = glyph_bitmap->buffer[(i * glyph_bitmap->pitch) + j];
00541                                 }
00542                                 break;
00543 
00544                         case FT_PIXEL_MODE_MONO:
00545                                 buffer[j] = ((glyph_bitmap->buffer[(i * glyph_bitmap->pitch) + j / 8] << (j % 8)) & 0x80) ? 0xFFFFFFFF : 0x00000000;
00546                                 break;
00547 
00548                         default:
00549                                 throw InvalidRequestException((utf8*)"Font::drawGlyphToBuffer - The glyph could not be drawn because the pixel mode is unsupported.");
00550                                 break;
00551                         }
00552 
00553                 }
00554 
00555                 buffer += buf_width;
00556         }
00557 
00558 }
00559 
00560 
00561 /*************************************************************************
00562         draws wrapped text
00563 *************************************************************************/
00564 size_t Font::drawWrappedText(const String& text, const Rect& draw_area, float z, const Rect& clip_rect, TextFormatting fmt, const ColourRect& colours, float x_scale, float y_scale) const
00565 {
00566         size_t  line_count = 0;
00567         Rect    dest_area(draw_area);
00568         float   wrap_width = draw_area.getWidth();
00569 
00570         String  whitespace = TextUtils::DefaultWhitespace;
00571         String  thisLine, thisWord;
00572         size_t  currpos = 0;
00573 
00574         // get first word.
00575         currpos += getNextWord(text, currpos, thisLine);
00576 
00577         // while there are words left in the string...
00578         while (String::npos != text.find_first_not_of(whitespace, currpos)) {
00579                 // get next word of the string...
00580                 currpos += getNextWord(text, currpos, thisWord);
00581 
00582                 // if the new word would make the string too long
00583                 if ((getTextExtent(thisLine, x_scale) + getTextExtent(thisWord, x_scale)) > wrap_width) {
00584                         // output what we had until this new word
00585                         line_count += drawText(thisLine, dest_area, z, clip_rect, fmt, colours, x_scale, y_scale);
00586 
00587                         // remove whitespace from next word - it will form start of next line
00588                         thisWord = thisWord.substr(thisWord.find_first_not_of(whitespace));
00589 
00590                         // reset for a new line.
00591                         thisLine.clear();
00592 
00593                         // update y co-ordinate for next line
00594                         dest_area.d_top += getLineSpacing(y_scale);
00595                 }
00596 
00597                 // add the next word to the line
00598                 thisLine += thisWord;
00599         }
00600 
00601         // Last line is left aligned
00602         TextFormatting last_fmt = (fmt == Justified ? LeftAligned : fmt);
00603         // output last bit of string
00604         line_count += drawText(thisLine, dest_area, z, clip_rect, last_fmt, colours, x_scale, y_scale);
00605 
00606         return line_count;
00607 }
00608 
00609 
00610 /*************************************************************************
00611         helper function for renderWrappedText to get next word of a string
00612 *************************************************************************/
00613 size_t Font::getNextWord(const String& in_string, size_t start_idx, String& out_string) const
00614 {
00615         out_string = TextUtils::getNextWord(in_string, start_idx, TextUtils::DefaultWrapDelimiters);
00616 
00617         return out_string.length();
00618 }
00619 
00620 
00621 /*************************************************************************
00622         Draw a line of text.  No formatting is applied.
00623 *************************************************************************/
00624 void Font::drawTextLine(const String& text, const Vector3& position, const Rect& clip_rect, const ColourRect& colours, float x_scale, float y_scale) const
00625 {
00626         Vector3 cur_pos(position);
00627 
00628         float base_y = position.d_y;
00629 
00630         size_t char_count = text.length();
00631         CodepointMap::const_iterator    pos, end = d_cp_map.end();
00632 
00633         for (size_t c = 0; c < char_count; ++c)
00634         {
00635                 pos = d_cp_map.find(text[c]);
00636 
00637                 if (pos != end)
00638                 {
00639                         const Image* img = pos->second.d_image;
00640                         cur_pos.d_y = base_y - (img->getOffsetY() - img->getOffsetY() * y_scale);
00641                         Size sz(img->getWidth() * x_scale, img->getHeight() * y_scale);
00642                         img->draw(cur_pos, sz, clip_rect, colours);
00643                         cur_pos.d_x += (float)pos->second.d_horz_advance * x_scale;
00644                 }
00645 
00646         }
00647 
00648 }
00649 
00650 
00651 /*************************************************************************
00652         Draw a justified line of text.
00653 *************************************************************************/
00654 void Font::drawTextLineJustified(const String& text, const Rect& draw_area, const Vector3& position, const Rect& clip_rect, const ColourRect& colours, float x_scale, float y_scale) const
00655 {
00656         Vector3 cur_pos(position);
00657 
00658         float base_y = position.d_y;
00659         // Calculate the length difference between the justified text and the same text, left aligned
00660         // This space has to be shared between the space characters of the line
00661         float lost_space = getFormattedTextExtent(text, draw_area, Justified, x_scale) - getTextExtent(text, x_scale);
00662 
00663         size_t char_count = text.length();
00664         CodepointMap::const_iterator    pos, end = d_cp_map.end();
00665 
00666         // The number of spaces and tabs in the current line
00667         uint space_count = 0;
00668     size_t c;
00669         for (c = 0; c < char_count; ++c)
00670                 if ((text[c] == ' ') || (text[c] == '\t')) ++space_count;
00671 
00672         // The width that must be added to each space character in order to transform the left aligned text in justified text
00673         float shared_lost_space = 0.0;
00674         if (space_count > 0) shared_lost_space = lost_space / (float)space_count;
00675 
00676         for (c = 0; c < char_count; ++c)
00677         {
00678                 pos = d_cp_map.find(text[c]);
00679 
00680                 if (pos != end)
00681                 {
00682                         const Image* img = pos->second.d_image;
00683                         cur_pos.d_y = base_y - (img->getOffsetY() - img->getOffsetY() * y_scale);
00684                         Size sz(img->getWidth() * x_scale, img->getHeight() * y_scale);
00685                         img->draw(cur_pos, sz, clip_rect, colours);
00686                         cur_pos.d_x += (float)pos->second.d_horz_advance * x_scale;
00687                         // That's where we adjust the size of each space character
00688                         if ((text[c] == ' ') || (text[c] == '\t')) cur_pos.d_x += shared_lost_space;
00689                 }
00690 
00691         }
00692 
00693 }
00694 
00695 
00696 /*************************************************************************
00697         Function to do real work of constructor
00698 *************************************************************************/
00699 void Font::constructor_impl(const String& name, const String& fontname, const String& resourceGroup, uint size, uint flags, const String& glyph_set)
00700 {
00701         ImagesetManager& ismgr  = ImagesetManager::getSingleton();
00702 
00703         // pull a-a setting from flags
00704         d_antiAliased = (flags == NoAntiAlias) ? false : true;
00705 
00706         // create an blank Imageset
00707         d_glyph_images = ismgr.createImageset(name + "_auto_glyph_images", System::getSingleton().getRenderer()->createTexture());
00708 
00709         uint            horzdpi         = System::getSingleton().getRenderer()->getHorzScreenDPI();
00710         uint            vertdpi         = System::getSingleton().getRenderer()->getVertScreenDPI();
00711         String          errMsg;
00712 
00713     System::getSingleton().getResourceProvider()->loadRawDataContainer(fontname, d_impldat->fontData, resourceGroup);
00714 
00715         // create face using input font
00716         if (FT_New_Memory_Face(d_impldat->library, d_impldat->fontData.getDataPtr(), 
00717                 (FT_Long)d_impldat->fontData.getSize(), 0, &d_impldat->fontFace) == 0)
00718         {
00719                 // check that default Unicode character map is available
00720                 if (d_impldat->fontFace->charmap != NULL)       
00721                 {
00722                         try
00723                         {
00724                                 d_glyphset = glyph_set;
00725                                 d_name = name;
00726                                 d_freetype = true;
00727                                 createFontFromFT_Face(size, horzdpi, vertdpi);
00728                                 return;
00729                         }
00730                         catch(...)
00731                         {
00732                                 ismgr.destroyImageset(d_glyph_images);
00733                                 d_glyph_images = NULL;
00734 
00735                                 FT_Done_Face(d_impldat->fontFace);
00736                                 d_freetype = false;
00737 
00738                                 // re-throw
00739                                 throw;
00740                         }
00741 
00742                 }
00743                 // missing Unicode character map
00744                 else
00745                 {
00746                         FT_Done_Face(d_impldat->fontFace);
00747                         d_freetype = false;
00748 
00749                         errMsg = (utf8*)"Font::constructor_impl - The source font '" + fontname +"' does not have a Unicode charmap, and cannot be used.";
00750                 }
00751 
00752         }
00753         // failed to create face (a problem with the font file?)
00754         else
00755         {
00756                 errMsg = (utf8*)"Font::constructor_impl - An error occurred while trying to create a FreeType face from source font '" + fontname + "'.";
00757         }
00758 
00759         // cleanup image set we created here
00760         ismgr.destroyImageset(d_glyph_images);
00761 
00762         throw GenericException(errMsg);
00763 }
00764 
00765 
00766 /*************************************************************************
00767         Load and complete construction of 'this' via an XML file
00768 *************************************************************************/
00769 void Font::load(const String& filename, const String& resourceGroup)
00770 {
00771         // unload old data
00772         unload();
00773 
00774         if (filename.empty() || (filename == (utf8*)""))
00775         {
00776                 throw InvalidRequestException((utf8*)"Font::load - Filename supplied for Font loading must be valid");
00777         }
00778 
00779     // create handler object
00780     Font_xmlHandler handler(this);
00781 
00782         // do parse (which uses handler to create actual data)
00783         try
00784         {
00785         System::getSingleton().getXMLParser()->parseXMLFile(handler, filename, FontSchemaName, resourceGroup);
00786         }
00787         catch(...)
00788         {
00789                 unload();
00790 
00791         Logger::getSingleton().logEvent("Font::load - loading of Font from file '" + filename +"' failed.", Errors);
00792                 throw;
00793         }
00794 
00795 }
00796 
00797 
00798 /*************************************************************************
00799         Unload data associated with the font (font is then useless.
00800         this is intended for cleanup).
00801 *************************************************************************/
00802 void Font::unload(void)
00803 {
00804         d_cp_map.clear();
00805 
00806         // cleanup Imageset if it's valid
00807         if (d_glyph_images != NULL)
00808         {
00809                 ImagesetManager::getSingleton().destroyImageset(d_glyph_images);
00810                 d_glyph_images = NULL;
00811         }
00812 
00813         // cleanup FreeType face if this is a FreeType based font.
00814         if (d_freetype)
00815         {
00816                 FT_Done_Face(d_impldat->fontFace);
00817                 d_freetype = false;
00818         }
00819 
00820         System::getSingleton().getResourceProvider()->unloadRawDataContainer(d_impldat->fontData);
00821 
00822 }
00823 
00824 
00825 /*************************************************************************
00826         Defines the set of code points on the font. (implementation).
00827 *************************************************************************/
00828 void Font::defineFontGlyphs_impl(void)
00829 {
00830         // must be a font using the FreeType system
00831         if (!d_freetype)
00832         {
00833                 throw InvalidRequestException((utf8*)"Font::defineFontGlyphs_impl - operation not supported on bitmap based fonts.");
00834         }
00835 
00836         uint texture_size = getRequiredTextureSize(d_glyphset);
00837 
00838         // check renderer can do a texture big enough
00839         if (texture_size > System::getSingleton().getRenderer()->getMaxTextureSize())
00840         {
00841                 throw   RendererException((utf8*)"Font::defineFontGlyphs_impl - operation requires a texture larger than the supported maximum.");
00842         }
00843 
00844         // allocate memory buffer where we will define the imagery
00845         argb_t* mem_buffer;
00846 
00847         try
00848         {
00849                 mem_buffer = new argb_t[texture_size * texture_size];
00850         }
00851         catch (std::bad_alloc)
00852         {
00853                 throw   MemoryException((utf8*)"Font::defineFontGlyphs_impl - failed to allocate required memory buffer.");
00854         }
00855 
00856         // initialise background to transparent black.
00857         memset(mem_buffer, 0, ((texture_size * texture_size) * sizeof(argb_t)));
00858 
00859         // clear old data about glyphs and images
00860         d_cp_map.clear();
00861         d_glyph_images->undefineAllImages();
00862 
00863         // render new glyphs and define Imageset images.
00864         createFontGlyphSet(d_glyphset, texture_size, mem_buffer);
00865 
00866         // update Imageset texture with new imagery.
00867         d_glyph_images->getTexture()->loadFromMemory(mem_buffer, texture_size, texture_size);
00868 
00869         delete[] mem_buffer;
00870 
00871         d_lineHeight = (float)d_maxGlyphHeight;
00872 
00873         // calculate spacing and base-line
00874     d_max_bearingY = ((float)d_impldat->fontFace->ascender / (float)d_impldat->fontFace->units_per_EM) * (float)d_impldat->fontFace->size->metrics.y_ppem;
00875         d_lineSpacing = ((float)d_impldat->fontFace->height / (float)d_impldat->fontFace->units_per_EM) * (float)d_impldat->fontFace->size->metrics.y_ppem;
00876 }
00877 
00878 
00879 /*************************************************************************
00880         Calculate the vertical spacing fields for a static / bitmap font
00881 *************************************************************************/
00882 void Font::calculateStaticVertSpacing(void)
00883 {
00884         if (!d_freetype)
00885         {
00886                 float scale = d_autoScale ? d_vertScaling : 1.0f;
00887 
00888                 d_lineHeight    = 0;
00889                 d_max_bearingY  = 0;
00890 
00891                 CodepointMap::iterator pos = d_cp_map.begin(), end = d_cp_map.end();
00892 
00893                 for (;pos != end; ++pos)
00894                 {
00895                         const Image* img = pos->second.d_image;
00896 
00897                         if (-img->getOffsetY() > d_max_bearingY)
00898                                 d_max_bearingY = -img->getOffsetY();
00899 
00900                         if (img->getHeight() > d_lineHeight)
00901                                 d_lineHeight = img->getHeight();
00902                 }
00903 
00904                 d_max_bearingY *= scale;
00905                 d_lineHeight *= scale;
00906                 d_lineSpacing = d_lineHeight;
00907         d_maxGlyphHeight = static_cast<uint>(d_lineHeight);
00908         }
00909 
00910 }
00911 
00912 
00913 /*************************************************************************
00914         Set the native resolution for this Font
00915 *************************************************************************/
00916 void Font::setNativeResolution(const Size& size)
00917 {
00918         d_nativeHorzRes = size.d_width;
00919         d_nativeVertRes = size.d_height;
00920 
00921         // set native resolution for underlying imageset for bitmap fonts
00922         if ((!d_freetype) && (d_glyph_images != NULL))
00923         {
00924                 d_glyph_images->setNativeResolution(size);
00925         }
00926 
00927         // re-calculate scaling factors & notify images as required
00928         notifyScreenResolution(System::getSingleton().getRenderer()->getSize());
00929 }
00930 
00931 
00932 /*************************************************************************
00933         Notify the Font of the current (usually new) display resolution.
00934 *************************************************************************/
00935 void Font::notifyScreenResolution(const Size& size)
00936 {
00937         // notification should come from System which would have notified the Imageset anyway, but
00938         // in case client code decides to call us, we must pass on notification here too.
00939         if (d_glyph_images)
00940         {
00941                 d_glyph_images->notifyScreenResolution(size);
00942         }
00943 
00944         d_horzScaling = size.d_width / d_nativeHorzRes;
00945         d_vertScaling = size.d_height / d_nativeVertRes;
00946 
00947         if (d_autoScale)
00948         {
00949                 updateFontScaling();
00950         }
00951 
00952 }
00953 
00954 
00955 /*************************************************************************
00956         Enable or disable auto-scaling for this Font.
00957 *************************************************************************/
00958 void Font::setAutoScalingEnabled(bool setting)
00959 {
00960         if (setting != d_autoScale)
00961         {
00962                 if ((!d_freetype) && (d_glyph_images != NULL))
00963                 {
00964                         d_glyph_images->setAutoScalingEnabled(setting);
00965                 }
00966 
00967                 d_autoScale = setting;
00968                 updateFontScaling();
00969         }
00970 
00971 }
00972 
00973 
00974 /*************************************************************************
00975         Update the font as required according to the current scaling
00976 *************************************************************************/
00977 void Font::updateFontScaling(void)
00978 {
00979         if (d_freetype)
00980         {
00981                 uint hdpi = System::getSingleton().getRenderer()->getHorzScreenDPI();
00982                 uint vdpi = System::getSingleton().getRenderer()->getVertScreenDPI();
00983 
00984                 createFontFromFT_Face(d_ptSize, hdpi, vdpi);
00985         }
00986         // bitmapped font
00987         else
00988         {
00989                 float hscale = d_autoScale ? d_horzScaling : 1.0f;
00990 
00991                 // perform update on font mapping advance values
00992                 CodepointMap::iterator pos = d_cp_map.begin(), end = d_cp_map.end();
00993                 for (; pos != end; ++pos)
00994                 {
00995                         pos->second.d_horz_advance = (uint)(((float)pos->second.d_horz_advance_unscaled) * hscale);
00996                 }
00997 
00998                 // re-calculate height
00999                 calculateStaticVertSpacing();
01000         }
01001 
01002 }
01003 
01004 
01005 /*************************************************************************
01006         Set the size of the free-type font (via d_impldat->fontFace which should already
01007         be setup) and render the glyphs in d_glyphset.
01008 *************************************************************************/
01009 void Font::createFontFromFT_Face(uint size, uint horzDpi, uint vertDpi)
01010 {
01011         if (d_autoScale)
01012         {
01013                 horzDpi = (uint)(((float)horzDpi) * d_horzScaling);
01014                 vertDpi = (uint)(((float)vertDpi) * d_vertScaling);
01015         }
01016 
01017         d_ptSize = size;
01018 
01019         if (FT_Set_Char_Size(d_impldat->fontFace, 0, d_ptSize * 64, horzDpi, vertDpi) == 0)
01020         {
01021                 defineFontGlyphs_impl();
01022         }
01023         // failed to set size for font
01024         else
01025         {
01026                 throw GenericException((utf8*)"Font::createFontFromFT_Face - An error occurred while creating a source font with the requested size.");
01027         }
01028 
01029 }
01030 
01031 
01032 /*************************************************************************
01033         Return the number of lines the given text would be formatted to.        
01034 *************************************************************************/
01035 size_t Font::getFormattedLineCount(const String& text, const Rect& format_area, TextFormatting fmt, float x_scale) const
01036 {
01037         // handle simple non-wrapped cases.
01038         if ((fmt == LeftAligned) || (fmt == Centred) || (fmt == RightAligned) || (fmt == Justified))
01039         {
01040                 return std::count(text.begin(), text.end(), static_cast<utf8>('\n')) + 1;
01041         }
01042 
01043         // handle wraping cases
01044         size_t lineStart = 0, lineEnd = 0;
01045         String  sourceLine;
01046 
01047         float   wrap_width = format_area.getWidth();
01048         String  whitespace = TextUtils::DefaultWhitespace;
01049         String  thisLine, thisWord;
01050         size_t  line_count = 0, currpos = 0;
01051 
01052         while (lineEnd < text.length())
01053         {
01054                 if ((lineEnd = text.find_first_of('\n', lineStart)) == String::npos)
01055                 {
01056                         lineEnd = text.length();
01057                 }
01058 
01059                 sourceLine = text.substr(lineStart, lineEnd - lineStart);
01060                 lineStart = lineEnd + 1;
01061 
01062                 // get first word.
01063                 currpos = getNextWord(sourceLine, 0, thisLine);
01064 
01065                 // while there are words left in the string...
01066                 while (String::npos != sourceLine.find_first_not_of(whitespace, currpos))
01067                 {
01068                         // get next word of the string...
01069                         currpos += getNextWord(sourceLine, currpos, thisWord);
01070 
01071                         // if the new word would make the string too long
01072                         if ((getTextExtent(thisLine, x_scale) + getTextExtent(thisWord, x_scale)) > wrap_width)
01073                         {
01074                                 // too long, so that's another line of text
01075                                 line_count++;
01076 
01077                                 // remove whitespace from next word - it will form start of next line
01078                                 thisWord = thisWord.substr(thisWord.find_first_not_of(whitespace));
01079 
01080                                 // reset for a new line.
01081                                 thisLine.clear();
01082                         }
01083 
01084                         // add the next word to the line
01085                         thisLine += thisWord;
01086                 }
01087 
01088                 // plus one for final line
01089                 line_count++;
01090         }
01091 
01092         return line_count;
01093 }
01094 
01095 
01096 /*************************************************************************
01097         Return whether this font is anti-aliased or not.
01098 *************************************************************************/
01099 bool Font::isAntiAliased(void) const
01100 {
01101         return d_freetype ? d_antiAliased : false;
01102 }
01103 
01104 
01105 /*************************************************************************
01106         Set whether the font is anti-aliased or not.
01107 *************************************************************************/
01108 void Font::setAntiAliased(bool setting)
01109 {
01110         if (d_freetype && (d_antiAliased != setting))
01111         {
01112                 d_antiAliased = setting;
01113 
01114                 // regenerate font
01115                 createFontFromFT_Face(d_ptSize, System::getSingleton().getRenderer()->getHorzScreenDPI(), System::getSingleton().getRenderer()->getVertScreenDPI());
01116         }
01117 
01118 }
01119 
01120 
01121 /*************************************************************************
01122         Return the horizontal pixel extent given text would be formatted to.    
01123 *************************************************************************/
01124 float Font::getFormattedTextExtent(const String& text, const Rect& format_area, TextFormatting fmt, float x_scale) const
01125 {
01126         float lineWidth;
01127         float widest = 0;
01128 
01129         size_t lineStart = 0, lineEnd = 0;
01130         String  currLine;
01131 
01132         while (lineEnd < text.length())
01133         {
01134                 if ((lineEnd = text.find_first_of('\n', lineStart)) == String::npos)
01135                 {
01136                         lineEnd = text.length();
01137                 }
01138 
01139                 currLine = text.substr(lineStart, lineEnd - lineStart);
01140                 lineStart = lineEnd + 1;        // +1 to skip \n char
01141 
01142                 switch(fmt)
01143                 {
01144                 case Centred:
01145                 case RightAligned:
01146                 case LeftAligned:
01147                         lineWidth = getTextExtent(currLine, x_scale);
01148                         break;
01149 
01150                 case Justified:
01151                         // usually we use the width of the rect but we have to ensure the current line is not wider than that
01152                         lineWidth = ceguimax(format_area.getWidth(), getTextExtent(currLine, x_scale));
01153                         break;
01154 
01155                 case WordWrapLeftAligned:
01156                 case WordWrapRightAligned:
01157                 case WordWrapCentred:
01158                         lineWidth = getWrappedTextExtent(currLine, format_area.getWidth(), x_scale);
01159                         break;
01160 
01161                 case WordWrapJustified:
01162                         // same as above
01163                         lineWidth = ceguimax(format_area.getWidth(), getWrappedTextExtent(currLine, format_area.getWidth(), x_scale));
01164                         break;
01165 
01166                 default:
01167                         throw InvalidRequestException((utf8*)"Font::getFormattedTextExtent - Unknown or unsupported TextFormatting value specified.");
01168                 }
01169 
01170                 if (lineWidth > widest)
01171                 {
01172                         widest = lineWidth;
01173                 }
01174 
01175         }
01176 
01177         return widest;
01178 }
01179 
01180 
01181 /*************************************************************************
01182         returns extent of widest line of wrapped text.
01183 *************************************************************************/
01184 float Font::getWrappedTextExtent(const String& text, float wrapWidth, float x_scale) const
01185 {
01186         String  whitespace = TextUtils::DefaultWhitespace;
01187         String  thisWord;
01188         size_t  currpos;
01189         float   lineWidth, wordWidth;
01190         float   widest = 0;
01191 
01192         // get first word.
01193         currpos = getNextWord(text, 0, thisWord);
01194         lineWidth = getTextExtent(thisWord, x_scale);
01195 
01196         // while there are words left in the string...
01197         while (String::npos != text.find_first_not_of(whitespace, currpos)) {
01198                 // get next word of the string...
01199                 currpos += getNextWord(text, currpos, thisWord);
01200                 wordWidth = getTextExtent(thisWord, x_scale);
01201 
01202                 // if the new word would make the string too long
01203                 if ((lineWidth + wordWidth) > wrapWidth) {
01204                         
01205                         if (lineWidth > widest)
01206                         {
01207                                 widest = lineWidth;
01208                         }
01209 
01210                         // remove whitespace from next word - it will form start of next line
01211                         thisWord = thisWord.substr(thisWord.find_first_not_of(whitespace));
01212                         wordWidth = getTextExtent(thisWord, x_scale);
01213 
01214                         // reset for a new line.
01215                         lineWidth = 0;
01216                 }
01217 
01218                 // add the next word to the line
01219                 lineWidth += wordWidth;
01220         }
01221 
01222         if (lineWidth > widest)
01223         {
01224                 widest = lineWidth;
01225         }
01226 
01227         return widest;
01228 }
01229 
01230 
01231 /*************************************************************************
01232         Return a String object that contains the code-points that the font
01233         is currently configured to render.
01234 *************************************************************************/
01235 const String& Font::getAvailableGlyphs(void) const
01236 {
01237         return d_glyphset;
01238 }
01239 
01240 
01241 /*************************************************************************
01242         Return the point size of a dynamic (ttf based) font.
01243 *************************************************************************/
01244 uint Font::getPointSize(void) const
01245 {
01246         if (d_freetype)
01247         {
01248                 return d_ptSize;
01249         }
01250         else
01251         {
01252                 throw InvalidRequestException("Font::getPointSize - unable to return point size for a static (bitmap based) font.");
01253         }
01254 }
01255 
01256 
01257 /*************************************************************************
01258     Writes an xml representation of this Font to \a out_stream.
01259 *************************************************************************/
01260 void Font::writeXMLToStream(OutStream& out_stream) const
01261 {
01262     // output starting <Font ... > element
01263     out_stream << "<Font Name=\"" << d_name << "\" Filename=\"" << d_sourceFilename << "\" ";
01264 
01265     if (d_freetype)
01266         out_stream << "Size=\"" << d_ptSize << "\" ";
01267 
01268     if (d_nativeHorzRes != DefaultNativeHorzRes)
01269         out_stream << "NativeHorzRes=\"" << static_cast<uint>(d_nativeHorzRes) << "\" ";
01270 
01271     if (d_nativeVertRes != DefaultNativeVertRes)
01272         out_stream << "NativeVertRes=\"" << static_cast<uint>(d_nativeVertRes) << "\" ";
01273 
01274     if (d_autoScale)
01275         out_stream << "AutoScaled=\"True\" ";
01276 
01277     out_stream << ">" << std::endl;
01278 
01279     // dynamic font so output defined glyphs
01280     if (d_freetype)
01281     {
01282         size_t start = 0, idx = 0;
01283 
01284         while(start < d_glyphset.length())
01285         {
01286             // find end of range
01287             while ((idx + 1 < d_glyphset.length()) && (d_glyphset[idx] + 1 == d_glyphset[idx + 1]))
01288                 ++idx;
01289 
01290             if (start == idx)
01291                 // if range is a just a single codepoint
01292                 out_stream << "<Glyph Codepoint=\"" << d_glyphset[start] << "\" />" << std::endl;
01293             else
01294                 // range contains >1 codepoint
01295                 out_stream << "<GlyphRange StartCodepoint=\"" << d_glyphset[start] << "\" EndCodepoint=\"" << d_glyphset[idx] << "\" />" << std::endl;
01296 
01297             start = ++idx;
01298         }
01299     }
01300     // static font, so output glyph to imageset mappings
01301     else
01302     {
01303         for (CodepointMap::const_iterator iter = d_cp_map.begin(); iter != d_cp_map.end(); ++iter)
01304         {
01305             out_stream << "<Mapping Codepoint=\"" << (*iter).first << "\" Image=\"" << (*iter).second.d_image->getName() << "\" ";
01306 
01307             if ((*iter).second.d_horz_advance_unscaled != -1)
01308                 out_stream << "HorzAdvance=\"" << (*iter).second.d_horz_advance_unscaled << "\" ";
01309 
01310             out_stream << "/>" << std::endl;
01311         }
01312     }
01313 
01314     // output closing </Font> element.
01315     out_stream << "</Font>" << std::endl;
01316 }
01317 
01318 
01319 } // End of  CEGUI namespace section

Generated on Wed Sep 7 09:56:31 2005 for Crazy Eddies GUI System by  doxygen 1.4.3