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