87ec26bdae4a034b6f28fa3862e6eaaf25359e80
[occt.git] / src / OpenGl / OpenGl_Display_1.cxx
1 // File:      OpenGl_Display_1.cxx
2 // Created:   25 October 2011
3 // Author:    Sergey ZERCHANINOV
4 // Copyright: OPEN CASCADE 2011
5
6 #include <OpenGl_Display.hxx>
7
8 #include <TCollection_AsciiString.hxx>
9 #include <TCollection_HAsciiString.hxx>
10
11 #include <OpenGl_FontMgr.hxx>
12
13 #include <OpenGl_AspectText.hxx>
14
15 #ifdef HAVE_GL2PS
16 #include <gl2ps.h>
17 #endif
18
19 /*-----------------------------------------------------------------------------*/
20 /*
21 * Prototypes variables statiques
22 */                                                 
23
24 struct FontMapNode
25 {
26   const char *    EnumName;
27   const char *    FontName;
28   OSD_FontAspect  FontAspect;
29 };
30
31 static const FontMapNode myFontMap[] =
32 {
33
34 #ifdef WNT
35
36   { "Courier"                  , "Courier New"    , OSD_FA_Regular },
37   { "Times-Roman"              , "Times New Roman", OSD_FA_Regular  },
38   { "Times-Bold"               , "Times New Roman", OSD_FA_Bold },
39   { "Times-Italic"             , "Times New Roman", OSD_FA_Italic  },
40   { "Times-BoldItalic"         , "Times New Roman", OSD_FA_BoldItalic  },
41   { "ZapfChancery-MediumItalic", "Script"         , OSD_FA_Regular  },
42   { "Symbol"                   , "Symbol"         , OSD_FA_Regular  },
43   { "ZapfDingbats"             , "WingDings"      , OSD_FA_Regular  },
44   { "Rock"                     , "Arial"          , OSD_FA_Regular  },
45   { "Iris"                     , "Lucida Console" , OSD_FA_Regular  }
46
47 #else   //X11
48
49   { "Courier"                  , "Courier"      , OSD_FA_Regular },
50   { "Times-Roman"              , "Times"        , OSD_FA_Regular  },
51   { "Times-Bold"               , "Times"        , OSD_FA_Bold },
52   { "Times-Italic"             , "Times"        , OSD_FA_Italic  },
53   { "Times-BoldItalic"         , "Times"        , OSD_FA_BoldItalic  },
54   { "Arial"                    , "Helvetica"    , OSD_FA_Regular  }, 
55   { "ZapfChancery-MediumItalic", "-adobe-itc zapf chancery-medium-i-normal--*-*-*-*-*-*-iso8859-1"              , OSD_FA_Regular  },
56   { "Symbol"                   , "-adobe-symbol-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific"                , OSD_FA_Regular  },
57   { "ZapfDingbats"             , "-adobe-itc zapf dingbats-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific"     , OSD_FA_Regular  },
58   { "Rock"                     , "-sgi-rock-medium-r-normal--*-*-*-*-p-*-iso8859-1"                             , OSD_FA_Regular  },
59   { "Iris"                     , "--iris-medium-r-normal--*-*-*-*-m-*-iso8859-1"                                , OSD_FA_Regular  }
60 #endif
61
62 };
63
64 #define NUM_FONT_ENTRIES (sizeof(myFontMap)/sizeof(FontMapNode))
65
66 /*-----------------------------------------------------------------------------*/
67
68 /*
69 *  Constants
70 */
71
72 #ifdef HAVE_GL2PS
73 void OpenGl_Display::getGL2PSFontName (const char *src_font, char *ps_font)
74 {
75   /* 
76   Convert font name used for rendering to some "good" font names
77   that produce good vector text 
78   */
79   static char const *family[] = {"Helvetica", "Courier", "Times"};
80   static char const *italic[] = {"Oblique", "Oblique", "Italic"};
81   static char const *base[] = {"", "", "-Roman"};
82
83   int font = 0;
84   int isBold = 0;
85   int isItalic = 0;
86
87   if( strstr( src_font, "Symbol" ) ){
88     sprintf( ps_font, "%s", "Symbol" );
89     return;
90   }
91
92   if( strstr( src_font, "ZapfDingbats" ) ){
93     sprintf( ps_font, "%s", "WingDings" );
94     return;
95   }
96
97   if ( strstr( src_font, "Courier" ) ){
98     font = 1;
99   }
100   else if ( strstr( src_font, "Times" ) ){
101     font = 2;
102   }
103
104   if ( strstr( src_font, "Bold" ) ){
105     isBold = 1;
106   }
107
108   if ( strstr( src_font, "Italic" ) || strstr( src_font, "Oblique" ) ){
109     isItalic = 1;
110   }
111
112   if ( isBold ){
113     sprintf( ps_font, "%s-%s", family[font], "Bold");
114     if ( isItalic ){
115       sprintf(ps_font, "%s%s", ps_font, italic[font]);
116     }
117   }
118   else if ( isItalic )
119   {
120     sprintf( ps_font, "%s-%s", family[font], italic[font] );
121   }
122   else
123   {
124     sprintf( ps_font, "%s%s", family[font], base[font] );
125   }
126 }
127 #endif
128
129 /*-----------------------------------------------------------------------------*/
130
131 /*
132 * Fonctions publiques 
133 */
134
135 /*-----------------------------------------------------------------------------*/
136
137 int OpenGl_Display::FindFont (const char* AFontName, const OSD_FontAspect AFontAspect,
138                              const int ABestSize, const float AXScale, const float AYScale)
139 {   
140   if (!AFontName)
141     return -1;
142
143   if (ABestSize != -1)
144     myFontSize = ABestSize;
145
146   OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
147
148   Handle(TCollection_HAsciiString) family_name = new TCollection_HAsciiString(AFontName);
149   myFont = mgr->request_font( family_name, AFontAspect, myFontSize );
150
151   if( myFont == -1 )
152   {
153     //try to use font names mapping
154     FontMapNode newTempFont = myFontMap[0];
155     for ( int i = 0; i < NUM_FONT_ENTRIES; ++i )
156     {
157       if ( TCollection_AsciiString(myFontMap[i].EnumName).IsEqual( family_name->ToCString() ) )
158       {
159         newTempFont = myFontMap[i];
160         break;
161       }
162     }
163     family_name = new TCollection_HAsciiString(newTempFont.FontName);
164     myFont = mgr->request_font( family_name, newTempFont.FontAspect, myFontSize );
165   }
166
167   // Requested family name not found -> serach for any font family with given aspect and height
168   if ( myFont == -1 )
169   {
170     family_name = new TCollection_HAsciiString( "" );
171     myFont = mgr->request_font( family_name, AFontAspect, myFontSize );
172   }
173
174   // The last resort: trying to use ANY font available in the system
175   if ( myFont == -1 )
176   {
177     myFont = mgr->request_font( family_name, OSD_FA_Undefined, -1 );
178   }
179
180   if ( myFont != -1 )
181     mgr->setCurrentScale( AXScale, AYScale );
182
183   return myFont;
184 }
185
186 /*-----------------------------------------------------------------------------*/
187
188 void OpenGl_Display::StringSize (const wchar_t *str, int &width, int &ascent, int &descent)
189 {
190   ascent = 0;
191   descent = 0;
192   width = 0;
193   if (myFont != -1) {
194     OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
195     const FTFont* font = mgr->fontById( myFont );
196     if ( font ) {
197       width = int( mgr->computeWidth( myFont, str ) );
198       ascent = int( font->Ascender() );
199       descent = int( font->Descender() );
200     }
201   }
202 }
203
204 /*
205   Class       : MultilineTextRenderer
206   Description : Class for constructing text and multi-line text
207 */
208
209 class MultilineTextRenderer
210 {
211   private:
212
213   Standard_Integer        myLFNum;        // Number of '\n' (Line Feed) '\x00\x0A' 
214   Standard_Integer        myCurrPos;      // Position of the current substring
215   Standard_Integer        myNewStrLen;    // Length of the new string 
216   Standard_Integer        mySubstrNum;    // Number of substrings
217   wchar_t                 *myNewStr;      // New string
218   const wchar_t           *myStrPtr;      // Pointer to the original string
219
220   public:
221
222   // ----------------------------------------------
223   // Function: MultilineTextRenderer
224   // Purpose:  Constructor with initialization
225   // ----------------------------------------------
226   MultilineTextRenderer ( const wchar_t           *theStr,
227                           GLdouble                &theXdis,
228                           GLdouble                &theYdis,
229                           const OpenGl_TextParam  *theParam,
230                           const FTFont            *theFnt,
231                           GLint                   theWidthFont,
232                           GLint                   theAscentFont,
233                           GLint                   theDescentFont ) :
234
235       myLFNum         (0),
236       myCurrPos       (0),
237       myNewStrLen     (0),
238       mySubstrNum     (0),
239       myNewStr        (0),
240       myStrPtr        (&theStr[0])
241   {
242     const Standard_Integer  aStrLen       = wcslen(theStr); // Length of the original string
243     Standard_Integer        aNextCRChar   = 0;  // Character after '\r' (Carriage Return)        '\x00\x0D'
244     Standard_Integer        aHTNum        = 0;  // Number of       '\t' (Horizontal Tabulation)  '\x00\x09'
245     Standard_Integer        aDumpNum      = 0;  // Number of '\a', '\b', '\v' and '\f'
246     Standard_Integer        anAllSpaces   = 0;  // Number of spaces instead of all '\t' characters
247     Standard_Integer        aMaxSubstrLen = 0;  // Length of the largest substring in the new string
248
249     Standard_Integer        aTimeVar      = 0;
250
251     // Calculation index after last '\r' character
252     for ( Standard_Integer anIndex = 0; anIndex < aStrLen; ++anIndex )
253     {
254       if ( theStr[anIndex] == '\r' )  aNextCRChar = anIndex+1;
255     }
256
257     // Calculation numbers of the some control characters
258     for (Standard_Integer anIndex = aNextCRChar; anIndex < aStrLen; anIndex++)
259     {
260       ++aTimeVar;
261       switch ( theStr[anIndex] )
262       {
263       case '\n':
264         ++myLFNum;
265         aTimeVar = 0;
266         break;
267       case '\b':
268       case '\v':
269       case '\f':
270       case '\a':
271         ++aDumpNum;
272         --aTimeVar;
273         break;
274       case '\t':
275         ++aHTNum;
276         anAllSpaces += ( 8 - ( aTimeVar - 1 )%8 );
277         aTimeVar = 0;
278         break;
279       }
280     }
281
282     // Calculation length of the new string
283     myStrPtr += aNextCRChar;
284     myNewStrLen = aStrLen - aNextCRChar + anAllSpaces - aHTNum - aDumpNum;
285
286     myNewStr = new wchar_t[myNewStrLen + 1];
287     myNewStr[myNewStrLen]='\0';
288
289     CalcString (aStrLen, aMaxSubstrLen);
290     CalcHAlign (theXdis, theParam, theWidthFont, aStrLen, aMaxSubstrLen);
291     CalcVAlign (theYdis, theParam, theFnt, theAscentFont, theDescentFont);
292   }
293
294   // ----------------------------------------------
295   // Function: ~MultilineTextRenderer
296   // Purpose:  Default destructor
297   // ----------------------------------------------
298   ~MultilineTextRenderer ()
299   {
300     delete[] myNewStr;
301   }
302
303   // ----------------------------------------------
304   // Function: Next
305   // Purpose:  Calculate position of the next substring
306   // ----------------------------------------------
307   void Next ()
308   {
309     for ( Standard_Integer anIndex = 0; anIndex <= myNewStrLen; ++anIndex )
310     {
311       if ( myNewStr[myCurrPos + anIndex] == '\0' )
312       {
313         ++mySubstrNum;
314         myCurrPos += anIndex + 1;
315         break;
316       }
317     }
318   }
319
320   // ----------------------------------------------
321   // Function: More
322   // Purpose:  Calculate position of the next substring
323   // ----------------------------------------------
324   Standard_Boolean More ()
325   {
326     if ( mySubstrNum <= myLFNum )  return Standard_True;
327     else                           return Standard_False;
328   }
329
330   // ----------------------------------------------
331   // Function: GetValue
332   // Purpose:  Returns current substring
333   // ----------------------------------------------
334   wchar_t* GetValue ()
335   {
336     return ( myNewStr + myCurrPos );
337   }
338
339   private:
340
341   // ----------------------------------------------
342   // Function: CalcString
343   // Purpose:  Calculate new string separated by '\0' without '\n', '\t', '\b', '\v', '\f', '\a'
344   // ----------------------------------------------
345   void CalcString ( const Standard_Integer theStrLen, Standard_Integer &theMaxSubstrLen )
346   {
347     Standard_Integer
348         aHelpIndex  = 0,
349         aTimeVar    = 0,
350         aSpacesNum  = 0;
351
352     for ( Standard_Integer anIndex1 = 0, anIndex2 = 0;
353           (anIndex1 < theStrLen)&&(anIndex2 < myNewStrLen);
354           ++anIndex1, ++anIndex2 )
355     {
356       ++aTimeVar;
357
358       if ( *(myStrPtr + anIndex1) == '\n' ) aTimeVar = 0;
359
360       while ( (*(myStrPtr + anIndex1)=='\b')||(*(myStrPtr + anIndex1)=='\f')
361             ||(*(myStrPtr + anIndex1)=='\v')||(*(myStrPtr + anIndex1)=='\a') )
362       {
363         ++anIndex1;
364       }
365
366       if ( *(myStrPtr + anIndex1) == '\t' )
367       {
368         aSpacesNum = ( 8 - ( aTimeVar - 1 )%8 );
369
370         for ( aHelpIndex = 0; aHelpIndex < aSpacesNum; aHelpIndex++ )
371         {
372           myNewStr[anIndex2 + aHelpIndex] = ' ';
373         }
374         anIndex2 += aHelpIndex - 1;
375         aTimeVar = 0;
376       }
377       else 
378       {
379         myNewStr[anIndex2] = *(myStrPtr + anIndex1);
380       }
381     }
382
383     // After this part of code we will have a string separated by '\0' characters
384     Standard_Integer aHelpLength = 0;
385
386     if( myNewStr )
387     {
388       for( Standard_Integer anIndex = 0; anIndex <= myNewStrLen; ++anIndex )
389       {
390         if ( myNewStr[anIndex] == '\n' )
391         {
392           myNewStr[anIndex] = '\0';
393         }
394
395         // Calculating length of the largest substring of the new string.
396         // It's needed for horizontal alignment
397         if ( myNewStr[anIndex] != '\0' )
398         {
399           ++aHelpLength;
400         }
401         else
402         {
403           if ( aHelpLength > theMaxSubstrLen ) theMaxSubstrLen = aHelpLength;
404
405           aHelpLength = 0;
406         }
407       }
408     }
409   }
410
411   // ----------------------------------------------
412   // Function: CalcVAlign
413   // Purpose:  Calculate vertical alignment for text
414   // ----------------------------------------------
415   void CalcVAlign ( GLdouble                &theYdis,
416                     const OpenGl_TextParam  *theParam,
417                     const FTFont            *theFnt,
418                     GLint                   theAscentFont,
419                     GLint                   theDescentFont )
420   {
421     switch (theParam->VAlign)
422     {
423     case Graphic3d_VTA_BOTTOM:
424       theYdis = (GLdouble)(myLFNum * theFnt->FaceSize());
425       break;
426     case Graphic3d_VTA_CENTER:
427       if ( (myLFNum%2) == 0 )   // An odd number of strings
428       {
429         theYdis = (GLdouble)((myLFNum/2.0) * theFnt->FaceSize()) + theDescentFont; 
430       }
431       else                      // An even number of strings
432       {
433         theYdis = (GLdouble)((myLFNum - 1)/2.0 * theFnt->FaceSize()) - theDescentFont/2.0;
434       }
435       break;
436     case Graphic3d_VTA_TOP:
437       theYdis = -(GLdouble)theAscentFont - theDescentFont;
438       break;
439     default:
440       theYdis = (GLdouble)(myLFNum * theFnt->FaceSize());
441       break;
442     }
443   }
444
445   // ----------------------------------------------
446   // Function: CalcHAlign
447   // Purpose:  Calculate horizontal alignment for text
448   // ----------------------------------------------
449   void CalcHAlign ( GLdouble                &theXdis,
450                     const OpenGl_TextParam  *theParam,
451                     GLint                   theWidthFont,
452                     const Standard_Integer  theStrLen,
453                     Standard_Integer        theMaxSubstrLen)
454   {
455     GLdouble aWidth = (GLdouble)(theMaxSubstrLen * theWidthFont)/theStrLen;
456
457     switch (theParam->HAlign)
458     {
459     case Graphic3d_HTA_LEFT:
460       theXdis = 0.;
461       break;
462     case Graphic3d_HTA_CENTER:
463       theXdis = -0.5 * (GLdouble)aWidth;
464       break;
465     case Graphic3d_HTA_RIGHT:
466       theXdis = -(GLdouble)aWidth;
467       break;
468     default:
469       theXdis = 0.;
470       break;
471     }
472   }
473 };
474
475 /*-----------------------------------------------------------------------------*/
476
477 void OpenGl_Display::RenderText (const wchar_t* str, const int is2d, const float x, const float y, const float z,
478                                 const OpenGl_AspectText *aspect, const OpenGl_TextParam *param)
479 {
480   OpenGl_FontMgr* mgr = OpenGl_FontMgr::instance();
481   const FTFont* fnt = mgr->fontById( myFont );
482   if ( !fnt )
483     return; 
484
485   // FTFont changes texture state when it renders and computes size for the text
486   glPushAttrib(GL_TEXTURE_BIT);
487
488   int widthFont, ascentFont, descentFont;
489   StringSize( str, widthFont, ascentFont, descentFont );
490
491   GLdouble xdis = 0., ydis = 0.;
492
493   float export_h = 1.;
494
495   MultilineTextRenderer aRenderer( str,
496                                    xdis,
497                                    ydis,
498                                    param,
499                                    fnt,
500                                    widthFont,
501                                    ascentFont,
502                                    descentFont );
503
504   glMatrixMode(GL_MODELVIEW);
505   glPushMatrix();
506   if (is2d)
507   {
508     glLoadIdentity();
509     glTranslatef(x, y, 0.f);
510     glRotatef( 180, 1, 0, 0 );
511   }
512   else
513   {
514     const GLdouble identityMatrix[4][4] =
515     {
516       {1.,0.,0.,0.},
517       {0.,1.,0.,0.},
518       {0.,0.,1.,0.},
519       {0.,0.,0.,1.}
520     };
521
522     GLdouble projMatrix[4][4], modelMatrix[4][4];
523     GLint viewport[4];
524
525     GLdouble wx, wy, wz;
526     GLdouble x1, y1, z1;
527     GLdouble x2, y2, z2;
528
529     glGetDoublev( GL_MODELVIEW_MATRIX, (GLdouble*)modelMatrix );
530     glGetDoublev( GL_PROJECTION_MATRIX, (GLdouble*)projMatrix );
531     glGetIntegerv( GL_VIEWPORT, (GLint*)viewport );
532
533     gluProject( x, y, z,
534       (GLdouble*)modelMatrix,
535       (GLdouble*)projMatrix,
536       (GLint*)viewport,
537       &wx, &wy, &wz );
538     glLoadIdentity();
539     gluUnProject( wx, wy, wz, 
540       (GLdouble*)identityMatrix, (GLdouble*)projMatrix, (GLint*)viewport,
541       &x1, &y1 , &z1 );    
542
543     GLdouble h = (GLdouble)fnt->FaceSize();
544
545     gluUnProject( wx, wy + h - 1., wz,
546       (GLdouble*)identityMatrix, (GLdouble*)projMatrix, (GLint*)viewport,
547       &x2, &y2, &z2 );
548
549     h = (y2-y1)/h;
550
551     glTranslated( x1, y1 , z1 );   
552     glRotated(aspect->Angle(), 0, 0, 1);
553     glTranslated(xdis, ydis, 0);  
554
555     if( !aspect->IsZoomable() )
556     {
557       glScaled( h, h, h );
558     }
559     else
560     {
561       export_h = (float)h;
562     }
563   }
564
565   GLint renderMode;  
566   glGetIntegerv(GL_RENDER_MODE, &renderMode);
567   if ( renderMode == GL_FEEDBACK ) 
568   {
569 #ifdef HAVE_GL2PS
570     export_h = (float)fnt->FaceSize() / export_h;
571     for ( ; aRenderer.More(); aRenderer.Next() )
572     {
573       // x, y, z coordinates are used here as zero values, because position of the text
574       // and alignment is calculated in the code above using glTranslated methods
575       ExportText( aRenderer.GetValue(), is2d, 0, 0, 0, aspect, param, (short)export_h );
576       glTranslated(0, -(GLdouble)fnt->FaceSize(), 0);
577     }
578 #endif
579   }
580   else
581   {
582     for ( ; aRenderer.More(); aRenderer.Next() )
583     {
584       mgr->render_text( myFont, aRenderer.GetValue(), is2d );
585       glTranslated(0, -(GLdouble)fnt->FaceSize(), 0);
586     }
587   }
588   glPopMatrix();
589   glPopAttrib();
590 }
591
592 /*-----------------------------------------------------------------------------*/
593
594 void OpenGl_Display::ExportText (const wchar_t* text, const int is2d, const float x, const float y, const float z,
595                                 const OpenGl_AspectText *aspect, const OpenGl_TextParam *param, const short height)
596 {
597 #ifdef HAVE_GL2PS
598
599   const char* fontname = aspect->Font();
600   float angle = aspect->Angle();
601
602   GLubyte zero = 0;
603   char ps_font[64];
604
605   getGL2PSFontName(fontname, ps_font);
606
607   if( is2d )
608     glRasterPos2f( x, y );
609   else
610     glRasterPos3f( x, y, z );
611
612   glBitmap( 1, 1, 0, 0, 0, 0, &zero );
613
614   //szv: workaround for gl2ps!
615   const int len = 4 * (wcslen(text) + 1); //szv: should be more than enough
616   char *astr = new char[len];
617   wcstombs(astr,text,len);
618
619   // Standard GL2PS's alignment isn't used, because it doesn't work correctly
620   // for all formats, therefore alignment is calculated manually relative
621   // to the bottom-left corner, which corresponds to the GL2PS_TEXT_BL value
622   gl2psTextOpt(astr, ps_font, height, GL2PS_TEXT_BL, angle);
623   delete[] astr;
624
625 #endif
626 }