0023457: Slow text rendering
[occt.git] / src / OpenGl / OpenGl_Font.cxx
CommitLineData
a174a3c5 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
25IMPLEMENT_STANDARD_HANDLE (OpenGl_Font, OpenGl_Resource)
26IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Font, OpenGl_Resource)
27
28// =======================================================================
29// function : OpenGl_Font
30// purpose :
31// =======================================================================
32OpenGl_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// =======================================================================
51OpenGl_Font::~OpenGl_Font()
52{
53 Release (NULL);
54}
55
56// =======================================================================
57// function : Release
58// purpose :
59// =======================================================================
60void 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// =======================================================================
84bool 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// =======================================================================
106bool 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// =======================================================================
140bool 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// =======================================================================
197void 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}