1 /*
2  * FTGL - OpenGL font library
3  *
4  * Copyright (c) 2001-2004 Henry Maddocks <ftgl@opengl.geek.nz>
5  * Copyright (c) 2008 Sam Hocevar <sam@hocevar.net>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26 
27 #include "config.h"
28 
29 #include <cassert>
30 #include <string> // For memset
31 
32 #include "FTGL/ftgl.h"
33 
34 #include "FTInternals.h"
35 
36 #include "../FTGlyph/FTTextureGlyphImpl.h"
37 #include "./FTTextureFontImpl.h"
38 #include "FTGL/FTLibrary.h"
39 
40 
41 //
42 //  FTTextureFont
43 //
44 
45 
FTTextureFont(char const * fontFilePath)46 FTTextureFont::FTTextureFont(char const *fontFilePath) :
47     FTFont(new FTTextureFontImpl(this, fontFilePath))
48 {}
49 
50 
FTTextureFont(const unsigned char * pBufferBytes,size_t bufferSizeInBytes)51 FTTextureFont::FTTextureFont(const unsigned char *pBufferBytes,
52                              size_t bufferSizeInBytes) :
53     FTFont(new FTTextureFontImpl(this, pBufferBytes, bufferSizeInBytes))
54 {}
55 
56 
~FTTextureFont()57 FTTextureFont::~FTTextureFont()
58 {}
59 
60 
MakeGlyph(FT_GlyphSlot ftGlyph)61 FTGlyph* FTTextureFont::MakeGlyph(FT_GlyphSlot ftGlyph)
62 {
63     FTTextureFontImpl *myimpl = dynamic_cast<FTTextureFontImpl *>(impl);
64     if(!myimpl)
65     {
66         return NULL;
67     }
68 
69     return myimpl->MakeGlyphImpl(ftGlyph);
70 }
71 
72 
73 //
74 //  FTTextureFontImpl
75 //
76 
77 
ClampSize(GLuint in,GLuint maxTextureSize)78 static inline GLuint ClampSize(GLuint in, GLuint maxTextureSize)
79 {
80     // Find next power of two
81     --in;
82     in |= in >> 16;
83     in |= in >> 8;
84     in |= in >> 4;
85     in |= in >> 2;
86     in |= in >> 1;
87     ++in;
88 
89     // Clamp to max texture size
90     return in < maxTextureSize ? in : maxTextureSize;
91 }
92 
93 
FTTextureFontImpl(FTFont * ftFont,const char * fontFilePath)94 FTTextureFontImpl::FTTextureFontImpl(FTFont *ftFont, const char* fontFilePath)
95 :   FTFontImpl(ftFont, fontFilePath),
96     maximumGLTextureSize(0),
97     textureWidth(0),
98     textureHeight(0),
99     glyphHeight(0),
100     glyphWidth(0),
101     padding(3),
102     xOffset(0),
103     yOffset(0)
104 {
105     load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
106     remGlyphs = numGlyphs = face.GlyphCount();
107 }
108 
109 
FTTextureFontImpl(FTFont * ftFont,const unsigned char * pBufferBytes,size_t bufferSizeInBytes)110 FTTextureFontImpl::FTTextureFontImpl(FTFont *ftFont,
111                                      const unsigned char *pBufferBytes,
112                                      size_t bufferSizeInBytes)
113 :   FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes),
114     maximumGLTextureSize(0),
115     textureWidth(0),
116     textureHeight(0),
117     glyphHeight(0),
118     glyphWidth(0),
119     padding(3),
120     xOffset(0),
121     yOffset(0)
122 {
123     load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
124     remGlyphs = numGlyphs = face.GlyphCount();
125 }
126 
127 
~FTTextureFontImpl()128 FTTextureFontImpl::~FTTextureFontImpl()
129 {
130     if(textureIDList.size())
131     {
132         glDeleteTextures((GLsizei)textureIDList.size(),
133                          (const GLuint*)&textureIDList[0]);
134     }
135 }
136 
137 
MakeGlyphImpl(FT_GlyphSlot ftGlyph)138 FTGlyph* FTTextureFontImpl::MakeGlyphImpl(FT_GlyphSlot ftGlyph)
139 {
140     glyphHeight = static_cast<int>(charSize.Height() + 0.5f);
141     glyphWidth = static_cast<int>(charSize.Width() + 0.5f);
142 
143     if(glyphHeight < 1) glyphHeight = 1;
144     if(glyphWidth < 1) glyphWidth = 1;
145 
146     if(textureIDList.empty())
147     {
148         textureIDList.push_back(CreateTexture());
149         xOffset = yOffset = padding;
150     }
151 
152     if(xOffset > (textureWidth - glyphWidth))
153     {
154         xOffset = padding;
155         yOffset += glyphHeight;
156 
157         if(yOffset > (textureHeight - glyphHeight))
158         {
159             textureIDList.push_back(CreateTexture());
160             yOffset = padding;
161         }
162     }
163 
164     FTTextureGlyph* tempGlyph = new FTTextureGlyph(ftGlyph, textureIDList[textureIDList.size() - 1],
165                                                     xOffset, yOffset, textureWidth, textureHeight);
166     xOffset += static_cast<int>(tempGlyph->BBox().Upper().X() - tempGlyph->BBox().Lower().X() + padding + 0.5);
167 
168     --remGlyphs;
169 
170     return tempGlyph;
171 }
172 
173 
CalculateTextureSize()174 void FTTextureFontImpl::CalculateTextureSize()
175 {
176     if(!maximumGLTextureSize)
177     {
178         maximumGLTextureSize = 1024;
179         glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&maximumGLTextureSize);
180         assert(maximumGLTextureSize); // Indicates an invalid OpenGL context
181     }
182 
183     // Texture width required for numGlyphs glyphs. Will probably not be
184     // large enough, but we try to fit as many glyphs in one line as possible
185     textureWidth = ClampSize(glyphWidth * numGlyphs + padding * 2,
186                              maximumGLTextureSize);
187 
188     // Number of lines required for that many glyphs in a line
189     int tmp = (textureWidth - (padding * 2)) / glyphWidth;
190     tmp = tmp > 0 ? tmp : 1;
191     tmp = (numGlyphs + (tmp - 1)) / tmp; // round division up
192 
193     // Texture height required for tmp lines of glyphs
194     textureHeight = ClampSize(glyphHeight * tmp + padding * 2,
195                               maximumGLTextureSize);
196 }
197 
198 
CreateTexture()199 GLuint FTTextureFontImpl::CreateTexture()
200 {
201     CalculateTextureSize();
202 
203     int totalMemory = textureWidth * textureHeight;
204     unsigned char* textureMemory = new unsigned char[totalMemory];
205     memset(textureMemory, 0, totalMemory);
206 
207     GLuint textID;
208     glGenTextures(1, (GLuint*)&textID);
209 
210     glBindTexture(GL_TEXTURE_2D, textID);
211     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
212     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
213     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
214     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
215 
216     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, textureWidth, textureHeight,
217                  0, GL_ALPHA, GL_UNSIGNED_BYTE, textureMemory);
218 
219     delete [] textureMemory;
220 
221     return textID;
222 }
223 
224 
FaceSize(const unsigned int size,const unsigned int res)225 bool FTTextureFontImpl::FaceSize(const unsigned int size, const unsigned int res)
226 {
227     if(!textureIDList.empty())
228     {
229         glDeleteTextures((GLsizei)textureIDList.size(), (const GLuint*)&textureIDList[0]);
230         textureIDList.clear();
231         remGlyphs = numGlyphs = face.GlyphCount();
232     }
233 
234     return FTFontImpl::FaceSize(size, res);
235 }
236 
237 
238 template <typename T>
RenderI(const T * string,const int len,FTPoint position,FTPoint spacing,int renderMode)239 inline FTPoint FTTextureFontImpl::RenderI(const T* string, const int len,
240                                           FTPoint position, FTPoint spacing,
241                                           int renderMode)
242 {
243     // Protect GL_TEXTURE_2D and optionally GL_BLEND
244     glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TEXTURE_ENV_MODE);
245 
246     if(FTLibrary::Instance().GetLegacyOpenGLStateSet())
247       {
248         glEnable(GL_BLEND);
249         /*
250          * Note: This is the historic legacy behaviour.
251          *
252          * A better blending function (see
253          * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=742469) is:
254          *
255          *   glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
256          *                       GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
257          *
258          * To use it, set
259          *
260          *   FTLibrary::Instance().LegacyOpenGLState(false);
261          *
262          * and set GL_BLEND and the blending function yourself.
263          */
264         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
265       }
266 
267     glEnable(GL_TEXTURE_2D);
268     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
269 
270     FTTextureGlyphImpl::ResetActiveTexture();
271 
272     FTPoint tmp = FTFontImpl::Render(string, len,
273                                      position, spacing, renderMode);
274 
275     glPopAttrib();
276 
277     return tmp;
278 }
279 
280 
Render(const char * string,const int len,FTPoint position,FTPoint spacing,int renderMode)281 FTPoint FTTextureFontImpl::Render(const char * string, const int len,
282                                   FTPoint position, FTPoint spacing,
283                                   int renderMode)
284 {
285     return RenderI(string, len, position, spacing, renderMode);
286 }
287 
288 
Render(const wchar_t * string,const int len,FTPoint position,FTPoint spacing,int renderMode)289 FTPoint FTTextureFontImpl::Render(const wchar_t * string, const int len,
290                                   FTPoint position, FTPoint spacing,
291                                   int renderMode)
292 {
293     return RenderI(string, len, position, spacing, renderMode);
294 }
295 
296