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