0023418: Crash on the object displaying when running DRAW on remote station. OpenGL...
[occt.git] / src / OpenGl / OpenGl_FontMgr.cxx
1 // Copyright (c) 1999-2012 OPEN CASCADE SAS
2 //
3 // The content of this file is subject to the Open CASCADE Technology Public
4 // License Version 6.5 (the "License"). You may not use the content of this file
5 // except in compliance with the License. Please obtain a copy of the License
6 // at http://www.opencascade.org and read it completely before using this file.
7 //
8 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
9 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
10 //
11 // The Original Code and all software distributed under the License is
12 // distributed on an "AS IS" basis, without warranty of any kind, and the
13 // Initial Developer hereby disclaims all such warranties, including without
14 // limitation, any warranties of merchantability, fitness for a particular
15 // purpose or non-infringement. Please see the License for the specific terms
16 // and conditions governing the rights and limitations under the License.
17
18 #include <OpenGl_FontMgr.hxx>
19 #include <OpenGl_GlCore11.hxx>
20
21 #include <Standard_Stream.hxx>
22
23 #include <ft2build.h>
24
25 #ifdef _MSC_VER
26 #pragma comment( lib, "ftgl.lib" )
27 #pragma comment( lib, "freetype.lib" )
28 #endif
29
30 #undef TRACE
31
32 #define DEFAULT_FONT_HEIGHT 16
33
34 float h_scale = 0;
35
36 void dump_texture( int id);
37
38 class Font_DataMap:
39   public NCollection_DataMap< Handle(TCollection_HAsciiString),
40   Handle(TCollection_HAsciiString)>
41 {
42 public:
43   inline Font_DataMap
44     (const Standard_Integer NbBuckets = 1,
45     const Handle(NCollection_BaseAllocator)& theAllocator = 0L)
46     :  NCollection_DataMap<Handle(TCollection_HAsciiString),
47     Handle(TCollection_HAsciiString)> (NbBuckets, theAllocator)
48   {}
49
50   inline Font_DataMap (const Font_DataMap& theOther)
51     :  NCollection_DataMap<Handle(TCollection_HAsciiString),
52     Handle(TCollection_HAsciiString)> (theOther)
53   {}  
54   friend Standard_Boolean IsEqual( const Handle(TCollection_HAsciiString)& h1,
55     const Handle(TCollection_HAsciiString)& h2 );
56 };
57 inline Standard_Boolean
58 IsEqual( const Handle(TCollection_HAsciiString)& h1,
59         const Handle(TCollection_HAsciiString)& h2 )
60 {
61   return (!h1->IsLess(h2) && !h1->IsGreater(h2));
62 }
63
64 OpenGl_FontMgr::OpenGl_FontMgr()
65 : _CurrentFontId(-1),
66 _XCurrentScale(1.f),
67 _YCurrentScale(1.f) 
68 {
69 }
70
71 OpenGl_FontMgr* OpenGl_FontMgr::instance() 
72 {
73   static OpenGl_FontMgr* _mgr = NULL;
74   if ( _mgr == NULL )
75   {
76     _mgr = new OpenGl_FontMgr();
77     _mgr->_initializeFontDB();
78   }
79   return _mgr;
80 }
81
82 void OpenGl_FontMgr::_initializeFontDB()
83 {
84   Handle(Font_FontMgr) fntMgr = Font_FontMgr::GetInstance();
85   if ( !fntMgr.IsNull() ) {
86
87     Font_NListOfSystemFont fontList = fntMgr->GetAvalableFonts();
88     if ( fontList.Size() != 0 ) {
89
90       // The library used as a tool for checking font aspect since Font_FontMgr
91       // fails to get aspect for the fonts that have name dependant
92       // on system locale.
93       FT_Library aFtLibrary;
94       FT_Error aLibError = FT_Init_FreeType(&aFtLibrary);
95       
96       Font_NListOfSystemFont::Iterator it(fontList);
97       for ( ; it.More(); it.Next() ) {
98         OGLFont_SysInfo* info = new OGLFont_SysInfo();
99         if ( it.Value()->FontAspect() == Font_FA_Regular ) {
100           
101           Handle(TCollection_HAsciiString) aFontPath = it.Value()->FontPath();
102             
103           //this workaround for fonts with names dependent on system locale.
104           //for example: "Times New Roman Fett Kursive" or "Times New Roman Gras Italiqui"
105           FT_Face aFontFace;
106           FT_Error aFaceError = FT_New_Face(aFtLibrary,
107                                             aFontPath->ToCString(), 0,
108                                             &aFontFace);
109               
110           if ( aFaceError == FT_Err_Ok ) {
111             if ( aFontFace->style_flags == 0 ) {
112               info->SysFont = it.Value();
113             }
114             else {
115               //new aspect detected for current font item
116 #ifdef TRACE
117               cout << "TKOpenGl::initializeFontDB() detected new font!\n"
118                 << "\tFont Previous Name: " << it.Value()->FontName()->ToCString() << endl
119                 << "\tFont New Name: " << aFontFace->family_name << endl
120                 << "\tFont Aspect: " << aFontFace->style_flags << endl;
121 #endif
122               Font_FontAspect aspect = Font_FA_Regular;
123               if ( aFontFace->style_flags == (FT_STYLE_FLAG_ITALIC | FT_STYLE_FLAG_BOLD) )
124                 aspect = Font_FA_BoldItalic;
125               else if ( aFontFace->style_flags == FT_STYLE_FLAG_ITALIC )
126                 aspect = Font_FA_Italic;
127               else if ( aFontFace->style_flags == FT_STYLE_FLAG_BOLD )
128                 aspect = Font_FA_Bold;
129
130 #ifdef TRACE
131               cout << "\tFont_FontAspect: " << aspect << endl;
132 #endif
133               Handle(TCollection_HAsciiString) aFontName =
134                 new TCollection_HAsciiString( aFontFace->family_name );
135               info->SysFont = new Font_SystemFont( aFontName, aspect, aFontPath );
136             }
137             
138             FT_Done_Face(aFontFace);
139           }
140           else
141             continue;
142         } else {
143           info->SysFont = it.Value();
144         }
145         _FontDB.Append(info);
146
147       }
148       
149       // finalize library instance
150       if ( aLibError == FT_Err_Ok )
151       {
152         FT_Done_FreeType(aFtLibrary);
153       }
154     }
155   }
156
157 #ifdef TRACE
158   if ( !_FontDB.Size() ) {
159     cout << "\nTKOpenGl::initializeFontDB() FAILED!!!\n"
160       << "No fonts detected in the system!\n"
161       << "Text rendering in 3D viewer will not be available." << endl;
162   }
163 #endif    
164 }
165
166 bool OpenGl_FontMgr::requestFontList( Graphic3d_NListOfHAsciiString& lst)
167 {
168   FontDBIt DBit(_FontDB);
169   lst.Clear();
170   for ( ; DBit.More(); DBit.Next() ) {
171     lst.Append( DBit.Value()->SysFont->FontName() );
172   }
173   return true;
174 }
175
176 // Empty fontName means that ANY family name can be used.
177 // fontAspect == Font_FA_Undefined means ANY font aspect is acceptable.
178 // fontheight == -1 means ANY font height is acceptable.
179 int OpenGl_FontMgr::request_font( const Handle(TCollection_HAsciiString)& fontName,
180                                  const Font_FontAspect                   fontAspect,
181                                  const Standard_Integer                  fontHeight )
182 {
183   Standard_Integer aFontHeight = fontHeight;
184   if ( aFontHeight < 2 && aFontHeight != -1 )
185   {
186 #ifdef TRACE
187     cout << "TKOpenGl::request_font\n"
188       << "  Error: font height is invalid!!!\n"
189       << "  font height:" << aFontHeight << "\n";
190 #endif //TRACE
191     return -1;
192   } 
193
194 #ifdef TRACE
195   cout << "TKOpenGl::request_font\n"
196     << "  font name: " << fontName->ToCString() << endl
197     << "  font aspect: " << fontAspect << endl
198     << "  font height: " << aFontHeight << endl;
199 #endif
200
201   GLCONTEXT ctx = GET_GL_CONTEXT();
202
203   //check for font 
204   FontDBIt DBit(_FontDB);
205
206 #ifdef TRACE
207   cout << "Searching font in font database...\n";
208 #endif
209
210   for ( ; DBit.More(); DBit.Next() ) {
211     // san (23.07.2010): comparing font names in case-insensitive mode,
212     // as on SUN and SGI system font names are in lower-case
213     if ( fontName->IsEmpty() || DBit.Value()->SysFont->FontName()->IsSameString(fontName, Standard_False) ) {
214 #ifdef TRACE
215       cout << "\tName is found...\n\tCheking aspect...\n";
216 #endif
217
218       //check for font aspect
219       if (fontAspect != Font_FA_Undefined && DBit.Value()->SysFont->FontAspect() != fontAspect) {
220 #ifdef TRACE
221         cout << "\tAspect of candidate font: " << DBit.Value()->SysFont->FontAspect() << endl;
222         cout << "\tAspects are not equal! Continue seaching...\n";
223 #endif
224         continue;
225       }
226
227 #ifdef TRACE
228       cout << "\tAspect is found...\n\tCheking height...\n";
229 #endif
230       //check for fixed height
231       if (DBit.Value()->SysFont->FontHeight() != -1) {
232 #ifdef TRACE
233         cout << "\tChecking fixed height: " << DBit.Value()->SysFont->FontHeight() << endl;
234 #endif
235         //fixed height font
236         if ( aFontHeight != -1 && DBit.Value()->SysFont->FontHeight() != aFontHeight ){
237 #ifdef TRACE
238           cout << "\tHeights are not equal! Continue seaching...\n";
239 #endif
240           continue;
241         }
242         else
243         {
244           // We need to remember the font height to be used
245           if ( aFontHeight == -1 )
246             aFontHeight = DBit.Value()->SysFont->FontHeight();
247 #ifdef TRACE
248           cout << "\tHeight is found\n";
249 #endif    
250         }
251       }
252       else 
253       {
254         // If font height is not specified -> use DEFAULT_FONT_HEIGHT for variable-height fonts
255         if ( aFontHeight == -1 )
256             aFontHeight = DEFAULT_FONT_HEIGHT;
257 #ifdef TRACE
258         cout << "\tFont has variable height == -1. height is found\n";
259 #endif
260       }
261 #ifdef TRACE
262       cout << "\tChecking font manager cache...\n";
263 #endif  
264       //check in loaded fonts
265       IDList::Iterator fIt(DBit.Value()->GeneratedFonts);
266       for ( ; fIt.More(); fIt.Next() ) {
267         OGLFont_Cache cache = _FontCache.Find( fIt.Value() );
268         if ( cache.FontHeight == aFontHeight && cache.GlContext == ctx ) {
269           //requested font is generated already
270
271 #ifdef TRACE
272           cout << "\tRequested font is generated already\n\tId = "  ;
273           cout << fIt.Value() << endl;
274 #endif                                                    
275           _CurrentFontId = fIt.Value();
276           return _CurrentFontId;
277         } else {
278 #ifdef TRACE
279           cout << "\t\tHeights or contexts are not equal:\n"
280             << "\t\t\tfont height: "  << aFontHeight << "\tchache height: " << cache.FontHeight << endl
281             << "\t\t\tfont context: " << ctx << "\tchache context: " << cache.GlContext << endl;
282           cout << "\t\tContinue searching in cache...\n";
283 #endif
284         }
285       }
286 #ifdef TRACE
287       cout << "\tRequested font is not found among genarated fonts.\n\tCreating new...\n";
288 #endif
289       //if not found in generated fonts
290       //create new
291       FTGLTextureFont* font = new FTGLTextureFont(DBit.Value()->SysFont->FontPath()->ToCString());
292       //by default creates regular font
293       if ( ! font || font->Error() != FT_Err_Ok) {
294 #ifdef TRACE
295         cout << "\t\tError during creation FTGL font object!\n";
296 #endif
297         return -1;
298       }
299
300       if ( ! font->FaceSize( aFontHeight) || font->Error() != FT_Err_Ok ){
301 #ifdef TRACE
302         cout << "\t\tError during setup FTGL font height!\n";
303 #endif
304         return -1;
305       }
306       font->UseDisplayList( false );
307
308       //put font to cache
309       OGLFont_Cache cache;
310       cache.Font = font;
311       cache.FontHeight = aFontHeight;     
312       cache.GlContext = ctx;
313
314       _CurrentFontId =_FontCache.Size() + 1;
315       _FontCache.Bind( _CurrentFontId, cache );
316       DBit.Value()->GeneratedFonts.Append(_CurrentFontId);
317
318 #ifdef TRACE
319       cout << "TKOpenGl::Loaded New FTGL font:\n"
320         << "  font name: " << fontName->ToCString() << "\n"
321         << "  font aspect: ";
322
323       switch( fontAspect ) 
324       {
325       case Font_FA_Bold:
326         cout << "Font_FA_Bold\n";
327         break;
328       case Font_FA_BoldItalic:
329         cout << "Font_FA_BoldItalic\n";
330         break;
331       case Font_FA_Italic:
332         cout << "Font_FA_Italic\n";
333         break;
334       case Font_FA_Regular:
335         cout << "Font_FA_Regular\n";
336         break;
337       default:
338         cout << "Font_FA_Undefined\n";
339         break;
340       }
341       cout  << "  font height: "<<aFontHeight<<"\n";
342       cout << "  font id: " << _CurrentFontId << "\n";
343 #endif
344
345       return _CurrentFontId;
346     }
347   }
348   //invalid family name
349 #ifdef TRACE
350   cout << "\n---Invalid Family Name!!!---\n";
351 #endif
352   return -1;
353 }
354
355 void OpenGl_FontMgr::render_text( const Standard_Integer id, const wchar_t* text,
356                                   const Standard_Boolean is2d )
357 {
358 #ifdef TRACE
359   cout << "TKOpenGl::render_text\n"
360     << "\tfont id = " << id << endl
361     << "\ttext = " << text << endl;
362 #endif
363   if ( text && _FontCache.IsBound( id ) ) {
364     glMatrixMode( GL_MODELVIEW );
365     glPushMatrix();
366
367     glScalef( _XCurrentScale, _YCurrentScale, 1 );
368     glPushAttrib( GL_ENABLE_BIT );
369
370     GLboolean enableTexture = glIsEnabled(GL_TEXTURE_2D);
371     GLboolean enableDepthTest = glIsEnabled(GL_DEPTH_TEST);
372
373     if( !enableTexture )
374       glEnable(GL_TEXTURE_2D);
375     if ( !is2d ) {
376       if ( !enableDepthTest )
377         glEnable(GL_DEPTH_TEST);
378     }
379     else if ( enableDepthTest ) {
380         glDisable(GL_DEPTH_TEST);
381     }
382
383     GLint param;
384     glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &param);
385
386     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 
387     glAlphaFunc(GL_GEQUAL, 0.285f);    
388     glEnable(GL_ALPHA_TEST);   
389     OGLFont_Cache cache = _FontCache.Find( id );
390     cache.Font->Render( text );
391
392     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, param);
393
394     if( !enableTexture )
395       glDisable(GL_TEXTURE_2D);
396     if( !enableDepthTest )
397       glDisable(GL_DEPTH_TEST);
398
399     glPopAttrib();
400     glMatrixMode( GL_MODELVIEW );
401     glPopMatrix();
402   }
403
404 }
405
406 void OpenGl_FontMgr::render_text ( const wchar_t* text, const Standard_Boolean is2d )
407 {
408   render_text( _CurrentFontId, text, is2d );
409 }
410
411 const FTFont* OpenGl_FontMgr::fontById (const Standard_Integer id)
412 {
413   return _FontCache.IsBound( id ) ? _FontCache.Find( id ).Font: NULL;
414 }
415
416 Standard_ShortReal OpenGl_FontMgr::computeWidth( const Standard_Integer id, const wchar_t* text )
417 {
418   if( !_FontCache.IsBound( id ) )
419     return 0.f;
420
421   OGLFont_Cache cache = _FontCache.Find( id );
422
423   Standard_ShortReal w = cache.Font->Advance( text );
424
425   return w;
426 }
427
428 void OpenGl_FontMgr::setCurrentScale (const Standard_ShortReal xScale,
429                                       const Standard_ShortReal yScale)
430 {
431   _XCurrentScale = xScale;
432   _YCurrentScale = yScale;
433 }
434
435 #include <AlienImage_BMPAlienData.hxx>
436 #include <OSD_File.hxx>
437 #include <OSD_Protection.hxx>
438 #include <Aspect_GenericColorMap.hxx>
439 #include <Image_ColorImage.hxx>
440 #include <Quantity_Color.hxx>
441
442 void dump_texture( int id)
443 {
444   Handle(AlienImage_BMPAlienData) image = new AlienImage_BMPAlienData();
445
446   if (!glIsTexture(id))
447     return;
448
449   unsigned char* pixels = new unsigned char[8192*1024];
450   memset( pixels, 0, 8192*1024 );
451   static int index = 0;
452   index++;
453
454   glBindTexture(GL_TEXTURE_2D, id );
455   glGetTexImage( GL_TEXTURE_2D , 
456     0,
457     GL_ALPHA,
458     GL_UNSIGNED_BYTE,
459     pixels );
460
461   Handle(Image_ColorImage) anImage = new Image_ColorImage( 0, 0, 1024, 8192 );
462
463   Aspect_ColorPixel mark( Quantity_Color (0.,0.5,1., Quantity_TOC_RGB ) ),
464     space( Quantity_Color (1.,1.,1., Quantity_TOC_RGB ) );
465
466   for( int i = 0; i < 1024; i++ ) {
467     for (int j = 0; j < 8192; j++ )
468       if (pixels[i*8192+j]>0) {
469         anImage->SetPixel( anImage->LowerX()+i, 
470           anImage->LowerY()+j,
471           mark );
472       }
473       else {
474         anImage->SetPixel( anImage->LowerX()+i, 
475           anImage->LowerY()+j,
476           space );
477       }
478   }
479
480   image->FromImage( anImage );
481   TCollection_AsciiString name( index );
482   name.Prepend( "D:\\Temp_image" );
483   name += ".bmp";
484   OSD_File file ( name );
485   file.Build( OSD_WriteOnly, OSD_Protection());
486   image->Write(file);
487   file.Close();
488   delete []pixels;
489 }