63f96a627c338bd9fc23f4855611d4335bfa6d84
[occt.git] / src / OpenGl / OpenGl_Font.cxx
1 // Created on: 2013-01-29
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2013-2014 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_Font.hxx>
17
18 #include <OpenGl_Context.hxx>
19 #include <Graphic3d_TextureParams.hxx>
20 #include <Standard_Assert.hxx>
21 #include <TCollection_ExtendedString.hxx>
22
23
24 // =======================================================================
25 // function : OpenGl_Font
26 // purpose  :
27 // =======================================================================
28 OpenGl_Font::OpenGl_Font (const Handle(Font_FTFont)&     theFont,
29                           const TCollection_AsciiString& theKey)
30 : myKey  (theKey),
31   myFont (theFont),
32   myAscender (0.0f),
33   myDescender (0.0f),
34   myLineSpacing (0.0f),
35   myTileSizeX (0),
36   myTileSizeY (0),
37   myLastTileId (-1),
38   myTextureFormat (GL_ALPHA)
39 {
40   memset (&myLastTilePx, 0, sizeof(myLastTilePx));
41 }
42
43 // =======================================================================
44 // function : ~OpenGl_Font
45 // purpose  :
46 // =======================================================================
47 OpenGl_Font::~OpenGl_Font()
48 {
49   Release (NULL);
50 }
51
52 // =======================================================================
53 // function : Release
54 // purpose  :
55 // =======================================================================
56 void OpenGl_Font::Release (OpenGl_Context* theCtx)
57 {
58   if (myTextures.IsEmpty())
59   {
60     return;
61   }
62
63   // application can not handle this case by exception - this is bug in code
64   Standard_ASSERT_RETURN (theCtx != NULL,
65     "OpenGl_Font destroyed without GL context! Possible GPU memory leakage...",);
66
67   for (Standard_Integer anIter = 0; anIter < myTextures.Length(); ++anIter)
68   {
69     Handle(OpenGl_Texture)& aTexture = myTextures.ChangeValue (anIter);
70     aTexture->Release (theCtx);
71     aTexture.Nullify();
72   }
73   myTextures.Clear();
74 }
75
76 // =======================================================================
77 // function : Init
78 // purpose  :
79 // =======================================================================
80 bool OpenGl_Font::Init (const Handle(OpenGl_Context)& theCtx)
81 {
82   Release (theCtx.operator->());
83   if (myFont.IsNull() || !myFont->IsValid())
84   {
85     return false;
86   }
87
88   myAscender    = myFont->Ascender();
89   myDescender   = myFont->Descender();
90   myLineSpacing = myFont->LineSpacing();
91   myTileSizeX   = myFont->GlyphMaxSizeX();
92   myTileSizeY   = myFont->GlyphMaxSizeY();
93
94   myLastTileId = -1;
95   return createTexture (theCtx);
96 }
97
98 // =======================================================================
99 // function : createTexture
100 // purpose  :
101 // =======================================================================
102 bool OpenGl_Font::createTexture (const Handle(OpenGl_Context)& theCtx)
103 {
104   const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
105
106   Standard_Integer aGlyphsNb = myFont->GlyphsNumber() - myLastTileId + 1;
107
108   const Standard_Integer aTextureSizeX = OpenGl_Context::GetPowerOfTwo (aGlyphsNb * myTileSizeX, aMaxSize);
109   const Standard_Integer aTilesPerRow  = aTextureSizeX / myTileSizeX;
110   const Standard_Integer aTextureSizeY = OpenGl_Context::GetPowerOfTwo (GLint((aGlyphsNb / aTilesPerRow) + 1) * myTileSizeY, aMaxSize);
111
112   memset (&myLastTilePx, 0, sizeof(myLastTilePx));
113   myLastTilePx.Bottom = myTileSizeY;
114
115   Handle(Graphic3d_TextureParams) aParams = new Graphic3d_TextureParams();
116   aParams->SetModulate    (Standard_False);
117   aParams->SetRepeat      (Standard_False);
118   aParams->SetFilter      (Graphic3d_TOTF_BILINEAR);
119   aParams->SetAnisoFilter (Graphic3d_LOTA_OFF);
120
121   myTextures.Append (new OpenGl_Texture (aParams));
122   Handle(OpenGl_Texture)& aTexture = myTextures.ChangeLast();
123
124   Image_PixMap aBlackImg;
125   if (!aBlackImg.InitZero (Image_PixMap::ImgAlpha, Standard_Size(aTextureSizeX), Standard_Size(aTextureSizeY))
126    || !aTexture->Init (theCtx, aBlackImg, Graphic3d_TOT_2D)) // myTextureFormat
127   {
128     TCollection_ExtendedString aMsg;
129     aMsg += "New texture intialization of size ";
130     aMsg += aTextureSizeX;
131     aMsg += "x";
132     aMsg += aTextureSizeY;
133     aMsg += " for textured font has failed.";
134     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_ERROR_ARB, 0, GL_DEBUG_SEVERITY_HIGH_ARB, aMsg);
135     return false;
136   }
137
138   return true;
139 }
140
141 // =======================================================================
142 // function : renderGlyph
143 // purpose  :
144 // =======================================================================
145 bool OpenGl_Font::renderGlyph (const Handle(OpenGl_Context)& theCtx,
146                                const Standard_Utf32Char      theChar)
147 {
148   if (!myFont->RenderGlyph (theChar))
149   {
150     return false;
151   }
152
153   Handle(OpenGl_Texture)& aTexture = myTextures.ChangeLast();
154
155   const Image_PixMap& anImg = myFont->GlyphImage();
156   const Standard_Integer aTileId = myLastTileId + 1;
157   myLastTilePx.Left  = myLastTilePx.Right + 3;
158   myLastTilePx.Right = myLastTilePx.Left + (Standard_Integer )anImg.SizeX();
159   if (myLastTilePx.Right >= aTexture->SizeX())
160   {
161     myLastTilePx.Left    = 0;
162     myLastTilePx.Right   = (Standard_Integer )anImg.SizeX();
163     myLastTilePx.Top    += myTileSizeY;
164     myLastTilePx.Bottom += myTileSizeY;
165
166     if (myLastTilePx.Bottom >= aTexture->SizeY())
167     {
168       if (!createTexture (theCtx))
169       {
170         return false;
171       }
172       return renderGlyph (theCtx, theChar);
173     }
174   }
175
176   aTexture->Bind (theCtx);
177 #if !defined(GL_ES_VERSION_2_0)
178   glPixelStorei (GL_UNPACK_LSB_FIRST,  GL_FALSE);
179   glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
180 #endif
181   glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
182
183   glTexSubImage2D (GL_TEXTURE_2D, 0,
184                    myLastTilePx.Left, myLastTilePx.Top, (GLsizei )anImg.SizeX(), (GLsizei )anImg.SizeY(),
185                    GL_ALPHA, GL_UNSIGNED_BYTE, anImg.Data());
186
187   OpenGl_Font::Tile aTile;
188   aTile.uv.Left   = GLfloat(myLastTilePx.Left)                / GLfloat(aTexture->SizeX());
189   aTile.uv.Right  = GLfloat(myLastTilePx.Right)               / GLfloat(aTexture->SizeX());
190   aTile.uv.Top    = GLfloat(myLastTilePx.Top)                 / GLfloat(aTexture->SizeY());
191   aTile.uv.Bottom = GLfloat(myLastTilePx.Top + anImg.SizeY()) / GLfloat(aTexture->SizeY());
192   aTile.texture   = aTexture->TextureId();
193   myFont->GlyphRect (aTile.px);
194
195   myLastTileId = aTileId;
196   myTiles.Append (aTile);
197   return true;
198 }
199
200 // =======================================================================
201 // function : RenderGlyph
202 // purpose  :
203 // =======================================================================
204 void OpenGl_Font::RenderGlyph (const Handle(OpenGl_Context)& theCtx,
205                                const Standard_Utf32Char      theUChar,
206                                const Standard_Utf32Char      theUCharNext,
207                                OpenGl_Font::Tile&            theGlyph,
208                                OpenGl_Vec2&                  thePen)
209 {
210   Standard_Integer aTileId = 0;
211   if (!myGlyphMap.Find (theUChar, aTileId))
212   {
213     if (renderGlyph (theCtx, theUChar))
214     {
215       aTileId = myLastTileId;
216     }
217     else
218     {
219       thePen.x() += myFont->AdvanceX (theUChar, theUCharNext);
220       return;
221     }
222     myGlyphMap.Bind (theUChar, aTileId);
223   }
224
225   const OpenGl_Font::Tile& aTile = myTiles.Value (aTileId);
226   theGlyph.px.Top    = thePen.y() + aTile.px.Top;
227   theGlyph.px.Bottom = thePen.y() + aTile.px.Bottom;
228   theGlyph.px.Left   = thePen.x() + aTile.px.Left;
229   theGlyph.px.Right  = thePen.x() + aTile.px.Right;
230   theGlyph.uv        = aTile.uv;
231   theGlyph.texture   = aTile.texture;
232
233   thePen.x() += myFont->AdvanceX (theUChar, theUCharNext);
234 }