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