0028915: Configuration, Font_BRepFont - do not include FreeType headers within OCCT...
[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   aCtx->ShaderManager()->BindFontProgram (aTextAspect->ShaderProgramRes (aCtx));
433
434   myOrientationMatrix = theWorkspace->View()->Camera()->OrientationMatrix();
435   myProjMatrix.Convert (aCtx->ProjectionState.Current());
436
437   // use highlight color or colors from aspect
438   render (aCtx,
439           *aTextAspect,
440           theWorkspace->TextColor(),
441           theWorkspace->TextSubtitleColor(),
442           aCtx->Resolution());
443
444   // restore aspects
445   if (!aPrevTexture.IsNull())
446   {
447     theWorkspace->EnableTexture (aPrevTexture);
448   }
449 #if !defined(GL_ES_VERSION_2_0)
450   aCtx->SetPolygonMode         (aPrevPolygonMode);
451   aCtx->SetPolygonHatchEnabled (aPrevHatchingMode);
452 #endif
453
454   // restore Z buffer settings
455   if (theWorkspace->UseZBuffer())
456   {
457     glEnable (GL_DEPTH_TEST);
458   }
459 }
460
461 // =======================================================================
462 // function : Render
463 // purpose  :
464 // =======================================================================
465 void OpenGl_Text::Render (const Handle(OpenGl_Context)& theCtx,
466                           const OpenGl_AspectText&      theTextAspect,
467                           const unsigned int            theResolution) const
468 {
469   render (theCtx, theTextAspect,
470           theTextAspect.Aspect()->ColorRGBA(),
471           theTextAspect.Aspect()->ColorSubTitleRGBA(),
472           theResolution);
473 }
474
475 // =======================================================================
476 // function : setupMatrix
477 // purpose  :
478 // =======================================================================
479 void OpenGl_Text::setupMatrix (const Handle(OpenGl_Context)& theCtx,
480                                const OpenGl_AspectText&      theTextAspect,
481                                const OpenGl_Vec3             theDVec) const
482 {
483   OpenGl_Mat4d aModViewMat;
484   OpenGl_Mat4d aProjectMat;
485   if (myHasPlane && myHasAnchorPoint)
486   {
487     aProjectMat = myProjMatrix * myOrientationMatrix;
488   }
489   else
490   {
491     aProjectMat = myProjMatrix;
492   }
493
494   if (myIs2d)
495   {
496     Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.f);
497     Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, 1.f, -1.f, 1.f);
498     Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.f, 0.f, 1.f);
499   }
500   else
501   {
502     // align coordinates to the nearest integer
503     // to avoid extra interpolation issues
504     GLdouble anObjX, anObjY, anObjZ;
505     Graphic3d_TransformUtils::UnProject<Standard_Real> (std::floor (myWinX + theDVec.x()),
506                                                         std::floor (myWinY + theDVec.y()),
507                                                         myWinZ + theDVec.z(),
508                                                         OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX),
509                                                         OpenGl_Mat4d::Map (aProjectMat),
510                                                         theCtx->Viewport(),
511                                                         anObjX,
512                                                         anObjY,
513                                                         anObjZ);
514
515     if (myHasPlane)
516     {
517       const gp_Dir& aVectorDir   = myOrientation.XDirection();
518       const gp_Dir& aVectorUp    = myOrientation.Direction();
519       const gp_Dir& aVectorRight = myOrientation.YDirection();
520
521       aModViewMat.SetColumn (2, OpenGl_Vec3d (aVectorUp.X(), aVectorUp.Y(), aVectorUp.Z()));
522       aModViewMat.SetColumn (1, OpenGl_Vec3d (aVectorRight.X(), aVectorRight.Y(), aVectorRight.Z()));
523       aModViewMat.SetColumn (0, OpenGl_Vec3d (aVectorDir.X(), aVectorDir.Y(), aVectorDir.Z()));
524
525       if (!myHasAnchorPoint)
526       {
527         OpenGl_Mat4d aPosMat;
528         aPosMat.SetColumn (3, OpenGl_Vec3d (myPoint.x(), myPoint.y(), myPoint.z()));
529         aPosMat *= aModViewMat;
530         aModViewMat.SetColumn (3, aPosMat.GetColumn (3));
531       }
532       else
533       {
534         aModViewMat.SetColumn (3, OpenGl_Vec3d (anObjX, anObjY, anObjZ));
535       }
536     }
537     else
538     {
539       Graphic3d_TransformUtils::Translate<GLdouble> (aModViewMat, anObjX, anObjY, anObjZ);
540       Graphic3d_TransformUtils::Rotate<GLdouble> (aModViewMat, theTextAspect.Aspect()->GetTextAngle(), 0.0, 0.0, 1.0);
541     }
542
543     if (!theTextAspect.Aspect()->GetTextZoomable())
544     {
545       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, myScaleHeight, myScaleHeight, myScaleHeight);
546     }
547     else if (theCtx->HasRenderScale())
548     {
549       Graphic3d_TransformUtils::Scale<GLdouble> (aModViewMat, theCtx->RenderScaleInv(), theCtx->RenderScaleInv(), theCtx->RenderScaleInv());
550     }
551   }
552
553   if (myHasPlane && !myHasAnchorPoint)
554   {
555     OpenGl_Mat4d aCurrentWorldViewMat;
556     aCurrentWorldViewMat.Convert (theCtx->WorldViewState.Current());
557     theCtx->WorldViewState.SetCurrent<Standard_Real> (aCurrentWorldViewMat * aModViewMat);
558   }
559   else
560   {
561     theCtx->WorldViewState.SetCurrent<Standard_Real> (aModViewMat);
562   }
563   theCtx->ApplyWorldViewMatrix();
564
565   if (!myIs2d)
566   {
567     theCtx->ProjectionState.SetCurrent<Standard_Real> (aProjectMat);
568     theCtx->ApplyProjectionMatrix();
569   }
570
571   // Upload updated state to shader program
572   theCtx->ShaderManager()->PushState (theCtx->ActiveProgram());
573 }
574
575 // =======================================================================
576 // function : drawText
577 // purpose  :
578 // =======================================================================
579 void OpenGl_Text::drawText (const Handle(OpenGl_Context)& theCtx,
580                             const OpenGl_AspectText&      theTextAspect) const
581 {
582 #ifdef HAVE_GL2PS
583   if (theCtx->IsFeedback())
584   {
585     // position of the text and alignment is calculated by transformation matrix
586     exportText (myString, myIs2d, theTextAspect, (Standard_Integer )myExportHeight);
587     return;
588   }
589 #else
590   (void )theTextAspect;
591 #endif
592
593   if (myVertsVbo.Length() != myTextures.Length()
594    || myTextures.IsEmpty())
595   {
596     return;
597   }
598
599   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
600   {
601     const GLuint aTexId = myTextures.Value (anIter);
602     glBindTexture (GL_TEXTURE_2D, aTexId);
603
604     const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
605     const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
606     aVerts->BindAttribute (theCtx, Graphic3d_TOA_POS);
607     aTCrds->BindAttribute (theCtx, Graphic3d_TOA_UV);
608
609     glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
610
611     aTCrds->UnbindAttribute (theCtx, Graphic3d_TOA_UV);
612     aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
613   }
614   glBindTexture (GL_TEXTURE_2D, 0);
615 }
616
617 // =======================================================================
618 // function : FontKey
619 // purpose  :
620 // =======================================================================
621 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
622                                               const Standard_Integer   theHeight,
623                                               const unsigned int       theResolution)
624 {
625   const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
626                                  ? theAspect.Aspect()->GetTextFontAspect()
627                                  : Font_FA_Regular;
628   return theAspect.Aspect()->Font()
629        + TCollection_AsciiString(":") + Standard_Integer(anAspect)
630        + TCollection_AsciiString(":") + Standard_Integer(theResolution)
631        + TCollection_AsciiString(":") + theHeight;
632 }
633
634 // =======================================================================
635 // function : FindFont
636 // purpose  :
637 // =======================================================================
638 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
639                                            const OpenGl_AspectText&      theAspect,
640                                            const Standard_Integer        theHeight,
641                                            const unsigned int            theResolution,
642                                            const TCollection_AsciiString theKey)
643 {
644   Handle(OpenGl_Font) aFont;
645   if (theHeight < 2)
646   {
647     return aFont; // invalid parameters
648   }
649
650   if (!theCtx->GetResource (theKey, aFont))
651   {
652     Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
653     const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.Aspect()->Font());
654     const Font_FontAspect anAspect = theAspect.Aspect()->GetTextFontAspect() != Font_FA_Undefined
655                                    ? theAspect.Aspect()->GetTextFontAspect()
656                                    : Font_FA_Regular;
657     Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
658     Handle(Font_FTFont) aFontFt;
659     if (!aRequestedFont.IsNull())
660     {
661       aFontFt = new Font_FTFont (Handle(Font_FTLibrary)());
662
663       if (aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight, theResolution))
664       {
665         aFont = new OpenGl_Font (aFontFt, theKey);
666         if (!aFont->Init (theCtx))
667         {
668           TCollection_ExtendedString aMsg;
669           aMsg += "Font '";
670           aMsg += theAspect.Aspect()->Font();
671           aMsg += "' - initialization of GL resources has failed!";
672           theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
673           aFontFt.Nullify();
674           aFont->Release (theCtx.operator->());
675           aFont = new OpenGl_Font (aFontFt, theKey);
676         }
677       }
678       else
679       {
680         TCollection_ExtendedString aMsg;
681         aMsg += "Font '";
682         aMsg += theAspect.Aspect()->Font();
683         aMsg += "' is broken or has incompatible format! File path: ";
684         aMsg += aRequestedFont->FontPath()->ToCString();
685         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
686         aFontFt.Nullify();
687         aFont = new OpenGl_Font (aFontFt, theKey);
688       }
689     }
690     else
691     {
692       TCollection_ExtendedString aMsg;
693       aMsg += "Font '";
694       aMsg += theAspect.Aspect()->Font();
695       aMsg += "' is not found in the system!";
696       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, aMsg);
697       aFont = new OpenGl_Font (aFontFt, theKey);
698     }
699
700     theCtx->ShareResource (theKey, aFont);
701   }
702   return aFont;
703 }
704
705 // =======================================================================
706 // function : drawRect
707 // purpose  :
708 // =======================================================================
709 void OpenGl_Text::drawRect (const Handle(OpenGl_Context)& theCtx,
710                             const OpenGl_AspectText&      theTextAspect,
711                             const OpenGl_Vec4&            theColorSubs) const
712 {
713   Handle(OpenGl_ShaderProgram) aPrevProgram = theCtx->ActiveProgram();
714   if (myBndVertsVbo.IsNull())
715   {
716     OpenGl_Vec2 aQuad[4] =
717     {
718       OpenGl_Vec2(myBndBox.Right, myBndBox.Bottom),
719       OpenGl_Vec2(myBndBox.Right, myBndBox.Top),
720       OpenGl_Vec2(myBndBox.Left,  myBndBox.Bottom),
721       OpenGl_Vec2(myBndBox.Left,  myBndBox.Top)
722     };
723     myBndVertsVbo = new OpenGl_VertexBuffer();
724     myBndVertsVbo->Init (theCtx, 2, 4, aQuad[0].GetData());
725   }
726
727   // bind flat program
728   theCtx->ShaderManager()->BindFaceProgram (Handle(OpenGl_Texture)(), Standard_False, Standard_False, Standard_False, Handle(OpenGl_ShaderProgram)());
729
730 #if !defined(GL_ES_VERSION_2_0)
731   if (theCtx->core11 != NULL
732    && theCtx->ActiveProgram().IsNull())
733   {
734     glBindTexture (GL_TEXTURE_2D, 0);
735   }
736 #endif
737   theCtx->SetColor4fv (theColorSubs);
738   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
739   myBndVertsVbo->BindAttribute (theCtx, Graphic3d_TOA_POS);
740
741   theCtx->core20fwd->glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
742
743   myBndVertsVbo->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
744   theCtx->BindProgram (aPrevProgram);
745 }
746
747 // =======================================================================
748 // function : render
749 // purpose  :
750 // =======================================================================
751 void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx,
752                           const OpenGl_AspectText&      theTextAspect,
753                           const OpenGl_Vec4&            theColorText,
754                           const OpenGl_Vec4&            theColorSubs,
755                           const unsigned int            theResolution) const
756 {
757   if (myString.IsEmpty())
758   {
759     return;
760   }
761
762   // Note that using difference resolution in different Views in same Viewer
763   // will lead to performance regression (for example, text will be recreated every time).
764   const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height, theResolution);
765   if (!myFont.IsNull()
766    && !myFont->ResourceKey().IsEqual (aFontKey))
767   {
768     // font changed
769     const_cast<OpenGl_Text* > (this)->Release (theCtx.operator->());
770   }
771
772   if (myFont.IsNull())
773   {
774     myFont = FindFont (theCtx, theTextAspect, myParams.Height, theResolution, aFontKey);
775   }
776   if (!myFont->WasInitialized())
777   {
778     return;
779   }
780
781   if (myTextures.IsEmpty())
782   {
783     Font_TextFormatter aFormatter;
784     aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
785     aFormatter.Reset();
786
787     aFormatter.Append (myString, *myFont->FTFont().operator->());
788     aFormatter.Format();
789
790     OpenGl_TextBuilder aBuilder;
791     aBuilder.Perform (aFormatter,
792                       theCtx,
793                       *myFont.operator->(),
794                       myTextures,
795                       myVertsVbo,
796                       myTCrdsVbo);
797
798     aFormatter.BndBox (myBndBox);
799     if (!myBndVertsVbo.IsNull())
800     {
801       myBndVertsVbo->Release (theCtx.get());
802       myBndVertsVbo.Nullify();
803     }
804   }
805
806   if (myTextures.IsEmpty())
807   {
808     return;
809   }
810
811   myExportHeight = 1.0f;
812   myScaleHeight  = 1.0f;
813
814   theCtx->WorldViewState.Push();
815   myModelMatrix.Convert (theCtx->WorldViewState.Current() * theCtx->ModelWorldState.Current());
816
817   const GLdouble aPointSize = (GLdouble )myFont->FTFont()->PointSize();
818   if (!myIs2d)
819   {
820     Graphic3d_TransformUtils::Project<Standard_Real> (myPoint.x(), myPoint.y(), myPoint.z(),
821                                                       myModelMatrix, myProjMatrix, theCtx->Viewport(),
822                                                       myWinX, myWinY, myWinZ);
823
824     // compute scale factor for constant text height
825     if (theTextAspect.Aspect()->GetTextZoomable())
826     {
827       myExportHeight = aPointSize;
828     }
829     else
830     {
831       Graphic3d_Vec3d aPnt1, aPnt2;
832       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY, myWinZ,
833                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
834                                                           aPnt1.x(), aPnt1.y(), aPnt1.z());
835       Graphic3d_TransformUtils::UnProject<Standard_Real> (myWinX, myWinY + aPointSize, myWinZ,
836                                                           OpenGl_Mat4d::Map (THE_IDENTITY_MATRIX), myProjMatrix, theCtx->Viewport(),
837                                                           aPnt2.x(), aPnt2.y(), aPnt2.z());
838       myScaleHeight = (aPnt2.y() - aPnt1.y()) / aPointSize;
839     }
840   }
841   myExportHeight = aPointSize / myExportHeight;
842
843 #if !defined(GL_ES_VERSION_2_0)
844   if (theCtx->core11 != NULL)
845   {
846     glDisable (GL_LIGHTING);
847   }
848 #endif
849
850   // setup depth test
851   if (myIs2d
852    || theTextAspect.Aspect()->Style() == Aspect_TOST_ANNOTATION)
853   {
854     glDisable (GL_DEPTH_TEST);
855   }
856
857   if (theCtx->core15fwd != NULL)
858   {
859     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
860   }
861 #if !defined(GL_ES_VERSION_2_0)
862   // activate texture unit
863   GLint aTexEnvParam = GL_REPLACE;
864   if (theCtx->core11 != NULL)
865   {
866     // setup alpha test
867     glAlphaFunc (GL_GEQUAL, 0.285f);
868     glEnable (GL_ALPHA_TEST);
869
870     glDisable (GL_TEXTURE_1D);
871     glEnable  (GL_TEXTURE_2D);
872     glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
873     if (aTexEnvParam != GL_REPLACE)
874     {
875       glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
876     }
877   }
878 #endif
879
880   // setup blending
881   glEnable (GL_BLEND);
882   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
883
884   // extra drawings
885   switch (theTextAspect.Aspect()->DisplayType())
886   {
887     case Aspect_TODT_BLEND:
888     {
889     #if !defined(GL_ES_VERSION_2_0)
890       glEnable  (GL_COLOR_LOGIC_OP);
891       glLogicOp (GL_XOR);
892     #endif
893       break;
894     }
895     case Aspect_TODT_SUBTITLE:
896     {
897       drawRect (theCtx, theTextAspect, theColorSubs);
898       break;
899     }
900     case Aspect_TODT_DEKALE:
901     {
902       theCtx->SetColor4fv (theColorSubs);
903       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.00001f));
904       drawText    (theCtx, theTextAspect);
905       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.00001f));
906       drawText    (theCtx, theTextAspect);
907       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.00001f));
908       drawText    (theCtx, theTextAspect);
909       setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.00001f));
910       drawText    (theCtx, theTextAspect);
911       break;
912     }
913     case Aspect_TODT_DIMENSION:
914     case Aspect_TODT_NORMAL:
915     {
916       break;
917     }
918   }
919
920   // main draw call
921   theCtx->SetColor4fv (theColorText);
922   setupMatrix (theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
923   drawText    (theCtx, theTextAspect);
924
925   if (!myIs2d)
926   {
927     theCtx->ProjectionState.SetCurrent<Standard_Real> (myProjMatrix);
928     theCtx->ApplyProjectionMatrix();
929   }
930
931 #if !defined(GL_ES_VERSION_2_0)
932   if (theCtx->core11 != NULL)
933   {
934     glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
935   }
936 #endif
937
938   if (theTextAspect.Aspect()->DisplayType() == Aspect_TODT_DIMENSION)
939   {
940     glDisable (GL_BLEND);
941     if (!myIs2d)
942     {
943       glDisable (GL_DEPTH_TEST);
944     }
945   #if !defined(GL_ES_VERSION_2_0)
946     if (theCtx->core11 != NULL)
947     {
948       glDisable (GL_TEXTURE_2D);
949       glDisable (GL_ALPHA_TEST);
950     }
951   #endif
952     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
953
954     glClear (GL_STENCIL_BUFFER_BIT);
955     glEnable (GL_STENCIL_TEST);
956     glStencilFunc (GL_ALWAYS, 1, 0xFF);
957     glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
958
959     drawRect (theCtx, theTextAspect, OpenGl_Vec4 (1.0f, 1.0f, 1.0f, 1.0f));
960
961     glStencilFunc (GL_ALWAYS, 0, 0xFF);
962
963     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
964   }
965
966   // reset OpenGL state
967   glDisable (GL_BLEND);
968   glDisable (GL_STENCIL_TEST);
969 #if !defined(GL_ES_VERSION_2_0)
970   if (theCtx->core11 != NULL)
971   {
972     glDisable (GL_ALPHA_TEST);
973   }
974   glDisable (GL_COLOR_LOGIC_OP);
975 #endif
976
977   // model view matrix was modified
978   theCtx->WorldViewState.Pop();
979   theCtx->ApplyModelViewMatrix();
980 }