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