CSCI441 OpenGL Library 6.1.0.0
CS@Mines CSCI441 Computer Graphics Course Library
Loading...
Searching...
No Matches
Font.hpp
1#ifndef CSCI441_FONT_HPP
2#define CSCI441_FONT_HPP
3
4#ifdef CSCI441_USE_GLEW
5 #include <GL/glew.h>
6#else
7 #include <glad/gl.h>
8#endif
9
10#include "LogUtils.hpp"
11
12#include <ft2build.h>
13#include FT_FREETYPE_H
14
15#include <cstdlib>
16
17namespace CSCI441 {
21 class Font {
22 public:
27 Font() = delete;
32 explicit Font(const char* filename);
36 ~Font();
37
42 [[nodiscard]] GLboolean isLoaded() const { return _loaded; }
43
50 void setScale(GLfloat scaleX, GLfloat scaleY);
51
56 void bind() const;
57
66 void draw(const char* str, GLfloat x, GLfloat y) const;
67 private:
71 GLboolean _loaded;
72
76 GLuint _vao;
80 GLuint _vbo;
84 GLuint _texHandle;
85
89 GLfloat _scaleX;
93 GLfloat _scaleY;
94
98 FT_Library _ftLibrary;
102 FT_Face _fontFace;
106 GLuint _atlasWidth;
110 GLuint _atlasHeight;
111
116 struct CharacterInfo {
120 GLfloat ax = 0.f;
124 GLfloat ay = 0.f;
125
129 GLfloat bw = 0.f;
133 GLfloat bh = 0.f;
134
138 GLfloat bl = 0.f;
142 GLfloat bt = 0.f;
143
147 GLfloat tx = 0.f;
148 } _fontCharacters[128];
149 };
150}
151
152inline CSCI441::Font::Font(const char *filename) :
153 _loaded(GL_FALSE),
154 _vao(0),
155 _vbo(0),
156 _texHandle(0),
157 _scaleX(1.0f),
158 _scaleY(1.0f),
159 _ftLibrary(nullptr),
160 _fontFace(nullptr),
161 _atlasWidth(0),
162 _atlasHeight(0)
163{
164
165 if(FT_Init_FreeType(&_ftLibrary)) {
166 CSCI441::LogUtils::logError("[font | ERROR]: Could not init freetype library\n");
167 return;
168 }
169
170 if(FT_New_Face(_ftLibrary, filename, 0, &_fontFace)) {
171 CSCI441::LogUtils::logError("[font | ERROR]: Could not open font\n");
172 return;
173 }
174
175 FT_Set_Pixel_Sizes(_fontFace, 0, 20);
176
177 const auto g = _fontFace->glyph;
178 GLuint w = 0;
179 GLuint h = 0;
180
181 for(int i = 32; i < 128; i++) {
182 if(FT_Load_Char(_fontFace, i, FT_LOAD_RENDER)) {
183 CSCI441::LogUtils::logError("[font | ERROR]: Loading character %c failed!\n", i);
184 continue;
185 }
186
187 w += g->bitmap.width;
188 h = (h > g->bitmap.rows ? h : g->bitmap.rows);
189 }
190
191 // save this value as it is needed later on
192 _atlasWidth = w;
193 _atlasHeight = h;
194
195 glActiveTexture(GL_TEXTURE0);
196 glGenTextures(1, &_texHandle);
197 glBindTexture(GL_TEXTURE_2D, _texHandle);
198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
202 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
203 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
204
205 GLuint x = 0;
206
207 for(int i = 32; i < 128; i++) {
208 if(FT_Load_Char(_fontFace, i, FT_LOAD_RENDER))
209 continue;
210
211 _fontCharacters[i].ax = static_cast<GLfloat>( g->advance.x >> 6 );
212 _fontCharacters[i].ay = static_cast<GLfloat>( g->advance.y >> 6 );
213
214 _fontCharacters[i].bw = static_cast<GLfloat>( g->bitmap.width );
215 _fontCharacters[i].bh = static_cast<GLfloat>( g->bitmap.rows );
216
217 _fontCharacters[i].bl = static_cast<GLfloat>( g->bitmap_left );
218 _fontCharacters[i].bt = static_cast<GLfloat>( g->bitmap_top );
219
220 _fontCharacters[i].tx = static_cast<GLfloat>(x) / static_cast<GLfloat>(w);
221
222 glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(x), 0, static_cast<GLint>(g->bitmap.width), static_cast<GLint>(g->bitmap.rows), GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer);
223
224 x += g->bitmap.width;
225 }
226
227 glGenVertexArrays(1, &_vao);
228 glBindVertexArray(_vao);
229 glGenBuffers(1, &_vbo);
230 glBindBuffer(GL_ARRAY_BUFFER, _vbo);
231 glEnableVertexAttribArray(0);
232 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
233
234 _loaded = GL_TRUE;
235}
236
238 glDeleteVertexArrays(1, &_vao);
239 glDeleteBuffers(1, &_vbo);
240 glDeleteTextures(1, &_texHandle);
241 FT_Done_Face(_fontFace);
242 FT_Done_FreeType(_ftLibrary);
243}
244
245inline void CSCI441::Font::setScale(const GLfloat scaleX, const GLfloat scaleY) {
246 if (_scaleX > 0 && _scaleY > 0) {
247 _scaleX = scaleX;
248 _scaleY = scaleY;
249 }
250}
251
252inline void CSCI441::Font::bind() const {
253 glBindVertexArray(_vao);
254 glBindBuffer(GL_ARRAY_BUFFER, _vbo);
255 glActiveTexture(GL_TEXTURE0);
256 glBindTexture(GL_TEXTURE_2D, _texHandle);
257}
258
259inline void CSCI441::Font::draw(const char* str, GLfloat x, GLfloat y) const {
260 struct FontPoint {
261 GLfloat x;
262 GLfloat y;
263 GLfloat s;
264 GLfloat t;
265 };
266 const auto coords = (FontPoint*)malloc(sizeof(FontPoint) * 6 * strlen(str));
267
268 GLint n = 0;
269
270 for(const char *p = str; *p; p++) {
271 const auto characterIndex = static_cast<int>(*p);
272 const auto character = _fontCharacters[characterIndex];
273 const auto x2 = x + character.bl * _scaleX;
274 const auto y2 = -y - character.bt * _scaleY;
275 const auto w = character.bw * _scaleX;
276 const auto h = character.bh * _scaleY;
277
278 // Advance the cursor to the start of the next character
279 x += character.ax * _scaleX;
280 y += character.ay * _scaleY;
281
282 // Skip glyphs that have no pixels
283 if(w == 0 || h == 0)
284 continue;
285
286 coords[n++] = (FontPoint){x2, -y2 , character.tx, 0};
287 coords[n++] = (FontPoint){x2 + w, -y2 , character.tx + character.bw / static_cast<GLfloat>(_atlasWidth), 0};
288 coords[n++] = (FontPoint){x2, -y2 - h, character.tx, character.bh / static_cast<GLfloat>(_atlasHeight)}; //remember: each glyph occupies a different amount of vertical space
289
290 coords[n++] = (FontPoint){x2 + w, -y2 , character.tx + character.bw / static_cast<GLfloat>(_atlasWidth), 0};
291 coords[n++] = (FontPoint){x2, -y2 - h, character.tx, character.bh / static_cast<GLfloat>(_atlasHeight)};
292 coords[n++] = (FontPoint){x2 + w, -y2 - h, character.tx + character.bw / static_cast<GLfloat>(_atlasWidth), character.bh / static_cast<GLfloat>(_atlasHeight)};
293 }
294 glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(FontPoint) * 6 * strlen(str)), coords, GL_DYNAMIC_DRAW);
295 glDrawArrays(GL_TRIANGLES, 0, n);
296
297 free(coords);
298}
299
300#endif//CSCI441_FONT_HPP
Stores character glyphs corresponding to a ttf file and draws text to the screen.
Definition: Font.hpp:21
GLboolean isLoaded() const
tracks if font file was loaded successfully
Definition: Font.hpp:42
~Font()
cleanup CPU and GPU memory
Definition: Font.hpp:237
Font()=delete
do not allow default Font objects to be constructed
void draw(const char *str, GLfloat x, GLfloat y) const
draws a text string at a given (x,y) window coordinate with the currently set scale
Definition: Font.hpp:259
void bind() const
make this font active, binding its VAO, VBO, and 2D texture to GL_TEXTURE0
Definition: Font.hpp:252
void setScale(GLfloat scaleX, GLfloat scaleY)
set the amount to scale each glyph when drawing
Definition: Font.hpp:245
void logError(const char *MSG,...)
log a message to both the standard error stream and file
Definition: LogUtils.hpp:128
CSCI441 Helper Functions for OpenGL.
Definition: ArcballCam.hpp:17