0024245: TKOpenGL - use Message_Messenger interface to report issues
[occt.git] / src / OpenGl / OpenGl_Font.cxx
1 // Created on: 2013-01-29
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2013 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20 #include <OpenGl_Font.hxx>
21
22 #include <OpenGl_Context.hxx>
23 #include <Standard_Assert.hxx>
24 #include <TCollection_ExtendedString.hxx>
25
26 IMPLEMENT_STANDARD_HANDLE (OpenGl_Font, OpenGl_Resource)
27 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Font, OpenGl_Resource)
28
29 // =======================================================================
30 // function : OpenGl_Font
31 // purpose  :
32 // =======================================================================
33 OpenGl_Font::OpenGl_Font (const Handle(Font_FTFont)&     theFont,
34                           const TCollection_AsciiString& theKey)
35 : myKey  (theKey),
36   myFont (theFont),
37   myAscender (0.0f),
38   myDescender (0.0f),
39   myLineSpacing (0.0f),
40   myTileSizeX (0),
41   myTileSizeY (0),
42   myLastTileId (-1),
43   myTextureFormat (GL_ALPHA8)
44 {
45   memset (&myLastTilePx, 0, sizeof(myLastTilePx));
46 }
47
48 // =======================================================================
49 // function : ~OpenGl_Font
50 // purpose  :
51 // =======================================================================
52 OpenGl_Font::~OpenGl_Font()
53 {
54   Release (NULL);
55 }
56
57 // =======================================================================
58 // function : Release
59 // purpose  :
60 // =======================================================================
61 void OpenGl_Font::Release (const OpenGl_Context* theCtx)
62 {
63   if (myTextures.IsEmpty())
64   {
65     return;
66   }
67
68   // application can not handle this case by exception - this is bug in code
69   Standard_ASSERT_RETURN (theCtx != NULL,
70     "OpenGl_Font destroyed without GL context! Possible GPU memory leakage...",);
71
72   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
73   {
74     Handle(OpenGl_Texture)& aTexture = myTextures.ChangeValue (anIter);
75     aTexture->Release (theCtx);
76     aTexture.Nullify();
77   }
78   myTextures.Clear();
79 }
80
81 // =======================================================================
82 // function : Init
83 // purpose  :
84 // =======================================================================
85 bool OpenGl_Font::Init (const Handle(OpenGl_Context)& theCtx)
86 {
87   Release (theCtx.operator->());
88   if (myFont.IsNull() || !myFont->IsValid())
89   {
90     return false;
91   }
92
93   myAscender    = myFont->Ascender();
94   myDescender   = myFont->Descender();
95   myLineSpacing = myFont->LineSpacing();
96   myTileSizeX   = myFont->GlyphMaxSizeX();
97   myTileSizeY   = myFont->GlyphMaxSizeY();
98
99   myLastTileId = -1;
100   return createTexture (theCtx);
101 }
102
103 // =======================================================================
104 // function : createTexture
105 // purpose  :
106 // =======================================================================
107 bool OpenGl_Font::createTexture (const Handle(OpenGl_Context)& theCtx)
108 {
109   const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
110
111   Standard_Integer aGlyphsNb = myFont->GlyphsNumber() - myLastTileId + 1;
112
113   const Standard_Integer aTextureSizeX = OpenGl_Context::GetPowerOfTwo (aGlyphsNb * myTileSizeX, aMaxSize);
114   const Standard_Integer aTilesPerRow  = aTextureSizeX / myTileSizeX;
115   const Standard_Integer aTextureSizeY = OpenGl_Context::GetPowerOfTwo (GLint((aGlyphsNb / aTilesPerRow) + 1) * myTileSizeY, aMaxSize);
116
117   memset (&myLastTilePx, 0, sizeof(myLastTilePx));
118   myLastTilePx.Bottom = myTileSizeY;
119
120   myTextures.Append (new OpenGl_Texture());
121   Handle(OpenGl_Texture)& aTexture = myTextures.ChangeLast();
122
123   Image_PixMap aBlackImg;
124   if (!aBlackImg.InitZero (Image_PixMap::ImgGray, Standard_Size(aTextureSizeX), Standard_Size(aTextureSizeY))
125    || !aTexture->Init (theCtx, aBlackImg, Graphic3d_TOT_2D)) // myTextureFormat
126   {
127     TCollection_ExtendedString aMsg;
128     aMsg += "New texture intialization of size ";
129     aMsg += aTextureSizeX;
130     aMsg += "x";
131     aMsg += aTextureSizeY;
132     aMsg += " for textured font has failed.";
133     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_ERROR_ARB, 0, GL_DEBUG_SEVERITY_HIGH_ARB, aMsg);
134     return false;
135   }
136
137   aTexture->Bind (theCtx);
138   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
139   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
140   aTexture->Unbind (theCtx);
141   return true;
142 }
143
144 // =======================================================================
145 // function : renderGlyph
146 // purpose  :
147 // =======================================================================
148 bool OpenGl_Font::renderGlyph (const Handle(OpenGl_Context)& theCtx,
149                                const Standard_Utf32Char      theChar)
150 {
151   if (!myFont->RenderGlyph (theChar))
152   {
153     return false;
154   }
155
156   Handle(OpenGl_Texture)& aTexture = myTextures.ChangeLast();
157
158   const Image_PixMap& anImg = myFont->GlyphImage();
159   const Standard_Integer aTileId = myLastTileId + 1;
160   myLastTilePx.Left  = myLastTilePx.Right + 3;
161   myLastTilePx.Right = myLastTilePx.Left + (Standard_Integer )anImg.SizeX();
162   if (myLastTilePx.Right >= aTexture->SizeX())
163   {
164     myLastTilePx.Left    = 0;
165     myLastTilePx.Right   = (Standard_Integer )anImg.SizeX();
166     myLastTilePx.Top    += myTileSizeY;
167     myLastTilePx.Bottom += myTileSizeY;
168
169     if (myLastTilePx.Bottom >= aTexture->SizeY())
170     {
171       if (!createTexture (theCtx))
172       {
173         return false;
174       }
175       return renderGlyph (theCtx, theChar);
176     }
177   }
178
179   aTexture->Bind (theCtx);
180   glPixelStorei (GL_UNPACK_LSB_FIRST,  GL_FALSE);
181   glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
182   glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
183
184   glTexSubImage2D (GL_TEXTURE_2D, 0,
185                    myLastTilePx.Left, myLastTilePx.Top, (GLsizei )anImg.SizeX(), (GLsizei )anImg.SizeY(),
186                    GL_ALPHA, GL_UNSIGNED_BYTE, anImg.Data());
187
188   OpenGl_Font::Tile aTile;
189   aTile.uv.Left   = GLfloat(myLastTilePx.Left)                / GLfloat(aTexture->SizeX());
190   aTile.uv.Right  = GLfloat(myLastTilePx.Right)               / GLfloat(aTexture->SizeX());
191   aTile.uv.Top    = GLfloat(myLastTilePx.Top)                 / GLfloat(aTexture->SizeY());
192   aTile.uv.Bottom = GLfloat(myLastTilePx.Top + anImg.SizeY()) / GLfloat(aTexture->SizeY());
193   aTile.texture   = aTexture->TextureId();
194   myFont->GlyphRect (aTile.px);
195
196   myLastTileId = aTileId;
197   myTiles.Append (aTile);
198   return true;
199 }
200
201 // =======================================================================
202 // function : RenderGlyph
203 // purpose  :
204 // =======================================================================
205 void OpenGl_Font::RenderGlyph (const Handle(OpenGl_Context)& theCtx,
206                                const Standard_Utf32Char      theUChar,
207                                const Standard_Utf32Char      theUCharNext,
208                                OpenGl_Font::Tile&            theGlyph,
209                                OpenGl_Vec2&                  thePen)
210 {
211   Standard_Integer aTileId = 0;
212   if (!myGlyphMap.Find (theUChar, aTileId))
213   {
214     if (renderGlyph (theCtx, theUChar))
215     {
216       aTileId = myLastTileId;
217     }
218     else
219     {
220       thePen.x() += myFont->AdvanceX (theUChar, theUCharNext);
221       return;
222     }
223     myGlyphMap.Bind (theUChar, aTileId);
224   }
225
226   const OpenGl_Font::Tile& aTile = myTiles.Value (aTileId);
227   theGlyph.px.Top    = thePen.y() + aTile.px.Top;
228   theGlyph.px.Bottom = thePen.y() + aTile.px.Bottom;
229   theGlyph.px.Left   = thePen.x() + aTile.px.Left;
230   theGlyph.px.Right  = thePen.x() + aTile.px.Right;
231   theGlyph.uv        = aTile.uv;
232   theGlyph.texture   = aTile.texture;
233
234   thePen.x() += myFont->AdvanceX (theUChar, theUCharNext);
235 }