MercuryText.cpp

Go to the documentation of this file.
00001 // FreeType Headers
00002 #include <ft2build.h>
00003 #include <freetype/freetype.h>
00004 #include <freetype/ftglyph.h>
00005 #include <freetype/ftoutln.h>
00006 #include <freetype/fttrigon.h>
00007 
00008 #include "global.h"
00009 #include "MercuryUtil.h"
00010 #include "MercuryText.h"
00011 #include "MercuryLog.h"
00012 #include "MercuryDisplay.h"
00013 #include "MercuryInput.h"
00014 #include "MercuryTextureManager.h"
00015 #include "MercuryFiles.h"
00016 #include "MercuryObjectFactory.h"
00017 #include "MercuryVector.h"
00018 #include "BMPWriter.h"
00019 
00020 GlobalFontHash FONTHASH;
00021 
00022 #define     MC_DISPLAYRESIZED       1       //convention: MESSAGE CODE  
00023 
00024 MercuryText::MercuryText()
00025 :MercuryObject()
00026 {
00027     m_glState.Disable(MGLS_ALL);
00028     m_glState.Enable(MGLS_COLORWRITE | MGLS_BLEND | MGLS_TEXTURING | MGLS_OPAQUE);
00029 }
00030 
00031 MercuryText::~MercuryText()
00032 {
00033     Clear();
00034 }
00035 
00036 void MercuryText::Init()
00037 {
00038     MercuryObject::Init();
00039 
00040     m_h = m_fontheight = 16;
00041     m_tabwidth = 64;
00042     m_maxwidth = -1;
00043 
00044     m_width = 0;
00045     m_fontFileData = NULL;
00046     m_mtgd = NULL;
00047     SetupSizing(DISPLAY->GetDimensions());
00048 
00049     RegisterMessage( MC_DISPLAYRESIZED, "DisplayResized" );
00050 }
00051 
00052 void MercuryText::SetSize(unsigned int px)
00053 {
00054     m_fontheight = px;
00055     SetupSizing(DISPLAY->GetDimensions());
00056 }
00057 
00058 void MercuryText::SetFont(const MString& font)
00059 {
00060     if( m_font == font)
00061         return;
00062 
00063     m_font = font;
00064 }
00065 
00066 bool MercuryText::New_Face(FT_Library library, const MString& filepathname, FT_Long face_index, FT_Face *aface )
00067 {
00068     FT_Open_Args_ a;
00069     unsigned int length;
00070 
00071     MercuryFile* f = FILEMAN.Open(filepathname);
00072 
00073     if(!f)
00074         return false;
00075 
00076     length = f->Length();
00077     SAFE_DELETE(m_fontFileData);
00078     m_fontFileData = new char[length];
00079     f->Read(m_fontFileData, length);
00080     SAFE_DELETE(f);
00081     a.flags = FT_OPEN_MEMORY;
00082     a.memory_base = (const FT_Byte*)m_fontFileData;
00083     a.memory_size = length;
00084 
00085     FT_Open_Face(library, &a, face_index, aface);
00086 
00087     return true;
00088 };
00089 
00090 void MercuryText::Clear()
00091 {
00092 //  for (unsigned int i = 0; i < m_characters.size(); i++)
00093 //      SAFE_DELETE(m_characters[i]);
00094 
00095     m_characters.clear();
00096     m_width = 0;
00097 }
00098 
00099 void MercuryText::SetText(const MString& text)
00100 {
00101     if (m_text == text)
00102         return;
00103 
00104     m_text = text;
00105     BuildFontDisplay();
00106 }
00107 
00108 MercuryTextChar* MercuryText::BuildCharSprite(const char ch, int& x, int& y)
00109 {
00110     MercuryTextChar* textChar = NULL;
00111 
00112     if (!m_mtgd)
00113         BuildGlyphData();
00114 
00115     textChar = new MercuryTextChar;
00116 
00117     MercuryTextCharData mtcd = m_mtgd->charDatas[ int(ch) ];
00118 
00119     textChar->vertices[0].x = 0;
00120     textChar->vertices[0].y = 0;
00121     textChar->vertices[0].SetU( mtcd.left_u );
00122     textChar->vertices[0].SetV( mtcd.top_v );
00123 
00124     textChar->vertices[1].x = 0;
00125     textChar->vertices[1].y = float(mtcd.height_px);
00126     textChar->vertices[1].SetU( mtcd.left_u );
00127     textChar->vertices[1].SetV( mtcd.bottom_v );
00128 
00129     textChar->vertices[2].x = float(mtcd.width_px);
00130     textChar->vertices[2].y = float(mtcd.height_px);
00131     textChar->vertices[2].SetU( mtcd.right_u );
00132     textChar->vertices[2].SetV( mtcd.bottom_v );
00133 
00134     textChar->vertices[3].x = float(mtcd.width_px);
00135     textChar->vertices[3].y = 0;
00136     textChar->vertices[3].SetU( mtcd.right_u );
00137     textChar->vertices[3].SetV( mtcd.top_v );
00138 
00139     textChar->advance_x = mtcd.advance_x;
00140     textChar->advance_y = mtcd.advance_y;
00141     textChar->position.x = float(mtcd.left_px);
00142     textChar->position.y = float(m_mtgd->height_px-mtcd.top_px);
00143 
00144     return textChar;
00145 }
00146 
00147 void MercuryText::BuildFontDisplay()
00148 {
00149     unsigned int i;
00150     MercuryTextChar* letter = NULL;
00151     int w_place = 0;
00152 
00153     if ( m_font.empty() )
00154         return;
00155 
00156     m_width = 0;
00157 
00158     Clear();
00159 
00160     MVector<LetterHolder> vTempSprites;
00161 
00162     int x, y;
00163     x = y = 0;
00164     for ( i = 0; i < m_text.size(); ++i)
00165     {
00166         if( m_text[i] == '\n' )
00167         {
00168             vTempSprites.push_back( LetterHolder( 0, 0, 0, 0, 0, 0, 1 ) );
00169             continue;
00170         }
00171         if( m_text[i] == '\0' || m_text[i] == '\r' )
00172             continue;
00173         letter = BuildCharSprite(m_text[i], x, y);
00174         if (letter != NULL)
00175         {
00176             m_characters.push_back(letter);
00177             vTempSprites.push_back( LetterHolder( letter, 
00178                 letter->advance_x,
00179                 0, 0, (m_text[i] == ' '), (m_text[i] == '\t'), 0 ) );
00180         }
00181     }
00182 
00183     //Ok, now we have a vector of all of the sprites, vTempSprites.  We just have to
00184     //move them to the right place.
00185 
00186     int iCurLine = 0;
00187     int iLastLineBreak = 0;
00188 
00189     for( i = 0; i < vTempSprites.size(); ++i )
00190     {
00191         LetterHolder &cur = vTempSprites[i];
00192         
00193         cur.iPixCol = w_place;
00194         w_place += cur.iWidth;
00195         cur.iLine = iCurLine;
00196 
00197         if( cur.bEnter )
00198         {
00199             ++iCurLine;
00200             m_width = (m_width>w_place)?m_width:w_place;
00201             iLastLineBreak = i;
00202             w_place = 0;
00203             continue;
00204         }
00205 
00206         //We've gone over the line's maximum width
00207         if( (w_place > m_maxwidth) && (m_maxwidth > 0) )
00208         {
00209             int j;
00210             for( j = i; j > iLastLineBreak; --j )
00211             {
00212                 //let's see if we've found somewhere that we can split
00213                 if( vTempSprites[j].bTab || vTempSprites[j].bSpace )
00214                     break;
00215             }
00216 
00217             if( j == iLastLineBreak )
00218             {
00219                 //We couldn't find any breaks; chop this character onto next line
00220                 ++iCurLine;
00221                 iLastLineBreak = i;
00222                 m_width = (m_width>w_place)?m_width:w_place;
00223                 cur.iLine = iCurLine;
00224                 w_place = 0;
00225                 cur.iPixCol = w_place;
00226                 w_place = cur.iWidth;
00227             } else {
00228                 //otherwise, we found a suitable chop spot.
00229                 ++iCurLine;
00230                 iLastLineBreak = j;
00231                 m_width = (m_width>w_place)?m_width:w_place;
00232                 cur.iLine = iCurLine;
00233                 w_place = 0;
00234                 for( unsigned k = j+1; k <= i; ++k )
00235                 {
00236                     vTempSprites[k].iLine = iCurLine;
00237                     vTempSprites[k].iPixCol = w_place;
00238                     w_place += vTempSprites[k].iWidth;
00239                 }
00240             }
00241         }
00242 
00243         if( cur.bTab )
00244         {
00245             int iNextTabPlace = w_place/m_tabwidth+m_tabwidth;
00246             if ( (iNextTabPlace > m_maxwidth) && (m_maxwidth > 0) )
00247             {
00248                 m_width = (m_width>w_place)?m_width:w_place;
00249                 ++iCurLine;
00250                 iLastLineBreak = i;
00251                 w_place = 0;
00252                 w_place = cur.iWidth;
00253             } else
00254                 w_place = iNextTabPlace;
00255         }
00256     }
00257 
00258     m_width = (m_width>w_place)?m_width:w_place;
00259 
00260     for( i = 0; i < vTempSprites.size(); ++i )
00261     {
00262         LetterHolder cur = vTempSprites[i];
00263         if( cur.pSprite )
00264         {
00265             cur.pSprite->position.x += ( float( cur.iPixCol ) );
00266             cur.pSprite->position.y += ( float( cur.iLine * m_fontheight * 1.5 ) );
00267         }
00268         if( i == vTempSprites.size() - 1 ) 
00269             m_height = (int)((cur.iLine+1) * m_fontheight * 1.5);
00270     }
00271 }
00272 
00273 void MercuryText::Message( int Message, PStack & data, const MString & name )
00274 {
00275     switch ( Message ) 
00276     {
00277     case MC_DISPLAYRESIZED:
00278         DisplayDimensions g;
00279         g.height = data.PopItem().GetValueF();
00280         g.width = data.PopItem().GetValueF();
00281         SetupSizing(g);
00282         break;
00283     };
00284 }
00285 
00286 void MercuryText::SetupSizing(const DisplayDimensions& d)
00287 {
00288     DisplayDimensions based = DISPLAY->GetBasedDimensions();
00289 //  float r_w = based.width / d.width;
00290     float r_h = based.height / d.height;
00291     m_h = (unsigned int)(m_fontheight / r_h);
00292     SetScaleX(r_h);
00293     SetScaleY(r_h);
00294 
00295     m_id = m_font + ssprintf("%d", m_h) + "Glyphs";
00296     m_mtgd = NULL; //Forget our last glyph map so that we either find or generate a new one
00297     //If we have a font we may have rendered something, so we should update it
00298     BuildFontDisplay();
00299 }
00300 
00301 bool MercuryText::Command( PStack & ret, const char * command, PStack & args )
00302 {
00303     if ( strcmp( command, "SetText" ) == 0 )
00304     {
00305         MString Parameter = args.PopItem().GetValueS();
00306         while( args.GetSize() )
00307             Parameter = args.PopItem().GetValueS() + "," + Parameter;
00308         this->SetText( Parameter );
00309         return true;
00310     }
00311     return MercuryObject::Command( ret, command, args );
00312 }
00313 
00314 void MercuryText::EnumerateCommands( MVector< MString > & toAdd )
00315 {
00316     toAdd.push_back( "SetText" );
00317     MercuryObject::EnumerateCommands( toAdd );
00318 }
00319 
00320 void MercuryText::BuildGlyphData()
00321 {
00322     m_mtgd = (*FONTHASH).get(m_id);
00323     if (!m_mtgd)
00324     {
00325         (*FONTHASH).insert(m_id, MercuryTextGlyphData());
00326         m_mtgd = (*FONTHASH).get(m_id);
00327     }
00328     else
00329         return; //glyph already exists
00330 
00331     FT_LibraryRec_* m_library;
00332     FT_FaceRec_* m_face;
00333     FT_Glyph glyph;
00334     FT_BitmapGlyph bitmap_glyph;
00335     int glyph_height;
00336     int glyph_width;
00337     bool restart = true;
00338 //  SAFE_DELETE(m_mtgd->image_data.data);
00339     m_mtgd->image_data = new RawImageData;
00340 
00341     unsigned int image_width_height = 256;
00342 
00343     if (FT_Init_FreeType( &m_library ))
00344     {
00345         LOG.Warn("FT_Init_FreeType failed");
00346         SAFE_DELETE(m_fontFileData);
00347         FT_Done_FreeType(m_library);
00348         throw;
00349     }
00350 
00351     if (!New_Face( m_library, m_font.c_str(), 0, &m_face ))
00352         ASSERT(!"FT_New_Face failed (there is probably a problem with your font file)");
00353 
00354     //Setup the image_data
00355     while ( restart )
00356     {
00357         restart = false;
00358 
00359         m_mtgd->image_data->attrs.m_width = image_width_height;
00360         m_mtgd->image_data->attrs.m_height = image_width_height;
00361         m_mtgd->image_data->attrs.m_ColorByteType = RGBA;
00362         unsigned int bpp = ColorBytesToSize(m_mtgd->image_data->attrs.m_ColorByteType);
00363         unsigned int image_size = image_width_height * image_width_height * bpp;
00364         SAFE_DELETE(m_mtgd->image_data->data);
00365         m_mtgd->image_data->data = new unsigned char[image_size];
00366         m_mtgd->image_data->attrs.m_dpi_x = 72;
00367         m_mtgd->image_data->attrs.m_dpi_y = 72;
00368         memset(m_mtgd->image_data->data, 0, image_size); //Make the bitmap white
00369 
00370         m_mtgd->height_px = m_h;
00371         m_mtgd->point_size = m_h*64;
00372         FT_Set_Char_Size( m_face, m_mtgd->point_size, m_mtgd->point_size, 96, 96);
00373 
00374         // Load The Glyph For Our Character.
00375         for (unsigned int ch = 32; ch < 256; ++ch)
00376         {
00377             if(FT_Load_Glyph( m_face, FT_Get_Char_Index( m_face, ch ), FT_LOAD_DEFAULT ))
00378             {
00379                 LOG.Warn("FT_Load_Glyph failed");
00380                 FT_Done_Face(m_face);
00381                 SAFE_DELETE(m_fontFileData);
00382                 FT_Done_FreeType(m_library);
00383                 return;
00384             }
00385             
00386             // Move The Face's Glyph Into A Glyph Object.
00387             if(FT_Get_Glyph( m_face->glyph, &glyph))
00388             {
00389                 LOG.Warn("FT_Get_Glyph failed");
00390                 FT_Done_Face(m_face);
00391                 SAFE_DELETE(m_fontFileData);
00392                 FT_Done_FreeType(m_library);
00393                 return;
00394             }
00395             
00396             FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 );
00397             bitmap_glyph = (FT_BitmapGlyph)glyph;
00398             
00399             // This Reference Will Make Accessing The Bitmap Easier.
00400             FT_Bitmap& bitmap = bitmap_glyph->bitmap;
00401 
00402             //Build from the bottom up, not the top down.
00403             //FT bitmap data is 8 bits per pixel we must expand to 16 bits per pixel
00404             int xCell = ch % 16;
00405             int yCell = (int)floorf(ch/16.0f);
00406 
00407             int xPixelsPerCell = (image_width_height/16);
00408             int yPixelsPerCell = (image_width_height/16);
00409 
00410             int xByteOffset = (xPixelsPerCell*bpp) * xCell;
00411             int yByteOffset = ((image_width_height*bpp) * yPixelsPerCell) * yCell;
00412 
00413             m_mtgd->charDatas[ch].left_u = (xCell * xPixelsPerCell)/float(image_width_height);
00414             m_mtgd->charDatas[ch].right_u = ( (xCell * xPixelsPerCell)+bitmap.width )/float(image_width_height);
00415             m_mtgd->charDatas[ch].top_v = (yCell * yPixelsPerCell)/float(image_width_height);
00416             m_mtgd->charDatas[ch].bottom_v = ( (yCell * yPixelsPerCell)+bitmap.rows )/float(image_width_height);
00417             m_mtgd->charDatas[ch].left_px = bitmap_glyph->left;
00418             m_mtgd->charDatas[ch].top_px = bitmap_glyph->top;
00419             m_mtgd->charDatas[ch].advance_x = m_face->glyph->advance.x/64;
00420             m_mtgd->charDatas[ch].advance_y = m_face->glyph->advance.y/64;
00421             m_mtgd->charDatas[ch].width_px = glyph_width = bitmap.width;
00422             m_mtgd->charDatas[ch].height_px = glyph_height = bitmap.rows;
00423 
00424             //If a character goes outside its cell, we need a bigger texture
00425             if ((glyph_width > xPixelsPerCell) || (glyph_height > yPixelsPerCell))
00426                 restart = true;
00427 
00428             if (restart)
00429             {
00430                 image_width_height = nextPow2(image_width_height);
00431                 break;
00432             }
00433 
00434             for(int j = 0; j < glyph_height; ++j)
00435                 for(int i = 0; i < glyph_width; ++i)
00436                 {
00437                     unsigned int color = bitmap.buffer[i + glyph_width*(j)];
00438                     unsigned int bytePosition = (yByteOffset) + xByteOffset + ((bpp*(i+j*image_width_height)));
00439 
00440                     m_mtgd->image_data->data[ bytePosition + 0] = 255;
00441                     m_mtgd->image_data->data[ bytePosition + 1] = 255;
00442                     m_mtgd->image_data->data[ bytePosition + 2] = 255;
00443                     m_mtgd->image_data->data[ bytePosition + 3] = color;
00444                 }
00445     /*
00446             //show the grid
00447             for(unsigned int j = 0; j < image_height; ++j)
00448                 for(unsigned int i = 0; i < image_width; ++i)
00449                     if ((j%(image_width/16) == 0) || (i%(image_height/16) == 0))
00450                     {
00451                         unsigned int bytePosition = bpp*(i+j*image_width);
00452                         m_mtgd->image_data->data[ bytePosition + 0] = 0;
00453                         m_mtgd->image_data->data[ bytePosition + 1] = 0;
00454                         m_mtgd->image_data->data[ bytePosition + 2] = 255;
00455                     }
00456                     */
00457         }
00458     }
00459 
00460     m_mtgd->textureData.SetPath(m_id);
00461     m_mtgd->textureData.SendLoadFromRaw(m_mtgd->image_data); //takes owner of image_data
00462 
00463 //  WriteBMP("TestGlyph.bmp", m_mtgd->image_data);
00464 
00465     FT_Done_Face(m_face);
00466     SAFE_DELETE(m_fontFileData);
00467     FT_Done_FreeType(m_library);
00468 }
00469 
00470 void MercuryText::CustomRender()
00471 {
00472     if (m_mtgd)
00473     {
00474         if (m_mtgd->textureData.GetState() == LOADED)
00475         {
00476             DISPLAY->SetStates( GetInheritedGLState() );
00477             DISPLAY->SetMaterial(&m_finalMaterial);
00478             DISPLAY->SendMatrixData(GetFinalMatrix());
00479             DISPLAY->RenderText(*this);
00480         }
00481     }
00482 }
00483 
00484 REGISTER_OBJECT_TYPE( MercuryText );
00485 
00486 /* 
00487  * Copyright (c) 2005-2006, Joshua Allen
00488  * All rights reserved.
00489  *
00490  * Redistribution and use in source and binary forms, with or
00491  * without modification, are permitted provided that the following
00492  * conditions are met:
00493  *  -   Redistributions of source code must retain the above
00494  *      copyright notice, this list of conditions and the following disclaimer.
00495  *  -   Redistributions in binary form must reproduce the above copyright
00496  *      notice, this list of conditions and the following disclaimer in
00497  *      the documentation and/or other materials provided with the distribution.
00498  *  -   Neither the name of the Mercury Engine nor the names of its
00499  *      contributors may be used to endorse or promote products derived from
00500  *      this software without specific prior written permission.
00501  *
00502  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00503  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00504  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00505  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
00506  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00507  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00508  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00509  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00510  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00511  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00512  */

Hosted by SourceForge.net Logo