00001
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
00093
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
00184
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
00207 if( (w_place > m_maxwidth) && (m_maxwidth > 0) )
00208 {
00209 int j;
00210 for( j = i; j > iLastLineBreak; --j )
00211 {
00212
00213 if( vTempSprites[j].bTab || vTempSprites[j].bSpace )
00214 break;
00215 }
00216
00217 if( j == iLastLineBreak )
00218 {
00219
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
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
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;
00297
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;
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
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
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);
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
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
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
00400 FT_Bitmap& bitmap = bitmap_glyph->bitmap;
00401
00402
00403
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
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
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457 }
00458 }
00459
00460 m_mtgd->textureData.SetPath(m_id);
00461 m_mtgd->textureData.SendLoadFromRaw(m_mtgd->image_data);
00462
00463
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
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512