rayfract

view src/utktext.cc @ 3:bf1d56975cc9

- added visual studio project - removed vmath dependency
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 26 Oct 2010 09:52:57 +0300
parents 87b6a11c920b
children
line source
1 #include <math.h>
2 #include <ctype.h>
3 #define GL_GLEXT_PROTOTYPES
4 #if defined(__APPLE__) && defined(__MACH__)
5 #include <GLUT/glut.h>
6 #else
7 #include <GL/glut.h>
8 #endif
9 #include <ft2build.h>
10 #include FT_FREETYPE_H
11 #include "utktext.h"
12 #include "utk_common.h"
13 #include "vmath.h"
15 #ifndef GL_BGRA
16 #define GL_BGRA 0x80E1
17 #endif
19 #ifndef GL_ABGR
20 #define GL_ABGR 0x8000
21 #endif
23 #ifdef UTK_BIG_ENDIAN
24 #define PIXFMT GL_ABGR
25 #else
26 #define PIXFMT GL_BGRA
27 #endif
29 #define MAX_CHARS 128
30 #define MAX_IMG_WIDTH 1024
31 #define SIZE_PIXELS(x) ((x) / 64)
33 struct Font {
34 unsigned int tex_id;
35 float scale; // this compensates if a higher res font is loaded in its stead, with a new CreateFont
36 float line_adv; // vertical advance to go to the next line
37 struct {
38 Vector2 pos, size; // glyph position (from origin) and size in normalized coords [0, 1]
39 float advance; // advance in normalized coords
40 Vector2 tc_pos, tc_sz; // tex coord box pos/size
41 } glyphs[MAX_CHARS];
42 };
44 static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz);
45 static void CleanUp();
47 static FT_Library ft;
48 static Vector2 text_pos;
49 static float text_size = 1.0;
50 static Color text_color;
51 static Font *act_fnt;
53 #if !defined(GLIBC) && !defined(__GLIBC__)
54 float log2(float x) {
55 float res = 0.0;
56 while(x > 1.0) {
57 x /= 2.0;
58 res += 1.0;
59 }
60 return res;
61 }
62 #endif // _MSC_VER
65 static inline int NextPow2(int x)
66 {
67 float lg2 = log2((float)x);
68 return (int)pow(2.0f, (int)ceil(lg2));
69 }
72 unsigned int CreateFont(const char *fname, int font_size)
73 {
74 if(!ft) {
75 if(FT_Init_FreeType(&ft) != 0) {
76 fprintf(stderr, "failed to initialize freetype\n");
77 return 0;
78 }
79 atexit(CleanUp);
80 }
82 FT_Face face;
83 if(FT_New_Face(ft, fname, 0, &face) != 0) {
84 fprintf(stderr, "failed to load font: %s\n", fname);
85 return 0;
86 }
88 FT_Set_Pixel_Sizes(face, 0, font_size);
90 Font *fnt = new Font;
91 int max_width = MAX_CHARS * SIZE_PIXELS(face->bbox.xMax - face->bbox.xMin);
92 int foo_xsz = MAX_IMG_WIDTH;
93 int foo_ysz = SIZE_PIXELS(face->bbox.yMax - face->bbox.yMin) * max_width / foo_xsz;
95 int tex_xsz = NextPow2(foo_xsz);
96 int tex_ysz = NextPow2(foo_ysz);
98 unsigned int *img;
99 img = new unsigned int[tex_xsz * tex_ysz];
100 memset(img, 0, tex_xsz * tex_ysz * sizeof *img);
102 extern int xsz, ysz;
103 int vport_xsz = xsz, vport_ysz = ysz;
105 int max_glyph_y = 0;
106 int max_glyph_x = 0;
107 for(int i=0; i<MAX_CHARS; i++) {
108 FT_Load_Char(face, i, 0);
109 int width = SIZE_PIXELS(face->glyph->metrics.width);
110 int height = SIZE_PIXELS(face->glyph->metrics.height);
112 if(height > max_glyph_y) {
113 max_glyph_y = height;
114 }
116 if(width > max_glyph_x) {
117 max_glyph_x = width;
118 }
119 }
121 int gx = 0, gy = 0;
122 for(int i=0; i<MAX_CHARS; i++) {
123 FT_Load_Char(face, i, FT_LOAD_RENDER);
124 FT_GlyphSlot g = face->glyph;
126 int gwidth = SIZE_PIXELS(g->metrics.width);
127 int gheight = SIZE_PIXELS(g->metrics.height);
129 if(gx > MAX_IMG_WIDTH - gwidth) {
130 gx = 0;
131 gy += max_glyph_y;
132 }
134 BlitFontGlyph(fnt, gx, gy, g, img, tex_xsz, tex_ysz);
135 fnt->scale = 1.0;
136 fnt->line_adv = (float)SIZE_PIXELS(g->metrics.vertAdvance) / (float)vport_ysz;
137 fnt->glyphs[i].tc_pos.x = (float)gx / (float)tex_xsz;
138 fnt->glyphs[i].tc_pos.y = (float)gy / (float)tex_ysz;
139 fnt->glyphs[i].tc_sz.x = (float)gwidth / (float)tex_xsz;
140 fnt->glyphs[i].tc_sz.y = (float)gheight / (float)tex_ysz;
141 fnt->glyphs[i].size.x = (float)gwidth / (float)vport_xsz;
142 fnt->glyphs[i].size.y = (float)gheight / (float)vport_ysz;
143 fnt->glyphs[i].pos.x = (float)SIZE_PIXELS(g->metrics.horiBearingX) / (float)vport_xsz;
144 fnt->glyphs[i].pos.y = -(float)SIZE_PIXELS(g->metrics.horiBearingY) / (float)vport_ysz;
145 fnt->glyphs[i].advance = (float)SIZE_PIXELS(g->metrics.horiAdvance) / (float)vport_xsz;
147 gx += gwidth;
148 }
150 FT_Done_Face(face);
152 glGenTextures(1, &fnt->tex_id);
153 glBindTexture(GL_TEXTURE_2D, fnt->tex_id);
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
158 glTexImage2D(GL_TEXTURE_2D, 0, 4, tex_xsz, tex_ysz, 0, PIXFMT, GL_UNSIGNED_BYTE, img);
160 #ifdef DUMP_FONT_IMG
161 FILE *fp;
162 unsigned int *ptr = img;
164 if((fp = fopen("fnt.ppm", "wb"))) {
165 fprintf(fp, "P6\n%d %d\n255\n", tex_xsz, tex_ysz);
167 for(int i=0; i<tex_xsz * tex_ysz; i++) {
168 fputc((*ptr >> 24) & 0xff, fp);
169 fputc((*ptr >> 24) & 0xff, fp);
170 fputc((*ptr >> 24) & 0xff, fp);
171 ptr++;
172 }
173 fclose(fp);
174 }
175 #endif
177 delete [] img;
179 act_fnt = fnt;
181 return 1;
182 }
184 void DeleteFont(unsigned int fid)
185 {
186 }
188 unsigned int GetFont(const char *fname, int sz)
189 {
190 return 1;
191 }
193 bool BindFont(unsigned int fid)
194 {
195 return true;
196 }
198 void SetTextPos(const Vector2 &pos)
199 {
200 text_pos = pos;
201 }
203 Vector2 GetTextPos()
204 {
205 return text_pos;
206 }
208 void TextLineAdvance(int adv)
209 {
210 text_pos.y += (float)adv * act_fnt->line_adv;
211 }
213 void TextCRet()
214 {
215 text_pos.x = 0.0;
216 }
218 void SetTextSize(float sz)
219 {
220 text_size = sz;
221 }
223 float GetTextSize()
224 {
225 return text_size;
226 }
228 void SetTextColor(const Color &col)
229 {
230 text_color = col;
231 }
233 Color GetTextColor()
234 {
235 return text_color;
236 }
238 static void ImOverlay(const Vector2 &v1, const Vector2 &v2, const Color &col, unsigned int tex)
239 {
240 float l = v1.x * 2.0f - 1.0f;
241 float r = v2.x * 2.0f - 1.0f;
242 float u = -v1.y * 2.0f + 1.0f;
243 float d = -v2.y * 2.0f + 1.0f;
245 glMatrixMode(GL_PROJECTION);
246 glPushMatrix();
247 glLoadIdentity();
248 glMatrixMode(GL_MODELVIEW);
249 glPushMatrix();
250 glLoadIdentity();
252 glBindTexture(GL_TEXTURE_2D, tex);
254 glDisable(GL_DEPTH_TEST);
255 glDisable(GL_LIGHTING);
256 glDisable(GL_CULL_FACE);
258 glBegin(GL_QUADS);
259 glColor4f(col.r, col.g, col.b, col.a);
260 glTexCoord2f(0, 0);
261 glVertex2f(l, u);
262 glTexCoord2f(1, 0);
263 glVertex2f(r, u);
264 glTexCoord2f(1, 1);
265 glVertex2f(r, d);
266 glTexCoord2f(0, 1);
267 glVertex2f(l, d);
268 glEnd();
270 glEnable(GL_LIGHTING);
271 glEnable(GL_DEPTH_TEST);
273 glMatrixMode(GL_PROJECTION);
274 glPopMatrix();
275 glMatrixMode(GL_MODELVIEW);
276 glPopMatrix();
277 }
279 float PrintChar(char c)
280 {
281 // get texture coordinates for the glyph, and construct the texture matrix
282 float tx = act_fnt->glyphs[(int)c].tc_pos.x;
283 float ty = act_fnt->glyphs[(int)c].tc_pos.y;
284 float sx = act_fnt->glyphs[(int)c].tc_sz.x;
285 float sy = act_fnt->glyphs[(int)c].tc_sz.y;
287 float mat[] = {
288 sx, 0, tx, 0,
289 0, sy, ty, 0,
290 0, 0, 1, 0,
291 0, 0, 0, 1
292 };
294 glMatrixMode(GL_TEXTURE);
295 glPushMatrix();
296 glLoadMatrixf(mat);
298 Vector2 pos = text_pos + act_fnt->glyphs[(int)c].pos * act_fnt->scale;
299 ImOverlay(pos, pos + act_fnt->glyphs[(int)c].size * act_fnt->scale, text_color, act_fnt->tex_id);
301 glMatrixMode(GL_TEXTURE);
302 glPopMatrix();
304 return act_fnt->glyphs[(int)c].advance * act_fnt->scale;
305 }
307 // this function contains the preamble of all block text drawing functions (i.e. not printchar above)
308 static void PreDraw()
309 {
310 glMatrixMode(GL_PROJECTION);
311 glPushMatrix();
312 /*glLoadTransposeMatrixf(OrthoProj(2, 2, 0, 10).m);*/
313 glLoadIdentity();
314 glMatrixMode(GL_MODELVIEW);
315 glPushMatrix();
316 glLoadIdentity();
317 glMatrixMode(GL_TEXTURE);
318 glPushMatrix();
319 glLoadIdentity();
321 glEnable(GL_TEXTURE_2D);
322 glBindTexture(GL_TEXTURE_2D, act_fnt->tex_id);
324 glBegin(GL_QUADS);
325 glColor4f(text_color.r, text_color.g, text_color.b, text_color.a);
326 }
328 static void PostDraw()
329 {
330 glEnd();
332 glDisable(GL_TEXTURE_2D);
334 glMatrixMode(GL_TEXTURE);
335 glPopMatrix();
336 glMatrixMode(GL_MODELVIEW);
337 glPopMatrix();
338 glMatrixMode(GL_PROJECTION);
339 glPopMatrix();
340 }
342 float PrintString(const char *str, bool standalone)
343 {
344 if(standalone) PreDraw();
346 float start_x = text_pos.x;
347 while(*str) {
348 float tx = act_fnt->glyphs[(int)*str].tc_pos.x;
349 float ty = act_fnt->glyphs[(int)*str].tc_pos.y;
350 float sx = act_fnt->glyphs[(int)*str].tc_sz.x;
351 float sy = act_fnt->glyphs[(int)*str].tc_sz.y;
353 Vector2 tc1 = Vector2(tx, ty);
354 Vector2 tc2 = Vector2(tx + sx, ty + sy);
356 Vector2 v1 = text_pos + act_fnt->glyphs[(int)*str].pos * act_fnt->scale * text_size;
357 Vector2 v2 = v1 + act_fnt->glyphs[(int)*str].size * act_fnt->scale * text_size;
358 float l = v1.x * 2.0f - 1.0f;
359 float r = v2.x * 2.0f - 1.0f;
360 float u = -v1.y * 2.0f + 1.0f;
361 float d = -v2.y * 2.0f + 1.0f;
363 glTexCoord2f(tc1.x, tc1.y);
364 glVertex2f(l, u);
365 glTexCoord2f(tc2.x, tc1.y);
366 glVertex2f(r, u);
367 glTexCoord2f(tc2.x, tc2.y);
368 glVertex2f(r, d);
369 glTexCoord2f(tc1.x, tc2.y);
370 glVertex2f(l, d);
372 text_pos.x += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size;
373 }
375 if(standalone) PostDraw();
376 return text_pos.x - start_x;
377 }
379 void PrintStringLines(const char **str, int lines)
380 {
381 PreDraw();
383 while(lines-- > 0) {
384 PrintString(*str++, false);
385 TextLineAdvance();
386 TextCRet();
387 }
389 PostDraw();
390 }
392 static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz)
393 {
394 if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) {
395 fprintf(stderr, "BlitFontGlyph: unsupported pixel mode: %d\n", glyph->bitmap.pixel_mode);
396 return;
397 }
399 unsigned int *dest = img + y * xsz + x;
400 unsigned char *src = glyph->bitmap.buffer;
402 for(int j=0; j<glyph->bitmap.rows; j++) {
403 for(int i=0; i<glyph->bitmap.width; i++) {
404 dest[i] = 0x00ffffff | ((unsigned int)src[i] << 24);
405 }
406 dest += xsz;
407 src += glyph->bitmap.pitch;
408 }
409 }
411 static void CleanUp()
412 {
413 FT_Done_FreeType(ft);
414 }
416 float GetMaxDescent()
417 {
418 Font *fnt = act_fnt;
420 float max_descent = 0.0f;
422 for(int i=0; i<MAX_CHARS; i++) {
423 float des = fnt->glyphs[i].pos.y + fnt->glyphs[i].size.y;
424 if(isprint(i) && des > max_descent) {
425 max_descent = des;
426 }
427 }
429 return max_descent;
430 }
432 float GetLineAdvance()
433 {
434 return act_fnt->line_adv;
435 }
437 float GetTextWidth(const char *str)
438 {
439 float width = 0;
440 while(*str) {
441 width += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size;
442 }
443 return width;
444 }