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