1 // Created on: 2011-07-13
2 // Created by: Sergey ZERCHANINOV
3 // Copyright (c) 2011-2013 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
25 #include <Font_FontMgr.hxx>
26 #include <TCollection_HAsciiString.hxx>
34 static const GLdouble THE_IDENTITY_MATRIX[4][4] =
43 static char const* TheFamily[] = {"Helvetica", "Courier", "Times"};
44 static char const* TheItalic[] = {"Oblique", "Oblique", "Italic"};
45 static char const* TheBase[] = {"", "", "-Roman"};
47 //! Convert font name used for rendering to some "good" font names
48 //! that produce good vector text.
49 static void getGL2PSFontName (const char* theSrcFont,
52 if (strstr (theSrcFont, "Symbol"))
54 sprintf (thePsFont, "%s", "Symbol");
57 else if (strstr (theSrcFont, "ZapfDingbats"))
59 sprintf (thePsFont, "%s", "WingDings");
65 bool isItalic = false;
66 if (strstr (theSrcFont, "Courier"))
70 else if (strstr (theSrcFont, "Times"))
75 if (strstr (theSrcFont, "Bold"))
79 if (strstr (theSrcFont, "Italic")
80 || strstr (theSrcFont, "Oblique"))
89 sprintf (thePsFont, "%s-Bold%s", TheFamily[aFontId], TheItalic[aFontId]);
93 sprintf (thePsFont, "%s-Bold", TheFamily[aFontId]);
98 sprintf (thePsFont, "%s-%s", TheFamily[aFontId], TheItalic[aFontId]);
102 sprintf (thePsFont, "%s%s", TheFamily[aFontId], TheBase[aFontId]);
106 static void exportText (const NCollection_String& theText,
107 const Standard_Boolean theIs2d,
108 const OpenGl_AspectText& theAspect,
109 const Standard_Integer theHeight)
113 getGL2PSFontName (theAspect.FontName().ToCString(), aPsFont);
117 glRasterPos2f (0.0f, 0.0f);
121 glRasterPos3f (0.0f, 0.0f, 0.0f);
125 glBitmap (1, 1, 0, 0, 0, 0, &aZero);
127 // Standard GL2PS's alignment isn't used, because it doesn't work correctly
128 // for all formats, therefore alignment is calculated manually relative
129 // to the bottom-left corner, which corresponds to the GL2PS_TEXT_BL value
130 gl2psTextOpt (theText.ToCString(), aPsFont, (GLshort)theHeight, GL2PS_TEXT_BL, theAspect.Angle());
136 // =======================================================================
137 // function : OpenGl_Text
139 // =======================================================================
140 OpenGl_Text::OpenGl_Text()
144 myScaleHeight (1.0f),
145 myPoint (0.0f, 0.0f, 0.0f),
148 myParams.Height = 10;
149 myParams.HAlign = Graphic3d_HTA_LEFT;
150 myParams.VAlign = Graphic3d_VTA_BOTTOM;
153 // =======================================================================
154 // function : OpenGl_Text
156 // =======================================================================
157 OpenGl_Text::OpenGl_Text (const Standard_Utf8Char* theText,
158 const OpenGl_Vec3& thePoint,
159 const OpenGl_TextParam& theParams)
163 myScaleHeight (1.0f),
164 myExportHeight (1.0f),
165 myParams (theParams),
173 // =======================================================================
174 // function : SetPosition
176 // =======================================================================
177 void OpenGl_Text::SetPosition (const OpenGl_Vec3& thePoint)
182 // =======================================================================
183 // function : SetFontSize
185 // =======================================================================
186 void OpenGl_Text::SetFontSize (const Handle(OpenGl_Context)& theCtx,
187 const Standard_Integer theFontSize)
189 if (myParams.Height != theFontSize)
191 Release (theCtx.operator->());
193 myParams.Height = theFontSize;
196 // =======================================================================
199 // =======================================================================
200 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
201 const Standard_Utf8Char* theText,
202 const OpenGl_Vec3& thePoint)
204 releaseVbos (theCtx.operator->());
207 myString.FromUnicode (theText);
210 // =======================================================================
213 // =======================================================================
214 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
215 const Standard_Utf8Char* theText,
216 const OpenGl_Vec3& thePoint,
217 const OpenGl_TextParam& theParams)
219 if (myParams.Height != theParams.Height)
221 Release (theCtx.operator->());
225 releaseVbos (theCtx.operator->());
228 myParams = theParams;
230 myString.FromUnicode (theText);
233 // =======================================================================
236 // =======================================================================
237 void OpenGl_Text::Init (const Handle(OpenGl_Context)& theCtx,
238 const TCollection_ExtendedString& theText,
239 const OpenGl_Vec2& thePoint,
240 const OpenGl_TextParam& theParams)
242 if (myParams.Height != theParams.Height)
244 Release (theCtx.operator->());
248 releaseVbos (theCtx.operator->());
251 myParams = theParams;
252 myPoint.xy() = thePoint;
254 myString.FromUnicode ((Standard_Utf16Char* )theText.ToExtString());
257 // =======================================================================
258 // function : ~OpenGl_Text
260 // =======================================================================
261 OpenGl_Text::~OpenGl_Text()
266 // =======================================================================
267 // function : releaseVbos
269 // =======================================================================
270 void OpenGl_Text::releaseVbos (OpenGl_Context* theCtx)
272 for (Standard_Integer anIter = 0; anIter < myVertsVbo.Length(); ++anIter)
274 Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.ChangeValue (anIter);
275 Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.ChangeValue (anIter);
279 theCtx->DelayedRelease (aVerts);
280 theCtx->DelayedRelease (aTCrds);
290 // =======================================================================
291 // function : Release
293 // =======================================================================
294 void OpenGl_Text::Release (OpenGl_Context* theCtx)
296 releaseVbos (theCtx);
297 if (!myFont.IsNull())
299 Handle(OpenGl_Context) aCtx = theCtx;
300 const TCollection_AsciiString aKey = myFont->ResourceKey();
303 aCtx->ReleaseResource (aKey, Standard_True);
307 // =======================================================================
308 // function : StringSize
310 // =======================================================================
311 void OpenGl_Text::StringSize (const Handle(OpenGl_Context)& theCtx,
312 const NCollection_String& theText,
313 const OpenGl_AspectText& theTextAspect,
314 const OpenGl_TextParam& theParams,
315 Standard_ShortReal& theWidth,
316 Standard_ShortReal& theAscent,
317 Standard_ShortReal& theDescent)
322 const TCollection_AsciiString aFontKey = FontKey (theTextAspect, theParams.Height);
323 Handle(OpenGl_Font) aFont = FindFont (theCtx, theTextAspect, theParams.Height, aFontKey);
324 if (aFont.IsNull() || !aFont->IsValid())
329 theAscent = aFont->Ascender();
330 theDescent = aFont->Descender();
332 GLfloat aWidth = 0.0f;
333 for (NCollection_Utf8Iter anIter = theText.Iterator(); *anIter != 0;)
335 const Standard_Utf32Char aCharThis = *anIter;
336 const Standard_Utf32Char aCharNext = *++anIter;
338 if (aCharThis == '\x0D' // CR (carriage return)
339 || aCharThis == '\a' // BEL (alarm)
340 || aCharThis == '\f' // FF (form feed) NP (new page)
341 || aCharThis == '\b' // BS (backspace)
342 || aCharThis == '\v') // VT (vertical tab)
344 continue; // skip unsupported carriage control codes
346 else if (aCharThis == '\x0A') // LF (line feed, new line)
348 theWidth = Max (theWidth, aWidth);
352 else if (aCharThis == ' ')
354 aWidth += aFont->AdvanceX (aCharThis, aCharNext);
357 else if (aCharThis == '\t')
359 aWidth += aFont->AdvanceX (' ', aCharNext) * 8.0f;
363 aWidth += aFont->AdvanceX (aCharThis, aCharNext);
365 theWidth = Max (theWidth, aWidth);
367 Handle(OpenGl_Context) aCtx = theCtx;
369 aCtx->ReleaseResource (aFontKey, Standard_True);
372 // =======================================================================
375 // =======================================================================
376 void OpenGl_Text::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
378 const OpenGl_AspectText* aTextAspect = theWorkspace->AspectText (Standard_True);
379 const Handle(OpenGl_Texture) aPrevTexture = theWorkspace->DisableTexture();
381 const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
383 if (aCtx->IsGlGreaterEqual (2, 0))
385 const Handle(OpenGl_ShaderProgram)& aProgram = aTextAspect->ShaderProgramRes (theWorkspace);
386 aCtx->BindProgram (aProgram);
387 if (!aProgram.IsNull())
389 aProgram->ApplyVariables (aCtx);
391 const OpenGl_MaterialState* aMaterialState = aCtx->ShaderManager()->MaterialState (aProgram);
393 if (aMaterialState == NULL || aMaterialState->Aspect() != aTextAspect)
394 aCtx->ShaderManager()->UpdateMaterialStateTo (aProgram, aTextAspect);
396 aCtx->ShaderManager()->PushState (aProgram);
400 // use highlight color or colors from aspect
401 if (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT)
403 render (theWorkspace->PrinterContext(),
406 *theWorkspace->HighlightColor,
407 *theWorkspace->HighlightColor);
411 render (theWorkspace->PrinterContext(),
414 aTextAspect->Color(),
415 aTextAspect->SubtitleColor());
419 if (!aPrevTexture.IsNull())
421 theWorkspace->EnableTexture (aPrevTexture);
425 // =======================================================================
428 // =======================================================================
429 void OpenGl_Text::Render (const Handle(OpenGl_PrinterContext)& thePrintCtx,
430 const Handle(OpenGl_Context)& theCtx,
431 const OpenGl_AspectText& theTextAspect) const
433 render (thePrintCtx, theCtx, theTextAspect, theTextAspect.Color(), theTextAspect.SubtitleColor());
436 // =======================================================================
437 // function : setupMatrix
439 // =======================================================================
440 void OpenGl_Text::setupMatrix (const Handle(OpenGl_PrinterContext)& thePrintCtx,
441 const Handle(OpenGl_Context)& /*theCtx*/,
442 const OpenGl_AspectText& theTextAspect,
443 const OpenGl_Vec3 theDVec) const
449 glTranslatef (myPoint.x() + theDVec.x(), myPoint.y() + theDVec.y(), 0.0f);
450 glScalef (1.0f, -1.0f, 1.0f);
451 glRotatef (theTextAspect.Angle(), 0.0, 0.0, 1.0);
455 // align coordinates to the nearest integer
456 // to avoid extra interpolation issues
457 GLdouble anObjX, anObjY, anObjZ;
458 gluUnProject (std::floor (myWinX + (GLdouble )theDVec.x()),
459 std::floor (myWinY + (GLdouble )theDVec.y()),
460 myWinZ + (GLdouble )theDVec.z(),
461 (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
462 &anObjX, &anObjY, &anObjZ);
465 glTranslated (anObjX, anObjY, anObjZ);
466 glRotated (theTextAspect.Angle(), 0.0, 0.0, 1.0);
467 if (!theTextAspect.IsZoomable())
470 // if the context has assigned printer context, use it's parameters
471 if (!thePrintCtx.IsNull())
473 // get printing scaling in x and y dimensions
474 GLfloat aTextScalex = 1.0f, aTextScaley = 1.0f;
475 thePrintCtx->GetScale (aTextScalex, aTextScaley);
477 // text should be scaled in all directions with same
478 // factor to save its proportions, so use height (y) scaling
479 // as it is better for keeping text/3d graphics proportions
480 glScalef (aTextScaley, aTextScaley, aTextScaley);
483 glScaled (myScaleHeight, myScaleHeight, myScaleHeight);
488 // =======================================================================
489 // function : drawText
491 // =======================================================================
493 void OpenGl_Text::drawText (const Handle(OpenGl_PrinterContext)& ,
494 const Handle(OpenGl_Context)& theCtx,
496 const OpenGl_AspectText& theTextAspect) const
498 const OpenGl_AspectText& ) const
502 if (theCtx->IsFeedback())
504 // position of the text and alignment is calculated by transformation matrix
505 exportText (myString, myIs2d, theTextAspect, (Standard_Integer )myExportHeight);
510 if (myVertsVbo.Length() != myTextures.Length()
511 || myTextures.IsEmpty())
516 for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
518 const GLuint aTexId = myTextures.Value (anIter);
519 glBindTexture (GL_TEXTURE_2D, aTexId);
521 const Handle(OpenGl_VertexBuffer)& aVerts = myVertsVbo.Value (anIter);
522 const Handle(OpenGl_VertexBuffer)& aTCrds = myTCrdsVbo.Value (anIter);
523 aVerts->BindAttribute (theCtx, Graphic3d_TOA_POS);
524 aTCrds->BindAttribute (theCtx, Graphic3d_TOA_UV);
526 glDrawArrays (GL_TRIANGLES, 0, GLsizei(aVerts->GetElemsNb()));
528 aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_UV);
529 aVerts->UnbindAttribute (theCtx, Graphic3d_TOA_POS);
531 glBindTexture (GL_TEXTURE_2D, 0);
534 // =======================================================================
535 // function : FontKey
537 // =======================================================================
538 TCollection_AsciiString OpenGl_Text::FontKey (const OpenGl_AspectText& theAspect,
539 const Standard_Integer theHeight)
541 const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
542 return theAspect.FontName()
543 + TCollection_AsciiString(":") + Standard_Integer(anAspect)
544 + TCollection_AsciiString(":") + theHeight;
547 // =======================================================================
548 // function : FindFont
550 // =======================================================================
551 Handle(OpenGl_Font) OpenGl_Text::FindFont (const Handle(OpenGl_Context)& theCtx,
552 const OpenGl_AspectText& theAspect,
553 const Standard_Integer theHeight,
554 const TCollection_AsciiString theKey)
556 Handle(OpenGl_Font) aFont;
559 return aFont; // invalid parameters
562 if (!theCtx->GetResource (theKey, aFont))
564 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
565 const Handle(TCollection_HAsciiString) aFontName = new TCollection_HAsciiString (theAspect.FontName());
566 const Font_FontAspect anAspect = (theAspect.FontAspect() != Font_FA_Undefined) ? theAspect.FontAspect() : Font_FA_Regular;
567 Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (aFontName, anAspect, theHeight);
568 if (aRequestedFont.IsNull())
573 Handle(Font_FTFont) aFontFt = new Font_FTFont (NULL);
574 if (!aFontFt->Init (aRequestedFont->FontPath()->ToCString(), theHeight))
579 Handle(OpenGl_Context) aCtx = theCtx;
580 glPushAttrib (GL_TEXTURE_BIT);
581 aFont = new OpenGl_Font (aFontFt, theKey);
582 if (!aFont->Init (aCtx))
585 //return aFont; // out of resources?
587 glPopAttrib(); // texture bit
589 aCtx->ShareResource (theKey, aFont);
594 // =======================================================================
597 // =======================================================================
598 void OpenGl_Text::render (const Handle(OpenGl_PrinterContext)& thePrintCtx,
599 const Handle(OpenGl_Context)& theCtx,
600 const OpenGl_AspectText& theTextAspect,
601 const TEL_COLOUR& theColorText,
602 const TEL_COLOUR& theColorSubs) const
604 if (myString.IsEmpty())
609 const TCollection_AsciiString aFontKey = FontKey (theTextAspect, myParams.Height);
611 && !myFont->ResourceKey().IsEqual (aFontKey))
614 const_cast<OpenGl_Text* > (this)->Release (theCtx.operator->());
619 myFont = FindFont (theCtx, theTextAspect, myParams.Height, aFontKey);
626 if (myTextures.IsEmpty())
628 OpenGl_TextFormatter aFormatter;
629 aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign);
631 aFormatter.Append (theCtx, myString, *myFont.operator->());
634 aFormatter.Result (theCtx, myTextures, myVertsVbo, myTCrdsVbo);
635 aFormatter.BndBox (myBndBox);
638 if (myTextures.IsEmpty())
643 myExportHeight = 1.0f;
644 myScaleHeight = 1.0f;
646 glMatrixMode (GL_MODELVIEW);
650 // retrieve active matrices for project/unproject calls
651 glGetDoublev (GL_MODELVIEW_MATRIX, myModelMatrix);
652 glGetDoublev (GL_PROJECTION_MATRIX, myProjMatrix);
653 glGetIntegerv (GL_VIEWPORT, myViewport);
654 gluProject (myPoint.x(), myPoint.y(), myPoint.z(),
655 myModelMatrix, myProjMatrix, myViewport,
656 &myWinX, &myWinY, &myWinZ);
658 // compute scale factor for constant text height
660 gluUnProject (myWinX, myWinY, myWinZ,
661 (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
665 const GLdouble h = (GLdouble )myFont->FTFont()->PointSize();
666 gluUnProject (myWinX, myWinY + h, myWinZ,
667 (GLdouble* )THE_IDENTITY_MATRIX, myProjMatrix, myViewport,
670 myScaleHeight = (y2 - y1) / h;
671 if (theTextAspect.IsZoomable())
673 myExportHeight = (float )h;
676 myExportHeight = (float )myFont->FTFont()->PointSize() / myExportHeight;
678 // push enabled flags to the stack
679 glPushAttrib (GL_ENABLE_BIT);
680 glDisable (GL_LIGHTING);
684 && theTextAspect.StyleType() != Aspect_TOST_ANNOTATION)
686 glEnable (GL_DEPTH_TEST);
690 glDisable (GL_DEPTH_TEST);
694 GLint aTexEnvParam = GL_REPLACE;
695 glGetTexEnviv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &aTexEnvParam);
696 if (aTexEnvParam != GL_REPLACE)
698 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
700 glAlphaFunc (GL_GEQUAL, 0.285f);
701 glEnable (GL_ALPHA_TEST);
705 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
707 // activate texture unit
708 glDisable (GL_TEXTURE_1D);
709 glEnable (GL_TEXTURE_2D);
710 if (theCtx->core15fwd != NULL)
712 theCtx->core15fwd->glActiveTexture (GL_TEXTURE0);
716 switch (theTextAspect.DisplayType())
718 case Aspect_TODT_BLEND:
720 glEnable (GL_COLOR_LOGIC_OP);
724 case Aspect_TODT_SUBTITLE:
726 glColor3fv (theColorSubs.rgb);
727 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
729 glBindTexture (GL_TEXTURE_2D, 0);
731 glVertex2f (myBndBox.Left, myBndBox.Top);
732 glVertex2f (myBndBox.Right, myBndBox.Top);
733 glVertex2f (myBndBox.Right, myBndBox.Bottom);
734 glVertex2f (myBndBox.Left, myBndBox.Bottom);
738 case Aspect_TODT_DEKALE:
740 glColor3fv (theColorSubs.rgb);
741 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, +1.0f, 0.00001f));
742 drawText (thePrintCtx, theCtx, theTextAspect);
743 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, -1.0f, 0.00001f));
744 drawText (thePrintCtx, theCtx, theTextAspect);
745 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (-1.0f, +1.0f, 0.00001f));
746 drawText (thePrintCtx, theCtx, theTextAspect);
747 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (+1.0f, -1.0f, 0.00001f));
748 drawText (thePrintCtx, theCtx, theTextAspect);
751 case Aspect_TODT_DIMENSION:
752 case Aspect_TODT_NORMAL:
759 glColor3fv (theColorText.rgb);
760 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.0f));
761 drawText (thePrintCtx, theCtx, theTextAspect);
763 glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, aTexEnvParam);
765 if (theTextAspect.DisplayType() == Aspect_TODT_DIMENSION)
767 setupMatrix (thePrintCtx, theCtx, theTextAspect, OpenGl_Vec3 (0.0f, 0.0f, 0.00001f));
769 glDisable (GL_BLEND);
770 glDisable (GL_TEXTURE_2D);
771 glDisable (GL_ALPHA_TEST);
774 glDisable (GL_DEPTH_TEST);
776 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
778 glClear (GL_STENCIL_BUFFER_BIT);
779 glEnable (GL_STENCIL_TEST);
780 glStencilFunc (GL_ALWAYS, 1, 0xFF);
781 glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);
784 glVertex2f (myBndBox.Left, myBndBox.Top);
785 glVertex2f (myBndBox.Right, myBndBox.Top);
786 glVertex2f (myBndBox.Right, myBndBox.Bottom);
787 glVertex2f (myBndBox.Left, myBndBox.Bottom);
790 glStencilFunc (GL_ALWAYS, 0, 0xFF);
791 // glPopAttrib() will reset state for us
792 //glDisable (GL_STENCIL_TEST);
793 //if (!myIs2d) glEnable (GL_DEPTH_TEST);
795 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
798 // revert OpenGL state
799 glPopAttrib(); // enable bit
800 glPopMatrix(); // model view matrix was modified