0024752: Visualization - inherit OpenGl_Group from Graphic3d_Group
[occt.git] / src / OpenGl / OpenGl_Text.cxx
1 // Created on: 2011-07-13
2 // Created by: Sergey ZERCHANINOV
3 // Copyright (c) 2011-2013 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <OpenGl_AspectText.hxx>
17 #include <OpenGl_GlCore11.hxx>
18 #include <OpenGl_GraphicDriver.hxx>
19 #include <OpenGl_ShaderManager.hxx>
20 #include <OpenGl_ShaderProgram.hxx>
21 #include <OpenGl_ShaderStates.hxx>
22 #include <OpenGl_Text.hxx>
23 #include <OpenGl_Workspace.hxx>
24
25 #include <Font_FontMgr.hxx>
26 #include <TCollection_HAsciiString.hxx>
27
28 #ifdef HAVE_CONFIG_H
29   #include <config.h>
30 #endif
31
32 #ifdef HAVE_GL2PS
33   #include <gl2ps.h>
34 #endif
35
36 namespace
37 {
38   static const GLdouble THE_IDENTITY_MATRIX[4][4] =
39   {
40     {1.,0.,0.,0.},
41     {0.,1.,0.,0.},
42     {0.,0.,1.,0.},
43     {0.,0.,0.,1.}
44   };
45
46 #ifdef HAVE_GL2PS
47   static char const* TheFamily[] = {"Helvetica", "Courier", "Times"};
48   static char const* TheItalic[] = {"Oblique",   "Oblique", "Italic"};
49   static char const* TheBase[]   = {"", "", "-Roman"};
50
51   //! Convert font name used for rendering to some "good" font names
52   //! that produce good vector text.
53   static void getGL2PSFontName (const char* theSrcFont,
54                                 char*       thePsFont)
55   {
56     if (strstr (theSrcFont, "Symbol"))
57     {
58       sprintf (thePsFont, "%s", "Symbol");
59       return;
60     }
61     else if (strstr (theSrcFont, "ZapfDingbats"))
62     {
63       sprintf (thePsFont, "%s", "WingDings");
64       return;
65     }
66
67     int  aFontId  = 0;
68     bool isBold   = false;
69     bool isItalic = false;
70     if (strstr (theSrcFont, "Courier"))
71     {
72       aFontId = 1;
73     }
74     else if (strstr (theSrcFont, "Times"))
75     {
76       aFontId = 2;
77     }
78
79     if (strstr (theSrcFont, "Bold"))
80     {
81       isBold = true;
82     }
83     if (strstr (theSrcFont, "Italic")
84      || strstr (theSrcFont, "Oblique"))
85     {
86       isItalic = true;
87     }
88
89     if (isBold)
90     {
91       if (isItalic)
92       {
93         sprintf (thePsFont, "%s-Bold%s", TheFamily[aFontId], TheItalic[aFontId]);
94       }
95       else
96       {
97         sprintf (thePsFont, "%s-Bold", TheFamily[aFontId]);
98       }
99     }
100     else if (isItalic)
101     {
102       sprintf (thePsFont, "%s-%s", TheFamily[aFontId], TheItalic[aFontId]);
103     }
104     else
105     {
106       sprintf (thePsFont, "%s%s", TheFamily[aFontId], TheBase[aFontId]);
107     }
108   }
109
110   static void exportText (const NCollection_String& theText,
111                           const Standard_Boolean    theIs2d,
112                           const OpenGl_AspectText&  theAspect,
113                           const Standard_Integer    theHeight)
114   {
115
116     char aPsFont[64];
117     getGL2PSFontName (theAspect.FontName().ToCString(), aPsFont);
118
119     if (theIs2d)
120     {
121       glRasterPos2f (0.0f, 0.0f);
122     }
123     else
124     {
125       glRasterPos3f (0.0f, 0.0f, 0.0f);
126     }
127
128     GLubyte aZero = 0;
129     glBitmap (1, 1, 0, 0, 0, 0, &aZero);
130
131     // Standard GL2PS's alignment isn't used, because it doesn't work correctly
132     // for all formats, therefore alignment is calculated manually relative
133     // to the bottom-left corner, which corresponds to the GL2PS_TEXT_BL value
134     gl2psTextOpt (theText.ToCString(), aPsFont, (GLshort)theHeight, GL2PS_TEXT_BL, theAspect.Angle());
135   }
136 #endif
137
138 };
139
140 // =======================================================================
141 // function : OpenGl_Text
142 // purpose  :
143 // =======================================================================
144 OpenGl_Text::OpenGl_Text()
145 : myWinX (0.0f),
146   myWinY (0.0f),
147   myWinZ (0.0f),
148   myScaleHeight (1.0f),
149   myPoint  (0.0f, 0.0f, 0.0f),
150   myIs2d   (false)
151 {
152   myParams.Height = 10;
153   myParams.HAlign = Graphic3d_HTA_LEFT;
154   myParams.VAlign = Graphic3d_VTA_BOTTOM;
155 }
156
157 // =======================================================================
158 // function : OpenGl_Text
159 // purpose  :
160 // =======================================================================
161 OpenGl_Text::OpenGl_Text (const Standard_Utf8Char* theText,
162                           const OpenGl_Vec3&       thePoint,
163                           const OpenGl_TextParam&  theParams)
164 : myWinX (0.0f),
165   myWinY (0.0f),
166   myWinZ (0.0f),
167   myScaleHeight  (1.0f),
168   myExportHeight (1.0f),
169   myParams (theParams),
170   myString (theText),
171   myPoint  (thePoint),
172   myIs2d   (false)
173 {
174   //
175 }
176
177 // =======================================================================
178 // function : SetPosition
179 // purpose  :
180 // =======================================================================
181 void OpenGl_Text::SetPosition (const OpenGl_Vec3& thePoint)
182 {
183   myPoint = thePoint;
184 }
185
186 // =======================================================================
187 // function : SetFontSize
188 // purpose  :
189 // =======================================================================
190 void OpenGl_Text::SetFontSize (const Handle(OpenGl_Context)& theCtx,
191                                const Standard_Integer        theFontSize)
192 {
193   if (myParams.Height != theFontSize)
194   {
195     Release (theCtx);
196   }
197   myParams.Height = theFontSize;
198 }
199
200 // =======================================================================
201 // function : Init
202 // purpose  :
203 // =======================================================================
204 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
205                         const Standard_Utf8Char*      theText,
206                         const OpenGl_Vec3&            thePoint)
207 {
208   releaseVbos (theCtx);
209   myIs2d   = false;
210   myPoint  = thePoint;
211   myString.FromUnicode (theText);
212 }
213
214 // =======================================================================
215 // function : Init
216 // purpose  :
217 // =======================================================================
218 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
219                         const Standard_Utf8Char*      theText,
220                         const OpenGl_Vec3&            thePoint,
221                         const OpenGl_TextParam&       theParams)
222 {
223   if (myParams.Height != theParams.Height)
224   {
225     Release (theCtx);
226   }
227   else
228   {
229     releaseVbos (theCtx);
230   }
231   myIs2d   = false;
232   myParams = theParams;
233   myPoint  = thePoint;
234   myString.FromUnicode (theText);
235 }
236
237 // =======================================================================
238 // function : Init
239 // purpose  :
240 // =======================================================================
241 void OpenGl_Text::Init (const Handle(OpenGl_Context)&     theCtx,
242                         const TCollection_ExtendedString& theText,
243                         const OpenGl_Vec2&                thePoint,
244                         const OpenGl_TextParam&           theParams)
245 {
246   if (myParams.Height != theParams.Height)
247   {
248     Release (theCtx);
249   }
250   else
251   {
252     releaseVbos (theCtx);
253   }
254   myIs2d       = true;
255   myParams     = theParams;
256   myPoint.xy() = thePoint;
257   myPoint.z()  = 0.0f;
258   myString.FromUnicode ((Standard_Utf16Char* )theText.ToExtString());
259 }
260
261 // =======================================================================
262 // function : ~OpenGl_Text
263 // purpose  :
264 // =======================================================================
265 OpenGl_Text::~OpenGl_Text()
266 {
267   //
268 }
269
270 // =======================================================================
271 // function : releaseVbos
272 // purpose  :
273 // =======================================================================
274 void OpenGl_Text::releaseVbos (const Handle(OpenGl_Context)& theCtx)
275 {
276   for (Standard_Integer anIter = 0; anIter < myVertsVbo.Length(); ++anIter)
277   {
278     Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.ChangeValue (anIter);
279     Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.ChangeValue (anIter);
280
281     if (!theCtx.IsNull())
282     {
283       theCtx->DelayedRelease (aVerts);
284       theCtx->DelayedRelease (aTCrds);
285     }
286     aVerts.Nullify();
287     aTCrds.Nullify();
288   }
289   myTextures.Clear();
290   myVertsVbo.Clear();
291   myTCrdsVbo.Clear();
292   myVertsArray.Clear();
293   myTCrdsArray.Clear();
294 }
295
296 // =======================================================================
297 // function : Release
298 // purpose  :
299 // =======================================================================
300 void OpenGl_Text::Release (const Handle(OpenGl_Context)& theCtx)
301 {
302   releaseVbos (theCtx);
303   if (!myFont.IsNull())
304   {
305     Handle(OpenGl_Context) aCtx = theCtx;
306     const TCollection_AsciiString aKey = myFont->ResourceKey();
307     myFont.Nullify();
308     aCtx->ReleaseResource (aKey, Standard_True);
309   }
310 }
311
312 // =======================================================================
313 // function : StringSize
314 // purpose  :
315 // =======================================================================
316 void OpenGl_Text::StringSize (const Handle(OpenGl_Context)& theCtx,
317                               const NCollection_String&     theText,
318                               const OpenGl_AspectText&      theTextAspect,
319                               const OpenGl_TextParam&       theParams,
320                               Standard_ShortReal&           theWidth,
321                               Standard_ShortReal&           theAscent,
322                               Standard_ShortReal&           theDescent)
323 {
324   theWidth   = 0.0f;
325   theAscent  = 0.0f;
326   theDescent = 0.0f;
327   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, theParams.Height);
328   Handle(OpenGl_Font) aFont = FindFont (theCtx, theTextAspect, theParams.Height, aFontKey);
329   if (aFont.IsNull() || !aFont->IsValid())
330   {
331     return;
332   }
333
334   theAscent  = aFont->Ascender();
335   theDescent = aFont->Descender();
336
337   GLfloat aWidth = 0.0f;
338   for (NCollection_Utf8Iter anIter = theText.Iterator(); *anIter != 0;)
339   {
340     const Standard_Utf32Char aCharThis =   *anIter;
341     const Standard_Utf32Char aCharNext = *++anIter;
342
343     if (aCharThis == '\x0D' // CR  (carriage return)
344      || aCharThis == '\a'   // BEL (alarm)
345      || aCharThis == '\f'   // FF  (form feed) NP (new page)
346      || aCharThis == '\b'   // BS  (backspace)
347      || aCharThis == '\v')  // VT  (vertical tab)
348     {
349       continue; // skip unsupported carriage control codes
350     }
351     else if (aCharThis == '\x0A') // LF (line feed, new line)
352     {
353       theWidth = Max (theWidth, aWidth);
354       aWidth   = 0.0f;
355       continue;
356     }
357     else if (aCharThis == ' ')
358     {
359       aWidth += aFont->AdvanceX (aCharThis, aCharNext);
360       continue;
361     }
362     else if (aCharThis == '\t')
363     {
364       aWidth += aFont->AdvanceX (' ', aCharNext) * 8.0f;
365       continue;
366     }
367
368     aWidth += aFont->AdvanceX (aCharThis, aCharNext);
369   }
370   theWidth = Max (theWidth, aWidth);
371
372   Handle(OpenGl_Context) aCtx = theCtx;
373   aFont.Nullify();
374   aCtx->ReleaseResource (aFontKey, Standard_True);
375 }
376
377 // =======================================================================
378 // function : Render
379 // purpose  :
380 // =======================================================================
381 void OpenGl_Text::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
382 {
383   const OpenGl_AspectText* aTextAspect = theWorkspace->AspectText (Standard_True);
384   const Handle(OpenGl_Texture) aPrevTexture = theWorkspace->DisableTexture();
385
386   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
387
388   if (aCtx->IsGlGreaterEqual (2, 0))
389   {
390     Handle(OpenGl_ShaderProgram) aProgram = aTextAspect->ShaderProgramRes (theWorkspace);
391
392     if (!aProgram.IsNull())
393     {
394       aProgram->BindWithVariables (aCtx);
395
396       const OpenGl_MaterialState* aMaterialState = aCtx->ShaderManager()->MaterialState (aProgram);
397
398       if (aMaterialState == NULL || aMaterialState->Aspect() != aTextAspect)
399         aCtx->ShaderManager()->UpdateMaterialStateTo (aProgram, aTextAspect);
400
401       aCtx->ShaderManager()->PushState (aProgram);
402     }
403     else
404     {
405       OpenGl_ShaderProgram::Unbind (aCtx);
406     }
407   }
408
409   // use highlight color or colors from aspect
410   if (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT)
411   {
412     render (theWorkspace->PrinterContext(),
413             aCtx,
414             *aTextAspect,
415             *theWorkspace->HighlightColor,
416             *theWorkspace->HighlightColor);
417   }
418   else
419   {
420     render (theWorkspace->PrinterContext(),
421             aCtx,
422             *aTextAspect,
423             aTextAspect->Color(),
424             aTextAspect->SubtitleColor());
425   }
426
427   // restore aspects
428   if (!aPrevTexture.IsNull())
429   {
430     theWorkspace->EnableTexture (aPrevTexture);
431   }
432 }
433
434 // =======================================================================
435 // function : Render
436 // purpose  :
437 // =======================================================================
438 void OpenGl_Text::Render (const Handle(OpenGl_PrinterContext)& thePrintCtx,
439                           const Handle(OpenGl_Context)&        theCtx,
440                           const OpenGl_AspectText&             theTextAspect) const
441 {
442   render (thePrintCtx, theCtx, theTextAspect, theTextAspect.Color(), theTextAspect.SubtitleColor());
443 }
444
445 // =======================================================================
446 // function : setupMatrix
447 // purpose  :
448 // =======================================================================
449 void OpenGl_Text::setupMatrix (const Handle(OpenGl_PrinterContext)& thePrintCtx,
450                                const Handle(OpenGl_Context)&        /*theCtx*/,
451                                const OpenGl_AspectText&             theTextAspect,
452                                const OpenGl_Vec3                    theDVec) const
453 {
454   // setup matrix
455   if (myIs2d)
456   {
457     glLoadIdentity();
458     glTranslatef (myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.0f);
459     glScalef (1.0f, -1.0f, 1.0f);
460     glRotatef (theTextAspect.Angle(), 0.0, 0.0, 1.0);
461   }
462   else
463   {
464     // align coordinates to the nearest integer
465     // to avoid extra interpolation issues
466     GLdouble anObjX, anObjY, anObjZ;
467     gluUnProject (std::floor (myWinX + (GLdouble )theDVec.x()),
468                   std::floor (myWinY + (GLdouble )theDVec.y()),
469                   myWinZ + (GLdouble )theDVec.z(),
470                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
471                   &anObjX, &anObjY, &anObjZ);
472
473     glLoadIdentity();
474     glTranslated (anObjX, anObjY, anObjZ);
475     glRotated (theTextAspect.Angle(), 0.0, 0.0, 1.0);
476     if (!theTextAspect.IsZoomable())
477     {
478     #ifdef _WIN32
479       // if the context has assigned printer context, use it's parameters
480       if (!thePrintCtx.IsNull())
481       {
482         // get printing scaling in x and y dimensions
483         GLfloat aTextScalex = 1.0f, aTextScaley = 1.0f;
484         thePrintCtx->GetScale (aTextScalex, aTextScaley);
485
486         // text should be scaled in all directions with same
487         // factor to save its proportions, so use height (y) scaling
488         // as it is better for keeping text/3d graphics proportions
489         glScalef (aTextScaley, aTextScaley, aTextScaley);
490       }
491     #endif
492       glScaled (myScaleHeight, myScaleHeight, myScaleHeight);
493     }
494   }
495 }
496
497 // =======================================================================
498 // function : drawText
499 // purpose  :
500 // =======================================================================
501
502 void OpenGl_Text::drawText (const Handle(OpenGl_PrinterContext)& ,
503                             const Handle(OpenGl_Context)&        theCtx,
504                           #ifdef HAVE_GL2PS
505                             const OpenGl_AspectText&             theTextAspect) const
506                           #else
507                             const OpenGl_AspectText&                          ) const
508                           #endif
509 {
510 #ifdef HAVE_GL2PS
511   if (theCtx->IsFeedback())
512   {
513     // position of the text and alignment is calculated by transformation matrix
514     exportText (myString, myIs2d, theTextAspect, (Standard_Integer )myExportHeight);
515     return;
516   }
517 #endif
518
519   if (myVertsVbo.Length() == myTextures.Length())
520   {
521     for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
522     {
523       const GLuint aTexId = myTextures.Value (anIter);
524       const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
525       const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
526       aVerts->BindFixed (theCtx, GL_VERTEX_ARRAY);
527       aTCrds->BindFixed (theCtx, GL_TEXTURE_COORD_ARRAY);
528       glBindTexture (GL_TEXTURE_2D, aTexId);
529
530       glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
531
532       glBindTexture (GL_TEXTURE_2D, 0);
533       aTCrds->UnbindFixed (theCtx, GL_TEXTURE_COORD_ARRAY);
534       aVerts->UnbindFixed (theCtx, GL_VERTEX_ARRAY);
535     }
536   }
537   else if (myVertsArray.Length() == myTextures.Length())
538   {
539     glEnableClientState (GL_VERTEX_ARRAY);
540     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
541     for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
542     {
543       const GLuint aTexId = myTextures.Value (anIter);
544       const Handle(OpenGl_Vec2Array)& aVerts = myVertsArray.Value (anIter);
545       const Handle(OpenGl_Vec2Array)& aTCrds = myTCrdsArray.Value (anIter);
546
547       glVertexPointer   (2, GL_FLOAT, 0, (GLfloat* )&aVerts->First());
548       glTexCoordPointer (2, GL_FLOAT, 0, (GLfloat* )&aTCrds->First());
549       glBindTexture (GL_TEXTURE_2D, aTexId);
550
551       glDrawArrays (GL_TRIANGLES, 0, aVerts->Length());
552
553       glBindTexture (GL_TEXTURE_2D, 0);
554     }
555     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
556     glDisableClientState (GL_VERTEX_ARRAY);
557   }
558 }
559
560 // =======================================================================
561 // function : FontKey
562 // purpose  :
563 // =======================================================================
564 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
565                                               const Standard_Integer   theHeight)
566 {
567   const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
568   return theAspect.FontName()
569        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
570        + TCollection_AsciiString(":") + theHeight;
571 }
572
573 // =======================================================================
574 // function : FindFont
575 // purpose  :
576 // =======================================================================
577 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
578                                            const OpenGl_AspectText&      theAspect,
579                                            const Standard_Integer        theHeight,
580                                            const TCollection_AsciiString theKey)
581 {
582   Handle(OpenGl_Font) aFont;
583   if (theHeight < 2)
584   {
585     return aFont; // invalid parameters
586   }
587
588   if (!theCtx->GetResource (theKey, aFont))
589   {
590     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
591     const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.FontName());
592     const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
593     Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
594     if (aRequestedFont.IsNull())
595     {
596       return aFont;
597     }
598
599     Handle(Font_FTFont) aFontFt = new Font_FTFont (NULL);
600     if (!aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight))
601     {
602       return aFont;
603     }
604
605     Handle(OpenGl_Context) aCtx = theCtx;
606     glPushAttrib (GL_TEXTURE_BIT);
607     aFont = new OpenGl_Font (aFontFt, theKey);
608     if (!aFont->Init (aCtx))
609     {
610       //glPopAttrib();
611       //return aFont; // out of resources?
612     }
613     glPopAttrib(); // texture bit
614
615     aCtx->ShareResource (theKey, aFont);
616   }
617   return aFont;
618 }
619
620 // =======================================================================
621 // function : render
622 // purpose  :
623 // =======================================================================
624 void OpenGl_Text::render (const Handle(OpenGl_PrinterContext)& thePrintCtx,
625                           const Handle(OpenGl_Context)&        theCtx,
626                           const OpenGl_AspectText&             theTextAspect,
627                           const TEL_COLOUR&                    theColorText,
628                           const TEL_COLOUR&                    theColorSubs) const
629 {
630   if (myString.IsEmpty())
631   {
632     return;
633   }
634
635   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height);
636   if (!myFont.IsNull()
637    && !myFont->ResourceKey().IsEqual (aFontKey))
638   {
639     // font changed
640     const_cast<OpenGl_Text* > (this)->Release (theCtx);
641   }
642
643   if (myFont.IsNull())
644   {
645     myFont = FindFont (theCtx, theTextAspect, myParams.Height, aFontKey);
646     if (myFont.IsNull())
647     {
648       return;
649     }
650   }
651
652   if (myTextures.IsEmpty())
653   {
654     OpenGl_TextFormatter aFormatter;
655     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
656     aFormatter.Reset();
657     aFormatter.Append (theCtx, myString, *myFont.operator->());
658     aFormatter.Format();
659
660     if (!theCtx->caps->vboDisable && theCtx->core15 != NULL)
661     {
662       aFormatter.Result (theCtx, myTextures, myVertsVbo, myTCrdsVbo);
663     }
664     else
665     {
666       aFormatter.Result (theCtx, myTextures, myVertsArray, myTCrdsArray);
667     }
668     aFormatter.BndBox (myBndBox);
669   }
670
671   if (myTextures.IsEmpty())
672   {
673     return;
674   }
675
676   myExportHeight = 1.0f;
677   myScaleHeight  = 1.0f;
678
679   glMatrixMode (GL_MODELVIEW);
680   glPushMatrix();
681   if (!myIs2d)
682   {
683     // retrieve active matrices for project/unproject calls
684     glGetDoublev  (GL_MODELVIEW_MATRIX,  myModelMatrix);
685     glGetDoublev  (GL_PROJECTION_MATRIX, myProjMatrix);
686     glGetIntegerv (GL_VIEWPORT,          myViewport);
687     gluProject (myPoint.x(), myPoint.y(), myPoint.z(),
688                 myModelMatrix, myProjMatrix, myViewport,
689                 &myWinX, &myWinY, &myWinZ);
690
691     // compute scale factor for constant text height
692     GLdouble x1, y1, z1;
693     gluUnProject (myWinX, myWinY, myWinZ,
694                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
695                   &x1, &y1, &z1);
696
697     GLdouble x2, y2, z2;
698     const GLdouble h = (GLdouble )myFont->FTFont()->PointSize();
699     gluUnProject (myWinX, myWinY + h - 1.0, myWinZ,
700                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
701                   &x2, &y2, &z2);
702
703     myScaleHeight = (y2 - y1) / h;
704     if (theTextAspect.IsZoomable())
705     {
706       myExportHeight = (float )h;
707     }
708   }
709   myExportHeight = (float )myFont->FTFont()->PointSize() / myExportHeight;
710
711   // push enabled flags to the stack
712   glPushAttrib (GL_ENABLE_BIT);
713   glDisable (GL_LIGHTING);
714
715   // setup depth test
716   if (!myIs2d
717    && theTextAspect.StyleType() != Aspect_TOST_ANNOTATION)
718   {
719     glEnable (GL_DEPTH_TEST);
720   }
721   else
722   {
723     glDisable (GL_DEPTH_TEST);
724   }
725
726   // setup alpha test
727   GLint aTexEnvParam = GL_REPLACE;
728   glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
729   if (aTexEnvParam != GL_REPLACE)
730   {
731     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
732   }
733   glAlphaFunc (GL_GEQUAL, 0.285f);
734   glEnable (GL_ALPHA_TEST);
735
736   // setup blending
737   glEnable (GL_BLEND);
738   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
739
740   // activate texture unit
741   glDisable (GL_TEXTURE_1D);
742   glEnable  (GL_TEXTURE_2D);
743   if (theCtx->core15fwd != NULL)
744   {
745     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
746   }
747
748   // extra drawings
749   switch (theTextAspect.DisplayType())
750   {
751     case Aspect_TODT_BLEND:
752     {
753       glEnable  (GL_COLOR_LOGIC_OP);
754       glLogicOp (GL_XOR);
755       break;
756     }
757     case Aspect_TODT_SUBTITLE:
758     {
759       glColor3fv  (theColorSubs.rgb);
760       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
761
762       glBindTexture (GL_TEXTURE_2D, 0);
763       glBegin (GL_QUADS);
764       glVertex2f (myBndBox.Left,  myBndBox.Top);
765       glVertex2f (myBndBox.Right, myBndBox.Top);
766       glVertex2f (myBndBox.Right, myBndBox.Bottom);
767       glVertex2f (myBndBox.Left,  myBndBox.Bottom);
768       glEnd();
769       break;
770     }
771     case Aspect_TODT_DEKALE:
772     {
773       glColor3fv  (theColorSubs.rgb);
774       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.00001f));
775       drawText    (thePrintCtx, theCtx, theTextAspect);
776       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.00001f));
777       drawText    (thePrintCtx, theCtx, theTextAspect);
778       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.00001f));
779       drawText    (thePrintCtx, theCtx, theTextAspect);
780       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.00001f));
781       drawText    (thePrintCtx, theCtx, theTextAspect);
782       break;
783     }
784     case Aspect_TODT_DIMENSION:
785     case Aspect_TODT_NORMAL:
786     {
787       break;
788     }
789   }
790
791   // main draw call
792   glColor3fv  (theColorText.rgb);
793   setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
794   drawText    (thePrintCtx, theCtx, theTextAspect);
795
796   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
797
798   if (theTextAspect.DisplayType() == Aspect_TODT_DIMENSION)
799   {
800     setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
801
802     glDisable (GL_BLEND);
803     glDisable (GL_TEXTURE_2D);
804     glDisable (GL_ALPHA_TEST);
805     if (!myIs2d)
806     {
807       glDisable (GL_DEPTH_TEST);
808     }
809     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
810
811     glClear (GL_STENCIL_BUFFER_BIT);
812     glEnable (GL_STENCIL_TEST);
813     glStencilFunc (GL_ALWAYS, 1, 0xFF);
814     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
815
816     glBegin (GL_QUADS);
817     glVertex2f (myBndBox.Left,  myBndBox.Top);
818     glVertex2f (myBndBox.Right, myBndBox.Top);
819     glVertex2f (myBndBox.Right, myBndBox.Bottom);
820     glVertex2f (myBndBox.Left,  myBndBox.Bottom);
821     glEnd();
822
823     glStencilFunc (GL_ALWAYS, 0, 0xFF);
824     // glPopAttrib() will reset state for us
825     //glDisable (GL_STENCIL_TEST);
826     //if (!myIs2d) glEnable (GL_DEPTH_TEST);
827
828     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
829   }
830
831   // revert OpenGL state
832   glPopAttrib(); // enable bit
833   glPopMatrix(); // model view matrix was modified
834 }