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