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