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