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