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