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