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