added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / src / fxwt / text.cpp
1 /*
2 This file is part of fxwt, the window system toolkit of 3dengfx.
3
4 Copyright (c) 2004, 2005 John Tsiombikas <nuclear@siggraph.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 /* True type text rendering and management.
22  *
23  * Author: John Tsiombikas 2005
24  */
25
26 #include "3dengfx_config.h"
27
28 #ifndef FXWT_NO_FREETYPE
29 #include <vector>
30 #include <cstdio>
31 #include <cassert>
32 #include <ft2build.h>
33 #include FT_FREETYPE_H
34 #include "text.hpp"
35 #include "3dengfx/textures.hpp"
36 #include "3dengfx/texman.hpp"
37 #include "common/hashtable.hpp"
38 #include "dsys/demosys.hpp"
39 #include "common/err_msg.h"
40 #include "common/string_hash.hpp"
41 #include "gfx/img_manip.hpp"
42
43 using namespace std;
44 using namespace fxwt;
45
46 struct Text {
47         Texture *texture;
48         scalar_t aspect;
49 };
50
51 static const char *find_font_file(const char *font);
52 static string gen_key_str(const char *text);
53 static void draw_free_type_bitmap(FT_Bitmap *ftbm, PixelBuffer *pbuf, int x, int y);
54 static PixelBuffer *create_text_image(const char *str, FT_Face face, int font_size);
55 static int next_pow_two(int num);
56 static Texture *pixel_buf_to_texture(const PixelBuffer &pbuf);
57
58 static FT_LibraryRec_ *ft;
59 static vector<FT_FaceRec_*> face_list;
60 static HashTable<string, Text> text_table;
61 static FT_FaceRec_ *font;
62 static int font_size = 64;
63 static scalar_t latest_fetched_aspect = 1;
64 static TextRenderMode render_mode = TEXT_TRANSPARENT;
65
66 /* This list MUST correspond to the enum Font at text.hpp
67  * so take care to keep them in sync.
68  */
69 static const char *font_names[] = {
70         "FreeSans",
71         "FreeSerif",
72         "FreeMono",
73         "Bitstream Vera Sans",
74         "Bitstream Vera Serif",
75         "Bitstream Vera Sans Mono",
76         "Verdana",
77         "Times New Roman",
78         "Courier New"
79 };
80
81 // update these lists when adding a new font
82 static Font font_style_list[3][4] = {
83         // sans-serif fonts
84         {FONT_FREE_SANS, FONT_MS_SANS, FONT_VERA_SANS, FONT_NULL},
85         // serif fonts
86         {FONT_FREE_SERIF, FONT_MS_SERIF, FONT_VERA_SERIF, FONT_NULL},
87         // monospaced fonts
88         {FONT_FREE_MONO, FONT_MS_MONO, FONT_VERA_MONO, FONT_NULL}
89 };
90
91
92 #if defined(__unix__)
93 #define VERDANA_FILE                    "Verdana.ttf"
94 #define TIMES_NEW_ROMAN_FILE    "Times_New_Roman.ttf"
95 #define COURIER_NEW_FILE                "Courier_New.ttf"
96 #elif defined(WIN32) || defined(__WIN32__)
97 #define VERDANA_FILE                    "verdana.ttf"
98 #define TIMES_NEW_ROMAN_FILE    "times.ttf"
99 #define COURIER_NEW_FILE                "cour.ttf"
100 #endif
101
102 bool fxwt::text_init() {
103         
104         set_verbosity(2);
105
106         text_table.set_hash_function(string_hash);
107
108         if(FT_Init_FreeType(&ft) != 0) return false;
109         
110         static const char *fonts[] = {
111                 "FreeSans.ttf", "FreeSerif.ttf", "FreeMono.ttf",                        // freefonts
112                 "Vera.ttf", "VeraSe.ttf", "VeraMono.ttf",                                       // bitstream vera fonts
113                 VERDANA_FILE, TIMES_NEW_ROMAN_FILE, COURIER_NEW_FILE,           // MS fonts
114                 0
115         };
116
117         const char **fptr = fonts;
118         while(*fptr) {
119                 const char *font_path = find_font_file(*fptr++);
120                 if(font_path) {
121                         FT_Face face;
122                         if(FT_New_Face(ft, font_path, 0, &face) == 0) {
123                                 info("Loaded font \"%s\" (%s)", font_path, face->family_name);
124                                 if(!font) font = face;
125                         }
126                         face_list.push_back(face);
127                 }
128         }
129
130         atexit(fxwt::text_close);
131
132         set_verbosity(3);
133
134         return true;
135 }
136
137 void fxwt::text_close() {
138         // TODO: free the textures
139         
140         for(size_t i=0; i<face_list.size(); i++) {
141                 FT_Done_Face(face_list[i]);
142         }
143         FT_Done_FreeType(ft);
144 }
145
146 void fxwt::set_text_render_mode(TextRenderMode mode) {
147         render_mode = mode;
148 }
149
150 void fxwt::set_font_size(int sz) {
151         font_size = sz;
152 }
153
154 int fxwt::get_font_size() {
155         return font_size;
156 }
157
158 bool fxwt::set_font(Font fnt) {
159         for(size_t i=0; i<face_list.size(); i++) {
160                 if(!strcmp(face_list[i]->family_name, font_names[fnt])) {
161                         font = face_list[i];    
162                         return true;
163                 }
164         }       
165         return false;
166 }
167
168 bool fxwt::set_font(FontStyle fstyle) {
169         int i = 0;
170         while(font_style_list[fstyle][i] != FONT_NULL) {
171                 if(set_font(font_style_list[fstyle][i++])) return true;
172         }
173         return false;
174 }
175
176 const char *fxwt::get_font_name(Font fnt) {
177         return font_names[fnt];
178 }
179
180 Texture *fxwt::get_text(const char *text_str) {
181         Pair<string, Text> *res;
182         if((res = text_table.find(gen_key_str(text_str)))) {
183                 latest_fetched_aspect = res->val.aspect;
184                 return res->val.texture;
185         }
186
187         PixelBuffer *text_img = create_text_image(text_str, font, font_size);
188         scalar_t aspect = (scalar_t)text_img->width / (scalar_t)text_img->height;
189         Texture *tex = pixel_buf_to_texture(*text_img);
190         delete text_img;
191
192         Text text = {tex, aspect};
193         text_table.insert(gen_key_str(text_str), text);
194         
195         latest_fetched_aspect = aspect;
196         return tex;
197 }
198
199
200 void fxwt::print_text(const char *text_str, const Vector2 &pos, scalar_t size, const Color &col) {
201         Texture *tex = get_text(text_str);
202         Vector2 sz_vec(size * latest_fetched_aspect, size);
203         
204         dsys::overlay(tex, pos, pos + sz_vec, col);
205 }
206
207 static const char *find_font_file(const char *font) {
208         static char path[512];
209         FILE *fp;
210
211         // TODO: add data path search through the locator
212         if((fp = fopen(font, "r"))) {
213                 fclose(fp);
214                 return font;
215         }
216
217 #ifdef __unix__
218         // try /usr/share/fonts/truetype
219         sprintf(path, "/usr/share/fonts/truetype/%s", font);
220         if((fp = fopen(path, "r"))) {
221                 fclose(fp);
222                 return path;
223         }
224
225         // try /usr/share/fonts/truetype/freefont
226         sprintf(path, "/usr/share/fonts/truetype/freefont/%s", font);
227         if((fp = fopen(path, "r"))) {
228                 fclose(fp);
229                 return path;
230         }
231
232         // try /usr/share/fonts/truetype/ttf-bitstream-vera
233         sprintf(path, "/usr/share/fonts/truetype/ttf-bitstream-vera/%s", font);
234         if((fp = fopen(path, "r"))) {
235                 fclose(fp);
236                 return path;
237         }
238 #endif  /* __unix__ */
239
240 #ifdef WIN32
241         // try %windir%\fonts
242         sprintf(path, "%s\\fonts\\%s", getenv("WINDIR"), font);
243         if((fp = fopen(path, "r"))) {
244                 fclose(fp);
245                 return path;
246         }
247 #endif  /* WIN32 */
248
249         return 0;
250 }
251
252 static string gen_key_str(const char *text) {
253         if(font->family_name) {
254                 return string(font->family_name) + string("##") + string(text);
255         }
256         return string(text);
257 }
258
259
260 static void draw_free_type_bitmap(FT_Bitmap *ftbm, PixelBuffer *pbuf, int x, int y) {
261         int i, j;
262         Pixel *dptr = pbuf->buffer + y * pbuf->width + x;
263         unsigned char *sptr = (unsigned char*)ftbm->buffer;
264
265         assert(x >= 0);
266         assert(y >= 0);
267
268         for(i=0; i<ftbm->rows; i++) {
269                 if(i + y >= (int)pbuf->height) break;
270                 
271                 for(j=0; j<ftbm->width; j++) {
272                         if(j + x >= (int)pbuf->width) break;
273
274                         Pixel pixel = *sptr++;
275                         
276                         if(render_mode == TEXT_TRANSPARENT) {
277                                 *dptr++ = 0x00ffffff | (pixel << 24);
278                         } else {
279                                 *dptr++ = 0xff000000 | (pixel << 8) | (pixel << 16) | pixel;
280                         }
281                 }
282
283                 sptr += ftbm->pitch - j;
284                 dptr += pbuf->width - j;
285         }
286 }
287
288 static PixelBuffer *create_text_image(const char *str, FT_Face face, int font_size) {
289         FT_GlyphSlot slot = face->glyph;
290         FT_Set_Pixel_Sizes(face, 0, font_size); // set size
291
292         size_t len = strlen(str);
293         PixelBuffer tmp_buf(len * font_size * 2, font_size * 2);
294         memset(tmp_buf.buffer, 0, tmp_buf.width * tmp_buf.height * sizeof(Pixel));
295
296         int pen_x = 0;
297         int pen_y = font_size;
298
299         for(size_t i=0; i<len; i++) {
300                 if(FT_Load_Char(face, *str++, FT_LOAD_RENDER) != 0) {
301                         cerr << "aaaaaaaa!\n";
302                         continue;
303                 }
304
305                 draw_free_type_bitmap(&slot->bitmap, &tmp_buf, pen_x + slot->bitmap_left, pen_y - slot->bitmap_top);
306
307                 pen_x += slot->advance.x >> 6;
308         }
309
310         assert(pen_x <= (int)tmp_buf.width);
311
312         PixelBuffer *pbuf = new PixelBuffer(pen_x, (int)(font_size * 1.5));
313         
314         Pixel *dptr = pbuf->buffer;
315         Pixel *sptr = tmp_buf.buffer;
316
317         for(size_t i=0; i<pbuf->height; i++) {
318                 memcpy(dptr, sptr, pbuf->width * sizeof(Pixel));
319                 dptr += pbuf->width;
320                 sptr += tmp_buf.width;
321         }
322
323         return pbuf;
324 }
325
326 static int next_pow_two(int num) {
327         int val = 1;
328         while(val < num) val <<= 1;
329         return val;
330 }
331
332 static Texture *pixel_buf_to_texture(const PixelBuffer &pbuf) {
333
334         int w, h;
335         Texture *tex;
336         
337         if (engfx_state::sys_caps.non_power_of_two_textures)
338         {
339                 w = pbuf.width;
340                 h = pbuf.height;
341                 tex = new Texture(w, h);
342                 tex->set_pixel_data(pbuf);
343         }
344         else
345         {
346                 w = next_pow_two(pbuf.width);
347                 h = next_pow_two(pbuf.height);
348                 PixelBuffer tmp = pbuf;
349                 resample_pixel_buffer(&tmp, w, h);
350                 tex = new Texture(w, h);
351                 tex->set_pixel_data(tmp);
352         }
353
354         /*tex->lock();
355
356         float dx = (float)pbuf.width / (float)w;
357         float dy = (float)pbuf.height / (float)h;
358
359         for(int j=0; j<h; j++) {
360                 for(int i=0; i<w; i++) {
361                         int x = (int)(((float)i * dx) + 0.5);
362                         int y = (int)(((float)j * dy) + 0.5);
363                         tex->buffer[j * w + i] = pbuf.buffer[y * pbuf.width + x];
364                 }
365         }
366
367         tex->unlock();*/
368         return tex;
369 }
370
371 #else           // if we excluded freetype dependencies from compilation
372
373 #include "text.hpp"
374 #include "common/err_msg.h"
375
376 using namespace fxwt;
377
378 #define FT_NOT_COMPILED         "some text-rendering function is called, but freetype support is not compiled in"
379
380 bool fxwt::text_init() {
381         return false;
382 }
383
384 void fxwt::text_close() {}
385
386 void fxwt::set_text_render_mode(TextRenderMode mode) {
387         error(FT_NOT_COMPILED);
388 }
389
390 void fxwt::set_font_size(int sz) {
391         error(FT_NOT_COMPILED);
392 }
393
394 int fxwt::get_font_size() {
395         error(FT_NOT_COMPILED);
396         return 0;
397 }
398
399 bool fxwt::set_font(Font fnt) {
400         error(FT_NOT_COMPILED);
401         return false;
402 }
403
404 bool fxwt::set_font(FontStyle fstyle) {
405         error(FT_NOT_COMPILED);
406         return false;
407 }
408
409 const char *fxwt::get_font_name(Font fnt) {
410         error(FT_NOT_COMPILED);
411         return 0;
412 }
413
414 Texture *fxwt::get_text(const char *text_str) {
415         error(FT_NOT_COMPILED);
416         return 0;
417 }
418
419 void fxwt::print_text(const char *text_str, const Vector2 &pos, scalar_t size, const Color &col) {
420         static bool first = true;
421         if(first) {
422                 error(FT_NOT_COMPILED);
423                 first = false;
424         }
425 }
426
427 #endif  // FXWT_NO_FREETYPE