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