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