a174a3c5 |
1 | // Created on: 2013-01-29 |
2 | // Created by: Kirill GAVRILOV |
d5f74e42 |
3 | // Copyright (c) 2013-2014 OPEN CASCADE SAS |
a174a3c5 |
4 | // |
973c2be1 |
5 | // This file is part of Open CASCADE Technology software library. |
a174a3c5 |
6 | // |
d5f74e42 |
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 |
973c2be1 |
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. |
a174a3c5 |
12 | // |
973c2be1 |
13 | // Alternatively, this file may be used under the terms of Open CASCADE |
14 | // commercial license or contractual agreement. |
a174a3c5 |
15 | |
16 | #include <OpenGl_Font.hxx> |
17 | |
18 | #include <OpenGl_Context.hxx> |
19 | #include <Standard_Assert.hxx> |
cbf18624 |
20 | #include <TCollection_ExtendedString.hxx> |
a174a3c5 |
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_ALPHA8) |
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 (const 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::ImgGray, Standard_Size(aTextureSizeX), Standard_Size(aTextureSizeY)) |
121 | || !aTexture->Init (theCtx, aBlackImg, Graphic3d_TOT_2D)) // myTextureFormat |
122 | { |
cbf18624 |
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); |
a174a3c5 |
130 | return false; |
131 | } |
132 | |
133 | aTexture->Bind (theCtx); |
134 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); |
135 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); |
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 | glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE); |
177 | glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
178 | glPixelStorei (GL_UNPACK_ALIGNMENT, 1); |
179 | |
180 | glTexSubImage2D (GL_TEXTURE_2D, 0, |
181 | myLastTilePx.Left, myLastTilePx.Top, (GLsizei )anImg.SizeX(), (GLsizei )anImg.SizeY(), |
182 | GL_ALPHA, GL_UNSIGNED_BYTE, anImg.Data()); |
183 | |
184 | OpenGl_Font::Tile aTile; |
185 | aTile.uv.Left = GLfloat(myLastTilePx.Left) / GLfloat(aTexture->SizeX()); |
186 | aTile.uv.Right = GLfloat(myLastTilePx.Right) / GLfloat(aTexture->SizeX()); |
187 | aTile.uv.Top = GLfloat(myLastTilePx.Top) / GLfloat(aTexture->SizeY()); |
188 | aTile.uv.Bottom = GLfloat(myLastTilePx.Top + anImg.SizeY()) / GLfloat(aTexture->SizeY()); |
189 | aTile.texture = aTexture->TextureId(); |
190 | myFont->GlyphRect (aTile.px); |
191 | |
192 | myLastTileId = aTileId; |
193 | myTiles.Append (aTile); |
194 | return true; |
195 | } |
196 | |
197 | // ======================================================================= |
198 | // function : RenderGlyph |
199 | // purpose : |
200 | // ======================================================================= |
201 | void OpenGl_Font::RenderGlyph (const Handle(OpenGl_Context)& theCtx, |
202 | const Standard_Utf32Char theUChar, |
203 | const Standard_Utf32Char theUCharNext, |
204 | OpenGl_Font::Tile& theGlyph, |
205 | OpenGl_Vec2& thePen) |
206 | { |
207 | Standard_Integer aTileId = 0; |
208 | if (!myGlyphMap.Find (theUChar, aTileId)) |
209 | { |
210 | if (renderGlyph (theCtx, theUChar)) |
211 | { |
212 | aTileId = myLastTileId; |
213 | } |
214 | else |
215 | { |
216 | thePen.x() += myFont->AdvanceX (theUChar, theUCharNext); |
217 | return; |
218 | } |
219 | myGlyphMap.Bind (theUChar, aTileId); |
220 | } |
221 | |
222 | const OpenGl_Font::Tile& aTile = myTiles.Value (aTileId); |
223 | theGlyph.px.Top = thePen.y() + aTile.px.Top; |
224 | theGlyph.px.Bottom = thePen.y() + aTile.px.Bottom; |
225 | theGlyph.px.Left = thePen.x() + aTile.px.Left; |
226 | theGlyph.px.Right = thePen.x() + aTile.px.Right; |
227 | theGlyph.uv = aTile.uv; |
228 | theGlyph.texture = aTile.texture; |
229 | |
230 | thePen.x() += myFont->AdvanceX (theUChar, theUCharNext); |
231 | } |