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