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