c6a745fc11b36363eee7a5d8ca7a2d9afe67f163
[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 TCollection_ExtendedString& 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 ((Standard_Utf16Char* )theText.ToExtString()),
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     glRotatef (180.0f, 1.0f, 0.0f, 0.0f);
460   }
461   else
462   {
463     // align coordinates to the nearest integer
464     // to avoid extra interpolation issues
465     GLdouble anObjX, anObjY, anObjZ;
466     gluUnProject (std::floor (myWinX + (GLdouble )theDVec.x()),
467                   std::floor (myWinY + (GLdouble )theDVec.y()),
468                   myWinZ + (GLdouble )theDVec.z(),
469                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
470                   &anObjX, &anObjY, &anObjZ);
471
472     glLoadIdentity();
473     glTranslated (anObjX, anObjY, anObjZ);
474     glRotated (theTextAspect.Angle(), 0.0, 0.0, 1.0);
475     if (!theTextAspect.IsZoomable())
476     {
477     #ifdef _WIN32
478       // if the context has assigned printer context, use it's parameters
479       if (!thePrintCtx.IsNull())
480       {
481         // get printing scaling in x and y dimensions
482         GLfloat aTextScalex = 1.0f, aTextScaley = 1.0f;
483         thePrintCtx->GetScale (aTextScalex, aTextScaley);
484
485         // text should be scaled in all directions with same
486         // factor to save its proportions, so use height (y) scaling
487         // as it is better for keeping text/3d graphics proportions
488         glScalef (aTextScaley, aTextScaley, aTextScaley);
489       }
490     #endif
491       glScaled (myScaleHeight, myScaleHeight, myScaleHeight);
492     }
493   }
494 }
495
496 // =======================================================================
497 // function : drawText
498 // purpose  :
499 // =======================================================================
500
501 void OpenGl_Text::drawText (const Handle(OpenGl_PrinterContext)& ,
502                             const Handle(OpenGl_Context)&        theCtx,
503                           #ifdef HAVE_GL2PS
504                             const OpenGl_AspectText&             theTextAspect) const
505                           #else
506                             const OpenGl_AspectText&                          ) const
507                           #endif
508 {
509 #ifdef HAVE_GL2PS
510   if (theCtx->IsFeedback())
511   {
512     // position of the text and alignment is calculated by transformation matrix
513     exportText (myString, myIs2d, theTextAspect, (Standard_Integer )myExportHeight);
514     return;
515   }
516 #endif
517
518   if (myVertsVbo.Length() == myTextures.Length())
519   {
520     for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
521     {
522       const GLuint aTexId = myTextures.Value (anIter);
523       const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
524       const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
525       aVerts->BindFixed (theCtx, GL_VERTEX_ARRAY);
526       aTCrds->BindFixed (theCtx, GL_TEXTURE_COORD_ARRAY);
527       glBindTexture (GL_TEXTURE_2D, aTexId);
528
529       glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
530
531       glBindTexture (GL_TEXTURE_2D, 0);
532       aTCrds->UnbindFixed (theCtx, GL_TEXTURE_COORD_ARRAY);
533       aVerts->UnbindFixed (theCtx, GL_VERTEX_ARRAY);
534     }
535   }
536   else if (myVertsArray.Length() == myTextures.Length())
537   {
538     glEnableClientState (GL_VERTEX_ARRAY);
539     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
540     for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
541     {
542       const GLuint aTexId = myTextures.Value (anIter);
543       const Handle(OpenGl_Vec2Array)& aVerts = myVertsArray.Value (anIter);
544       const Handle(OpenGl_Vec2Array)& aTCrds = myTCrdsArray.Value (anIter);
545
546       glVertexPointer   (2, GL_FLOAT, 0, (GLfloat* )&aVerts->First());
547       glTexCoordPointer (2, GL_FLOAT, 0, (GLfloat* )&aTCrds->First());
548       glBindTexture (GL_TEXTURE_2D, aTexId);
549
550       glDrawArrays (GL_TRIANGLES, 0, aVerts->Length());
551
552       glBindTexture (GL_TEXTURE_2D, 0);
553     }
554     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
555     glDisableClientState (GL_VERTEX_ARRAY);
556   }
557 }
558
559 // =======================================================================
560 // function : FontKey
561 // purpose  :
562 // =======================================================================
563 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
564                                               const Standard_Integer   theHeight)
565 {
566   const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
567   return theAspect.FontName()
568        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
569        + TCollection_AsciiString(":") + theHeight;
570 }
571
572 // =======================================================================
573 // function : FindFont
574 // purpose  :
575 // =======================================================================
576 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
577                                            const OpenGl_AspectText&      theAspect,
578                                            const Standard_Integer        theHeight,
579                                            const TCollection_AsciiString theKey)
580 {
581   Handle(OpenGl_Font) aFont;
582   if (theHeight < 2)
583   {
584     return aFont; // invalid parameters
585   }
586
587   if (!theCtx->GetResource (theKey, aFont))
588   {
589     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
590     const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.FontName());
591     const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
592     Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
593     if (aRequestedFont.IsNull())
594     {
595       return aFont;
596     }
597
598     Handle(Font_FTFont) aFontFt = new Font_FTFont (NULL);
599     if (!aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight))
600     {
601       return aFont;
602     }
603
604     Handle(OpenGl_Context) aCtx = theCtx;
605     glPushAttrib (GL_TEXTURE_BIT);
606     aFont = new OpenGl_Font (aFontFt, theKey);
607     if (!aFont->Init (aCtx))
608     {
609       //glPopAttrib();
610       //return aFont; // out of resources?
611     }
612     glPopAttrib(); // texture bit
613
614     aCtx->ShareResource (theKey, aFont);
615   }
616   return aFont;
617 }
618
619 // =======================================================================
620 // function : render
621 // purpose  :
622 // =======================================================================
623 void OpenGl_Text::render (const Handle(OpenGl_PrinterContext)& thePrintCtx,
624                           const Handle(OpenGl_Context)&        theCtx,
625                           const OpenGl_AspectText&             theTextAspect,
626                           const TEL_COLOUR&                    theColorText,
627                           const TEL_COLOUR&                    theColorSubs) const
628 {
629   if (myString.IsEmpty())
630   {
631     return;
632   }
633
634   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height);
635   if (!myFont.IsNull()
636    && !myFont->ResourceKey().IsEqual (aFontKey))
637   {
638     // font changed
639     const_cast<OpenGl_Text* > (this)->Release (theCtx);
640   }
641
642   if (myFont.IsNull())
643   {
644     myFont = FindFont (theCtx, theTextAspect, myParams.Height, aFontKey);
645     if (myFont.IsNull())
646     {
647       return;
648     }
649   }
650
651   if (myTextures.IsEmpty())
652   {
653     OpenGl_TextFormatter aFormatter;
654     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
655     aFormatter.Reset();
656     aFormatter.Append (theCtx, myString, *myFont.operator->());
657     aFormatter.Format();
658
659     if (!theCtx->caps->vboDisable && theCtx->core15 != NULL)
660     {
661       aFormatter.Result (theCtx, myTextures, myVertsVbo, myTCrdsVbo);
662     }
663     else
664     {
665       aFormatter.Result (theCtx, myTextures, myVertsArray, myTCrdsArray);
666     }
667     aFormatter.BndBox (myBndBox);
668   }
669
670   if (myTextures.IsEmpty())
671   {
672     return;
673   }
674
675   myExportHeight = 1.0f;
676   myScaleHeight  = 1.0f;
677
678   glMatrixMode (GL_MODELVIEW);
679   glPushMatrix();
680   if (!myIs2d)
681   {
682     // retrieve active matrices for project/unproject calls
683     glGetDoublev  (GL_MODELVIEW_MATRIX,  myModelMatrix);
684     glGetDoublev  (GL_PROJECTION_MATRIX, myProjMatrix);
685     glGetIntegerv (GL_VIEWPORT,          myViewport);
686     gluProject (myPoint.x(), myPoint.y(), myPoint.z(),
687                 myModelMatrix, myProjMatrix, myViewport,
688                 &myWinX, &myWinY, &myWinZ);
689
690     // compute scale factor for constant text height
691     GLdouble x1, y1, z1;
692     gluUnProject (myWinX, myWinY, myWinZ,
693                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
694                   &x1, &y1, &z1);
695
696     GLdouble x2, y2, z2;
697     const GLdouble h = (GLdouble )myFont->FTFont()->PointSize();
698     gluUnProject (myWinX, myWinY + h - 1.0, myWinZ,
699                   (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
700                   &x2, &y2, &z2);
701
702     myScaleHeight = (y2 - y1) / h;
703     if (theTextAspect.IsZoomable())
704     {
705       myExportHeight = (float )h;
706     }
707   }
708   myExportHeight = (float )myFont->FTFont()->PointSize() / myExportHeight;
709
710   // push enabled flags to the stack
711   glPushAttrib (GL_ENABLE_BIT);
712   glDisable (GL_LIGHTING);
713
714   // setup depth test
715   if (!myIs2d
716    && theTextAspect.StyleType() != Aspect_TOST_ANNOTATION)
717   {
718     glEnable (GL_DEPTH_TEST);
719   }
720   else
721   {
722     glDisable (GL_DEPTH_TEST);
723   }
724
725   // setup alpha test
726   GLint aTexEnvParam = GL_REPLACE;
727   glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
728   if (aTexEnvParam != GL_REPLACE)
729   {
730     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
731   }
732   glAlphaFunc (GL_GEQUAL, 0.285f);
733   glEnable (GL_ALPHA_TEST);
734
735   // setup blending
736   glEnable (GL_BLEND);
737   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
738
739   // activate texture unit
740   glDisable (GL_TEXTURE_1D);
741   glEnable  (GL_TEXTURE_2D);
742   if (theCtx->core13 != NULL)
743   {
744     theCtx->core13->glActiveTexture (GL_TEXTURE0);
745   }
746
747   // extra drawings
748   switch (theTextAspect.DisplayType())
749   {
750     case Aspect_TODT_BLEND:
751     {
752       glEnable  (GL_COLOR_LOGIC_OP);
753       glLogicOp (GL_XOR);
754       break;
755     }
756     case Aspect_TODT_SUBTITLE:
757     {
758       glColor3fv  (theColorSubs.rgb);
759       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
760
761       glBindTexture (GL_TEXTURE_2D, 0);
762       glBegin (GL_QUADS);
763       glVertex2f (myBndBox.Left,  myBndBox.Top);
764       glVertex2f (myBndBox.Right, myBndBox.Top);
765       glVertex2f (myBndBox.Right, myBndBox.Bottom);
766       glVertex2f (myBndBox.Left,  myBndBox.Bottom);
767       glEnd();
768       break;
769     }
770     case Aspect_TODT_DEKALE:
771     {
772       glColor3fv  (theColorSubs.rgb);
773       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.00001f));
774       drawText    (thePrintCtx, theCtx, theTextAspect);
775       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.00001f));
776       drawText    (thePrintCtx, theCtx, theTextAspect);
777       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.00001f));
778       drawText    (thePrintCtx, theCtx, theTextAspect);
779       setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.00001f));
780       drawText    (thePrintCtx, theCtx, theTextAspect);
781       break;
782     }
783     case Aspect_TODT_DIMENSION:
784     case Aspect_TODT_NORMAL:
785     {
786       break;
787     }
788   }
789
790   // main draw call
791   glColor3fv  (theColorText.rgb);
792   setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
793   drawText    (thePrintCtx, theCtx, theTextAspect);
794
795   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
796
797   if (theTextAspect.DisplayType() == Aspect_TODT_DIMENSION)
798   {
799     setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
800
801     glDisable (GL_BLEND);
802     glDisable (GL_TEXTURE_2D);
803     glDisable (GL_ALPHA_TEST);
804     if (!myIs2d)
805     {
806       glDisable (GL_DEPTH_TEST);
807     }
808     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
809
810     glClear (GL_STENCIL_BUFFER_BIT);
811     glEnable (GL_STENCIL_TEST);
812     glStencilFunc (GL_ALWAYS, 1, 0xFF);
813     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
814
815     glBegin (GL_QUADS);
816     glVertex2f (myBndBox.Left,  myBndBox.Top);
817     glVertex2f (myBndBox.Right, myBndBox.Top);
818     glVertex2f (myBndBox.Right, myBndBox.Bottom);
819     glVertex2f (myBndBox.Left,  myBndBox.Bottom);
820     glEnd();
821
822     glStencilFunc (GL_ALWAYS, 0, 0xFF);
823     // glPopAttrib() will reset state for us
824     //glDisable (GL_STENCIL_TEST);
825     //if (!myIs2d) glEnable (GL_DEPTH_TEST);
826
827     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
828   }
829
830   // revert OpenGL state
831   glPopAttrib(); // enable bit
832   glPopMatrix(); // model view matrix was modified
833 }