1 | /* |
2 | * OGLFT: A library for drawing text with OpenGL using the FreeType library |
3 | * Copyright (C) 2002 lignum Computing, Inc. <oglft@lignumcomputing.com> |
4 | * $Id: OGLFT.cpp,v 1.11 2003/10/01 14:21:18 allen Exp $ |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * This library 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 GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, write |
18 | * Free Software Foundation, Inc., |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #include <string.h> |
23 | #include <cmath> |
24 | #include <iomanip> |
25 | #include <iostream> |
26 | #include <utility> |
27 | |
28 | #include "OGLFT.h" |
29 | |
30 | int wstrlen(const wchar_t * s) |
31 | { |
32 | int r = 0; |
33 | while (*s++) r++; |
34 | return r; |
35 | } |
36 | |
37 | namespace OGLFT |
38 | { |
39 | FT_Library ft_library; |
40 | bool Init_FT(void) |
41 | { |
42 | FT_Error error = FT_Init_FreeType(&ft_library); |
43 | if(error != 0) std::cerr << "[OGLFT] Could not initialize the FreeType library." << std::endl; |
44 | return (error == 0); |
45 | } |
46 | bool Uninit_FT(void) |
47 | { |
48 | FT_Error error = FT_Done_FreeType(ft_library); |
49 | if(error != 0) std::cerr << "[OGLFT] Could not terminate the FreeType library." << std::endl; |
50 | return (error == 0); |
51 | } |
52 | |
53 | // Load a new face |
54 | Face::Face (const char* filename, float point_size, FT_UInt resolution) |
55 | : point_size_(point_size), resolution_(resolution) |
56 | { |
57 | valid_ = true; |
58 | FT_Face ft_face; |
59 | FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face); |
60 | if(error != 0) |
61 | { |
62 | valid_ = false; |
63 | return; |
64 | } |
65 | |
66 | // As of FreeType 2.1: only a UNICODE charmap is automatically activated. |
67 | // If no charmap is activated automatically, just use the first one. |
68 | if(ft_face->charmap == 0 && ft_face->num_charmaps > 0) FT_Select_Charmap(ft_face, ft_face->charmaps[0]->encoding); |
69 | |
70 | faces_.push_back(FaceData(ft_face)); |
71 | |
72 | init(); |
73 | } |
74 | |
75 | // Go with a face that the user has already opened. |
76 | Face::Face (FT_Face face, float point_size, FT_UInt resolution) |
77 | : point_size_(point_size), resolution_(resolution) |
78 | { |
79 | valid_ = true; |
80 | |
81 | // As of FreeType 2.1: only a UNICODE charmap is automatically activated. |
82 | // If no charmap is activated automatically, just use the first one. |
83 | if(face->charmap == 0 && face->num_charmaps > 0) FT_Select_Charmap(face, face->charmaps[0]->encoding); |
84 | |
85 | faces_.push_back(FaceData(face, false)); |
86 | |
87 | init(); |
88 | } |
89 | |
90 | // Standard initialization behavior once the font file is opened. |
91 | void Face::init (void) |
92 | { |
93 | // By default, each glyph is compiled into a display list the first |
94 | // time it is encountered |
95 | compile_mode_ = COMPILE; |
96 | |
97 | // By default, all drawing is wrapped with push/pop matrix so that the |
98 | // MODELVIEW matrix is not modified. If advance_ is set, then subsequent |
99 | // drawings follow from the advance of the last glyph rendered. |
100 | advance_ = false; |
101 | |
102 | // Initialize the default colors |
103 | foreground_color_[R] = 0.; foreground_color_[G] = 0.; foreground_color_[B] = 0.; foreground_color_[A] = 1.; |
104 | background_color_[R] = 1.; background_color_[G] = 1.; background_color_[B] = 1.; background_color_[A] = 0.; |
105 | |
106 | // The default positioning of the text is at the origin of the first glyph |
107 | horizontal_justification_ = ORIGIN; |
108 | vertical_justification_ = BASELINE; |
109 | |
110 | // By default, strings are rendered in their nominal direction |
111 | string_rotation_ = 0; |
112 | |
113 | // setCharacterRotationReference calls the virtual function clearCaches() |
114 | // so it is up to a subclass to set the real default |
115 | rotation_reference_glyph_ = 0; |
116 | rotation_reference_face_ = 0; |
117 | rotation_offset_y_ = 0.; |
118 | } |
119 | |
120 | Face::~Face (void) |
121 | { |
122 | for(unsigned int i=0; i<faces_.size(); i++) |
123 | if(faces_[i].free_on_exit_) |
124 | FT_Done_Face(faces_[i].face_); |
125 | } |
126 | |
127 | // Add another Face to select characters from |
128 | bool Face::addAuxiliaryFace (const char* filename) |
129 | { |
130 | FT_Face ft_face; |
131 | |
132 | FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face); |
133 | |
134 | if(error != 0) return false; |
135 | |
136 | faces_.push_back(FaceData(ft_face)); |
137 | setCharSize(); |
138 | |
139 | return true; |
140 | } |
141 | |
142 | // Add another Face to select characters from |
143 | bool Face::addAuxiliaryFace (FT_Face face) |
144 | { |
145 | faces_.push_back(FaceData(face, false)); |
146 | |
147 | setCharSize(); |
148 | |
149 | return true; |
150 | } |
151 | |
152 | // Note: Changing the point size also clears the display list cache |
153 | void Face::setPointSize (float point_size) |
154 | { |
155 | if(point_size != point_size_) |
156 | { |
157 | point_size_ = point_size; |
158 | clearCaches(); |
159 | setCharSize(); |
160 | } |
161 | } |
162 | |
163 | // Note: Changing the resolution also clears the display list cache |
164 | void Face::setResolution (FT_UInt resolution) |
165 | { |
166 | if(resolution != resolution_) |
167 | { |
168 | resolution_ = resolution; |
169 | clearCaches(); |
170 | setCharSize(); |
171 | } |
172 | } |
173 | |
174 | // Note: Changing the background color also clears the display list cache. |
175 | void Face::setBackgroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) |
176 | { |
177 | if(background_color_[R] != red||background_color_[G] != green||background_color_[B] != blue||background_color_[A] != alpha) |
178 | { |
179 | background_color_[R] = red; |
180 | background_color_[G] = green; |
181 | background_color_[B] = blue; |
182 | background_color_[A] = alpha; |
183 | } |
184 | } |
185 | |
186 | // Note: Changing the foreground color also clears the display list cache. |
187 | void Face::setForegroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) |
188 | { |
189 | if(foreground_color_[R] != red||foreground_color_[G] != green||foreground_color_[B] != blue||foreground_color_[A] != alpha) |
190 | { |
191 | foreground_color_[R] = red; |
192 | foreground_color_[G] = green; |
193 | foreground_color_[B] = blue; |
194 | foreground_color_[A] = alpha; |
195 | } |
196 | } |
197 | |
198 | // Note: Changing the foreground color also clears the display list cache. |
199 | void Face::setForegroundColor (const GLfloat foreground_color[4]) |
200 | { |
201 | foreground_color_[R] = foreground_color[R]; |
202 | foreground_color_[G] = foreground_color[G]; |
203 | foreground_color_[B] = foreground_color[B]; |
204 | foreground_color_[A] = foreground_color[A]; |
205 | } |
206 | |
207 | // Note: Changing the background color also clears the display list cache. |
208 | void Face::setBackgroundColor (const GLfloat background_color[4]) |
209 | { |
210 | background_color_[R] = background_color[R]; |
211 | background_color_[G] = background_color[G]; |
212 | background_color_[B] = background_color[B]; |
213 | background_color_[A] = background_color[A]; |
214 | } |
215 | |
216 | // Note: Changing the string rotation angle clears the display list cache |
217 | void Face::setStringRotation (GLfloat string_rotation) |
218 | { |
219 | if(string_rotation != string_rotation_) |
220 | { |
221 | string_rotation_ = string_rotation; |
222 | |
223 | clearCaches(); |
224 | |
225 | // Note that this affects ALL glyphs accessed through |
226 | // the Face, both the vector and the raster glyphs. Very nice! |
227 | if (string_rotation_ != 0) |
228 | { |
229 | float angle; |
230 | if (string_rotation_<0) |
231 | angle = 360.0f - fmod(fabs(string_rotation_), 360.f); |
232 | else |
233 | angle = fmod(string_rotation_, 360.f); |
234 | |
235 | FT_Matrix rotation_matrix; |
236 | FT_Vector sinus; |
237 | |
238 | FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L)); |
239 | |
240 | rotation_matrix.xx = sinus.x; |
241 | rotation_matrix.xy = -sinus.y; |
242 | rotation_matrix.yx = sinus.y; |
243 | rotation_matrix.yy = sinus.x; |
244 | |
245 | for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, &rotation_matrix, 0); |
246 | } |
247 | else for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, 0, 0); |
248 | } |
249 | } |
250 | |
251 | // Note: Changing the rotation reference character clears the display list cache. |
252 | void Face::setCharacterRotationReference (unsigned char c) |
253 | { |
254 | unsigned int f; |
255 | FT_UInt glyph_index = 0; |
256 | |
257 | for(f=0; f<faces_.size(); f++) |
258 | { |
259 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
260 | if(glyph_index != 0) break; |
261 | } |
262 | |
263 | if(f<faces_.size() && glyph_index != rotation_reference_glyph_) |
264 | { |
265 | FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT); |
266 | |
267 | if(error != 0) return; |
268 | |
269 | rotation_reference_glyph_ = glyph_index; |
270 | rotation_reference_face_ = faces_[f].face_; |
271 | setRotationOffset(); |
272 | |
273 | clearCaches(); |
274 | } |
275 | } |
276 | |
277 | BBox Face::measure (const char* s) |
278 | { |
279 | BBox bbox; |
280 | char c; |
281 | |
282 | if((c = *s++) != 0) |
283 | { |
284 | bbox = measure((unsigned char)c); |
285 | |
286 | for(c = *s; c != 0; c = *++s) |
287 | { |
288 | BBox char_bbox = measure((unsigned char)c); |
289 | bbox += char_bbox; |
290 | } |
291 | } |
292 | // make sure the origin is at 0,0 |
293 | if (bbox.x_min_ != 0) |
294 | { |
295 | bbox.x_max_ -= bbox.x_min_; |
296 | bbox.x_min_ = 0; |
297 | } |
298 | if (bbox.y_min_ != 0) |
299 | { |
300 | bbox.y_max_ -= bbox.y_min_; |
301 | bbox.y_min_ = 0; |
302 | } |
303 | |
304 | return bbox; |
305 | } |
306 | |
307 | BBox Face::measureRaw (const char* s) |
308 | { |
309 | BBox bbox; |
310 | |
311 | for(char c = *s; c != 0; c = *++s) |
312 | { |
313 | BBox char_bbox; |
314 | |
315 | unsigned int f; |
316 | FT_UInt glyph_index = 0; |
317 | |
318 | for(f=0; f<faces_.size(); f++) |
319 | { |
320 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
321 | if(glyph_index != 0) break; |
322 | } |
323 | |
324 | if(glyph_index == 0) continue; |
325 | |
326 | FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT); |
327 | if(error != 0) continue; |
328 | |
329 | FT_Glyph glyph; |
330 | error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph); |
331 | if(error != 0) continue; |
332 | |
333 | FT_BBox ft_bbox; |
334 | FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox); |
335 | |
336 | FT_Done_Glyph(glyph); |
337 | |
338 | char_bbox = ft_bbox; |
339 | char_bbox.advance_ = faces_[f].face_->glyph->advance; |
340 | |
341 | bbox += char_bbox; |
342 | } |
343 | |
344 | return bbox; |
345 | } |
346 | |
347 | BBox Face::measure (const wchar_t* s) |
348 | { |
349 | BBox bbox; |
350 | int i; |
351 | |
352 | if(wstrlen(s) > 0) |
353 | { |
354 | bbox = measure(s[0]); |
355 | for(i = 1; i < wstrlen(s); i++) |
356 | { |
357 | BBox char_bbox = measure(s[i]); |
358 | bbox += char_bbox; |
359 | } |
360 | } |
361 | // make sure the origin is at 0,0 |
362 | if (bbox.x_min_ != 0) |
363 | { |
364 | bbox.x_max_ -= bbox.x_min_; |
365 | bbox.x_min_ = 0; |
366 | } |
367 | if (bbox.y_min_ != 0) |
368 | { |
369 | bbox.y_max_ -= bbox.y_min_; |
370 | bbox.y_min_ = 0; |
371 | } |
372 | return bbox; |
373 | } |
374 | |
375 | BBox Face::measureRaw (const wchar_t* s) |
376 | { |
377 | BBox bbox; |
378 | int i; |
379 | |
380 | for(i = 0; i < wstrlen(s); i++) |
381 | { |
382 | BBox char_bbox; |
383 | |
384 | unsigned int f; |
385 | FT_UInt glyph_index = 0; |
386 | |
387 | for(f=0; f<faces_.size(); f++) |
388 | { |
389 | glyph_index = FT_Get_Char_Index(faces_[f].face_, s[i]); |
390 | if(glyph_index != 0) break; |
391 | } |
392 | |
393 | if(glyph_index == 0) |
394 | { |
395 | continue; |
396 | } |
397 | |
398 | FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT); |
399 | if(error != 0) continue; |
400 | |
401 | FT_Glyph glyph; |
402 | error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph); |
403 | if(error != 0) continue; |
404 | |
405 | FT_BBox ft_bbox; |
406 | FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox); |
407 | |
408 | FT_Done_Glyph(glyph); |
409 | |
410 | char_bbox = ft_bbox; |
411 | char_bbox.advance_ = faces_[f].face_->glyph->advance; |
412 | |
413 | bbox += char_bbox; |
414 | } |
415 | |
416 | return bbox; |
417 | } |
418 | |
419 | // Measure the bounding box as if the (latin1) string were not rotated |
420 | BBox Face::measure_nominal (const char* s) |
421 | { |
422 | if(string_rotation_ == 0.) return measure(s); |
423 | |
424 | for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, 0, 0); |
425 | |
426 | BBox bbox = measure(s); |
427 | |
428 | float angle; |
429 | if(string_rotation_<0.) |
430 | angle = 360.0f - fmod(fabs(string_rotation_), 360.f); |
431 | else |
432 | angle = fmod(string_rotation_, 360.f); |
433 | |
434 | FT_Matrix rotation_matrix; |
435 | FT_Vector sinus; |
436 | |
437 | FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L)); |
438 | |
439 | rotation_matrix.xx = sinus.x; |
440 | rotation_matrix.xy = -sinus.y; |
441 | rotation_matrix.yx = sinus.y; |
442 | rotation_matrix.yy = sinus.x; |
443 | |
444 | for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0); |
445 | |
446 | return bbox; |
447 | } |
448 | |
449 | // Measure the bounding box as if the (UNICODE) string were not rotated |
450 | BBox Face::measure_nominal (const wchar_t* s) |
451 | { |
452 | if(string_rotation_ == 0.)return measure(s); |
453 | |
454 | for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, 0, 0); |
455 | |
456 | BBox bbox = measure(s); |
457 | |
458 | float angle; |
459 | if(string_rotation_<0.0) |
460 | angle = 360.0f - fmod(fabs(string_rotation_), 360.f); |
461 | else |
462 | angle = fmod(string_rotation_, 360.f); |
463 | |
464 | FT_Matrix rotation_matrix; |
465 | FT_Vector sinus; |
466 | |
467 | FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L)); |
468 | |
469 | rotation_matrix.xx = sinus.x; |
470 | rotation_matrix.xy = -sinus.y; |
471 | rotation_matrix.yx = sinus.y; |
472 | rotation_matrix.yy = sinus.x; |
473 | |
474 | for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0); |
475 | |
476 | return bbox; |
477 | } |
478 | |
479 | // Compile a (latin1) character glyph into a display list and cache |
480 | // it for later |
481 | |
482 | GLuint Face::compile (unsigned char c) |
483 | { |
484 | // See if we've done it already |
485 | GDLCI fgi = glyph_dlists_.find(c); |
486 | |
487 | if(fgi != glyph_dlists_.end())return fgi->second; |
488 | |
489 | unsigned int f; |
490 | FT_UInt glyph_index = 0; |
491 | |
492 | for(f=0; f<faces_.size(); f++) |
493 | { |
494 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
495 | if(glyph_index != 0) break; |
496 | } |
497 | |
498 | if(glyph_index == 0)return 0; |
499 | |
500 | GLuint dlist = compileGlyph(faces_[f].face_, glyph_index); |
501 | glyph_dlists_[ c ] = dlist; |
502 | |
503 | return dlist; |
504 | } |
505 | |
506 | |
507 | // Compile a (UNICODE) character glyph into a display list and cache |
508 | // it for later |
509 | GLuint Face::compile (const wchar_t c) |
510 | { |
511 | // See if we've done it already |
512 | GDLCI fgi = glyph_dlists_.find(c); |
513 | |
514 | if(fgi != glyph_dlists_.end())return fgi->second; |
515 | |
516 | unsigned int f; |
517 | FT_UInt glyph_index = 0; |
518 | |
519 | for(f=0; f<faces_.size(); f++) |
520 | { |
521 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
522 | if(glyph_index != 0) break; |
523 | } |
524 | |
525 | if(glyph_index == 0)return 0; |
526 | |
527 | GLuint dlist = compileGlyph(faces_[f].face_, glyph_index); |
528 | |
529 | glyph_dlists_[ c ] = dlist; |
530 | |
531 | return dlist; |
532 | } |
533 | |
534 | // Assume the MODELVIEW matrix is already set and draw the (latin1) |
535 | // string. Note: this routine now ignores almost all settings: |
536 | // including the position (both modelview and raster), color, |
537 | // justification and advance settings. Consider this to be the raw |
538 | // drawing routine for which you are responsible for most of the |
539 | // setup. |
540 | void Face::draw (const char* s) |
541 | { |
542 | DLCI character_display_list = character_display_lists_.begin(); |
543 | |
544 | for(char c = *s; c != 0; c = *++s) |
545 | { |
546 | if(character_display_list != character_display_lists_.end()) |
547 | { |
548 | glCallList(*character_display_list); |
549 | character_display_list++; |
550 | } |
551 | draw((unsigned char)c); |
552 | } |
553 | } |
554 | |
555 | // Assume the MODELVIEW matrix is already set and draw the (UNICODE) |
556 | // string. Note: this routine now ignores almost all settings: |
557 | // including the position (both modelview and raster), color, |
558 | // justification and advance settings. Consider this to be the raw |
559 | // drawing routine for which you are responsible for most of the |
560 | // setup. |
561 | void Face::draw (const wchar_t* s) |
562 | { |
563 | DLCI character_display_list = character_display_lists_.begin(); |
564 | int i; |
565 | |
566 | for(i = 0; i < wstrlen(s); i++) |
567 | { |
568 | if(character_display_list != character_display_lists_.end()) |
569 | { |
570 | glCallList(*character_display_list); |
571 | character_display_list++; |
572 | } |
573 | draw(s[i]); |
574 | } |
575 | } |
576 | |
577 | // Assume the MODELVIEW matrix is already setup and draw the |
578 | // (latin1) character. |
579 | void Face::draw (unsigned char c) |
580 | { |
581 | // See if we've done it already |
582 | GDLCI fgi = glyph_dlists_.find(c); |
583 | |
584 | if(fgi != glyph_dlists_.end()) |
585 | { |
586 | glCallList(fgi->second); |
587 | return; |
588 | } |
589 | |
590 | unsigned int f; |
591 | FT_UInt glyph_index = 0; |
592 | |
593 | for(f=0; f<faces_.size(); f++) |
594 | { |
595 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
596 | if(glyph_index != 0) break; |
597 | } |
598 | |
599 | if(glyph_index == 0) return; |
600 | |
601 | if(compile_mode_ == COMPILE) |
602 | { |
603 | GLuint dlist = compile(c); |
604 | glCallList(dlist); |
605 | } |
606 | else renderGlyph(faces_[f].face_, glyph_index); |
607 | } |
608 | |
609 | // Assume the MODELVIEW matrix is already setup and draw the |
610 | // (UNICODE) character. |
611 | |
612 | void Face::draw (const wchar_t c) |
613 | { |
614 | // See if we've done it already |
615 | GDLCI fgi = glyph_dlists_.find(c); |
616 | |
617 | if(fgi != glyph_dlists_.end()) |
618 | { |
619 | glCallList(fgi->second); |
620 | return; |
621 | } |
622 | |
623 | unsigned int f; |
624 | FT_UInt glyph_index = 0; |
625 | |
626 | for(f=0; f<faces_.size(); f++) |
627 | { |
628 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
629 | if(glyph_index != 0) break; |
630 | } |
631 | |
632 | if(glyph_index == 0) return; |
633 | |
634 | if(compile_mode_ == COMPILE) |
635 | { |
636 | GLuint dlist = compile(c); |
637 | glCallList(dlist); |
638 | } |
639 | else renderGlyph(faces_[f].face_, glyph_index); |
640 | } |
641 | |
642 | // Draw the (latin1) character at the given position. The MODELVIEW |
643 | // matrix is modified by the glyph advance. |
644 | void Face::draw (GLfloat x, GLfloat y, unsigned char c) |
645 | { |
646 | glTranslatef(x, y, 0.); |
647 | |
648 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
649 | |
650 | glRasterPos3i(0, 0, 0); |
651 | |
652 | draw(c); |
653 | } |
654 | |
655 | // Draw the (latin1) character at the given position. The MODELVIEW |
656 | // matrix is modified by the glyph advance. |
657 | void Face::draw (GLfloat x, GLfloat y, GLfloat z, unsigned char c) |
658 | { |
659 | glTranslatef(x, y, z); |
660 | |
661 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
662 | |
663 | glRasterPos3i(0, 0, 0); |
664 | |
665 | draw(c); |
666 | } |
667 | |
668 | // Draw the (UNICODE) character at the given position. The MODELVIEW |
669 | // matrix is modified by the glyph advance. |
670 | void Face::draw (GLfloat x, GLfloat y, wchar_t c) |
671 | { |
672 | glTranslatef(x, y, 0.); |
673 | |
674 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], |
675 | foreground_color_[A]); |
676 | |
677 | glRasterPos3i(0, 0, 0); |
678 | |
679 | draw(c); |
680 | } |
681 | |
682 | // Draw the (UNICODE) character at the given position. The MODELVIEW |
683 | // matrix is modified by the glyph advance. |
684 | void Face::draw (GLfloat x, GLfloat y, GLfloat z, wchar_t c) |
685 | { |
686 | glTranslatef(x, y, z); |
687 | |
688 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
689 | |
690 | glRasterPos3i(0, 0, 0); |
691 | |
692 | draw(c); |
693 | } |
694 | |
695 | |
696 | // Draw the (latin1) string at the given position. |
697 | void Face::draw (GLfloat x, GLfloat y, const char* s, float *sizebox) |
698 | { |
699 | // sizebox is xmin,ymin, xmax,ymax |
700 | if(!advance_) glPushMatrix(); |
701 | |
702 | if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) |
703 | { |
704 | glPushMatrix(); |
705 | |
706 | GLfloat dx = 0, dy = 0; |
707 | |
708 | switch (horizontal_justification_) |
709 | { |
710 | case LEFT: dx = -sizebox[0] + 1; break; |
711 | case CENTER: dx = -(sizebox[0] + sizebox[2])/ 2.0f; break; |
712 | case RIGHT: dx = -sizebox[2] - 1; break; |
713 | default: break; |
714 | } |
715 | switch (vertical_justification_) |
716 | { |
717 | case BOTTOM: dy = -sizebox[1] + 1; break; |
718 | case MIDDLE: dy = -(sizebox[1] + sizebox[3])/ 2.0f; break; |
719 | case TOP: dy = -sizebox[3] - 1; break; |
720 | default: break; |
721 | } |
722 | |
723 | // There is probably a less expensive way to compute this |
724 | glRotatef(string_rotation_, 0., 0., 1.); |
725 | glTranslatef(dx, dy, 0); |
726 | glRotatef(-string_rotation_, 0., 0., 1.); |
727 | } |
728 | |
729 | glTranslatef(x, y, 0.); |
730 | |
731 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
732 | |
733 | glRasterPos3i(0, 0, 0); |
734 | |
735 | draw(s); |
736 | |
737 | if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix(); |
738 | |
739 | if(!advance_) glPopMatrix(); |
740 | } |
741 | |
742 | // Draw the (latin1) string at the given position. |
743 | void Face::draw (GLfloat x, GLfloat y, GLfloat z, const char* s) |
744 | { |
745 | if(!advance_) glPushMatrix(); |
746 | |
747 | if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) |
748 | { |
749 | glPushMatrix(); |
750 | |
751 | BBox bbox = measure_nominal(s); |
752 | |
753 | GLfloat dx = 0, dy = 0; |
754 | |
755 | switch (horizontal_justification_) |
756 | { |
757 | case LEFT: dx = -bbox.x_min_; break; |
758 | case CENTER: dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break; |
759 | case RIGHT: dx = -bbox.x_max_; break; |
760 | default: break; |
761 | } |
762 | switch (vertical_justification_) |
763 | { |
764 | case BOTTOM: dy = -bbox.y_min_; break; |
765 | case MIDDLE: dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break; |
766 | case TOP: dy = -bbox.y_max_; break; |
767 | default: break; |
768 | } |
769 | |
770 | // There is probably a less expensive way to compute this |
771 | glRotatef(string_rotation_, 0., 0., 1.); |
772 | glTranslatef(dx, dy, 0); |
773 | glRotatef(-string_rotation_, 0., 0., 1.); |
774 | } |
775 | |
776 | glTranslatef(x, y, z); |
777 | |
778 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
779 | |
780 | glRasterPos3i(0, 0, 0); |
781 | |
782 | draw(s); |
783 | |
784 | if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix(); |
785 | |
786 | if(!advance_) glPopMatrix(); |
787 | } |
788 | |
789 | // Draw the (UNICODE) string at the given position. |
790 | void Face::draw (GLfloat x, GLfloat y, const wchar_t* s) |
791 | { |
792 | if(!advance_) |
793 | glPushMatrix(); |
794 | |
795 | if(horizontal_justification_!=ORIGIN||vertical_justification_!=BASELINE) |
796 | { |
797 | glPushMatrix(); |
798 | |
799 | BBox bbox = measure_nominal(s); |
800 | |
801 | GLfloat dx = 0, dy = 0; |
802 | |
803 | switch (horizontal_justification_) |
804 | { |
805 | case LEFT: |
806 | dx = -bbox.x_min_; break; |
807 | case CENTER: |
808 | dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break; |
809 | case RIGHT: |
810 | dx = -bbox.x_max_; break; |
811 | default: |
812 | break; |
813 | } |
814 | switch (vertical_justification_) |
815 | { |
816 | case BOTTOM: |
817 | dy = -bbox.y_min_; break; |
818 | case MIDDLE: |
819 | dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break; |
820 | case TOP: |
821 | dy = -bbox.y_max_; break; |
822 | default: |
823 | break; |
824 | } |
825 | |
826 | // There is probably a less expensive way to compute this |
827 | glRotatef(string_rotation_, 0., 0., 1.); |
828 | glTranslatef(dx, dy, 0); |
829 | glRotatef(-string_rotation_, 0., 0., 1.); |
830 | } |
831 | |
832 | glTranslatef(x, y, 0.); |
833 | |
834 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], |
835 | foreground_color_[A]); |
836 | |
837 | glRasterPos3i(0, 0, 0); |
838 | |
839 | draw(s); |
840 | |
841 | if(horizontal_justification_ != ORIGIN || |
842 | vertical_justification_ != BASELINE) |
843 | glPopMatrix(); |
844 | |
845 | if(!advance_) |
846 | glPopMatrix(); |
847 | } |
848 | |
849 | // Draw the (UNICODE) string at the given position. |
850 | void Face::draw (GLfloat x, GLfloat y, GLfloat z, const wchar_t* s) |
851 | { |
852 | if(!advance_) glPushMatrix(); |
853 | |
854 | if(horizontal_justification_!= ORIGIN||vertical_justification_!= BASELINE) |
855 | { |
856 | glPushMatrix(); |
857 | |
858 | // In 3D, we need to exert more care in the computation of the |
859 | // bounding box of the text. NOTE: Needs to be fixed up for |
860 | // polygonal faces, too... |
861 | |
862 | BBox bbox; |
863 | // Code from measure_nominal, but changed to use measureRaw instead |
864 | if(string_rotation_ == 0.) bbox = measureRaw(s); |
865 | else |
866 | { |
867 | for(unsigned int f=0; f<faces_.size(); f++) |
868 | FT_Set_Transform(faces_[f].face_, 0, 0); |
869 | |
870 | bbox = measureRaw(s); |
871 | |
872 | float angle; |
873 | if(string_rotation_<0.0) |
874 | angle = 360.0f - fmod(fabs(string_rotation_), 360.f); |
875 | else |
876 | angle = fmod(string_rotation_, 360.f); |
877 | |
878 | FT_Matrix rotation_matrix; |
879 | FT_Vector sinus; |
880 | |
881 | FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L)); |
882 | |
883 | rotation_matrix.xx = sinus.x; |
884 | rotation_matrix.xy = -sinus.y; |
885 | rotation_matrix.yx = sinus.y; |
886 | rotation_matrix.yy = sinus.x; |
887 | |
888 | for(unsigned int f=0; f<faces_.size(); f++) |
889 | FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0); |
890 | } |
891 | |
892 | GLfloat dx = 0, dy = 0; |
893 | |
894 | switch (horizontal_justification_) |
895 | { |
896 | case LEFT: |
897 | dx = bbox.x_min_; break; |
898 | case CENTER: |
899 | dx = (bbox.x_min_ + bbox.x_max_)/ 2; break; |
900 | case RIGHT: |
901 | dx = bbox.x_max_; break; |
902 | default: |
903 | break; |
904 | } |
905 | switch (vertical_justification_) |
906 | { |
907 | case BOTTOM: |
908 | dy = bbox.y_min_; break; |
909 | case MIDDLE: |
910 | dy = (bbox.y_min_ + bbox.y_max_)/2; break; |
911 | case TOP: |
912 | dy = bbox.y_max_; break; |
913 | default: |
914 | break; |
915 | } |
916 | |
917 | GLint viewport[4]; |
918 | GLdouble modelview[16], projection[16]; |
919 | |
920 | glGetIntegerv(GL_VIEWPORT, viewport); |
921 | glGetDoublev(GL_MODELVIEW_MATRIX, modelview); |
922 | glGetDoublev(GL_PROJECTION_MATRIX, projection); |
923 | |
924 | GLdouble x0, y0, z0; |
925 | gluUnProject(0, 0, 0, modelview, projection, viewport, &x0, &y0, &z0); |
926 | |
927 | GLdouble dx_m, dy_m, dz_m; |
928 | gluUnProject(dx, dy, 0., modelview, projection, viewport,&dx_m,&dy_m,&dz_m); |
929 | |
930 | glTranslated(x0-dx_m, y0-dy_m, z0-dz_m); |
931 | } |
932 | |
933 | glTranslatef(x, y, z); |
934 | glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]); |
935 | glRasterPos3i(0, 0, 0); |
936 | draw(s); |
937 | |
938 | if(horizontal_justification_!=ORIGIN||vertical_justification_!= BASELINE) |
939 | glPopMatrix(); |
940 | |
941 | if(!advance_) |
942 | glPopMatrix(); |
943 | } |
944 | |
945 | Raster::Raster (const char* filename, float point_size, FT_UInt resolution) |
946 | : Face(filename, point_size, resolution) |
947 | { |
948 | if(!isValid()) return; |
949 | |
950 | init(); |
951 | } |
952 | |
953 | Raster::Raster (FT_Face face, float point_size, FT_UInt resolution) |
954 | : Face(face, point_size, resolution) |
955 | { |
956 | init(); |
957 | } |
958 | |
959 | void Raster::init (void) |
960 | { |
961 | character_rotation_z_ = 0; |
962 | setCharSize(); |
963 | setCharacterRotationReference('o'); |
964 | } |
965 | |
966 | Raster::~Raster (void) |
967 | { |
968 | clearCaches(); |
969 | } |
970 | |
971 | void Raster::setCharacterRotationZ (GLfloat character_rotation_z) |
972 | { |
973 | if(character_rotation_z != character_rotation_z_) |
974 | { |
975 | character_rotation_z_ = character_rotation_z; |
976 | clearCaches(); |
977 | } |
978 | } |
979 | |
980 | double Raster::height (void)const |
981 | { |
982 | if(faces_[0].face_->height > 0) return faces_[0].face_->height / 64.; |
983 | else return faces_[0].face_->size->metrics.y_ppem; |
984 | } |
985 | |
986 | BBox Raster::measure (unsigned char c) |
987 | { |
988 | BBox bbox; |
989 | |
990 | // For starters, just get the unscaled glyph bounding box |
991 | unsigned int f; |
992 | FT_UInt glyph_index = 0; |
993 | |
994 | for(f=0; f<faces_.size(); f++) |
995 | { |
996 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
997 | if(glyph_index != 0) break; |
998 | } |
999 | |
1000 | if(glyph_index == 0) return bbox; |
1001 | |
1002 | FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT); |
1003 | if(error != 0) return bbox; |
1004 | |
1005 | FT_Glyph glyph; |
1006 | error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph); |
1007 | if(error != 0) return bbox; |
1008 | |
1009 | FT_BBox ft_bbox; |
1010 | FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox); |
1011 | |
1012 | FT_Done_Glyph(glyph); |
1013 | |
1014 | bbox = ft_bbox; |
1015 | bbox.advance_ = faces_[f].face_->glyph->advance; |
1016 | |
1017 | // In order to be accurate regarding the placement of text not |
1018 | // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box |
1019 | // of the raster format has to be projected back into the |
1020 | // view's coordinates |
1021 | GLint viewport[4]; |
1022 | GLdouble modelview[16], projection[16]; |
1023 | |
1024 | glGetIntegerv(GL_VIEWPORT, viewport); |
1025 | glGetDoublev(GL_MODELVIEW_MATRIX, modelview); |
1026 | glGetDoublev(GL_PROJECTION_MATRIX, projection); |
1027 | |
1028 | // Well, first we have to get the Origin, since that is the basis |
1029 | // of the bounding box |
1030 | GLdouble x0, y0, z0; |
1031 | gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0); |
1032 | |
1033 | GLdouble x, y, z; |
1034 | gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z); |
1035 | bbox.x_min_ = (float) (x - x0); |
1036 | bbox.y_min_ = (float) (y - y0); |
1037 | |
1038 | gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z); |
1039 | bbox.x_max_ = (float) (x - x0); |
1040 | bbox.y_max_ = (float) (y - y0); |
1041 | |
1042 | gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z); |
1043 | bbox.advance_.dx_ = (float) (x - x0); |
1044 | bbox.advance_.dy_ = (float) (y - y0); |
1045 | |
1046 | return bbox; |
1047 | } |
1048 | |
1049 | BBox Raster::measure (wchar_t c) |
1050 | { |
1051 | BBox bbox; |
1052 | |
1053 | // For starters, just get the unscaled glyph bounding box |
1054 | unsigned int f; |
1055 | FT_UInt glyph_index = 0; |
1056 | |
1057 | for(f=0; f<faces_.size(); f++) |
1058 | { |
1059 | glyph_index = FT_Get_Char_Index(faces_[f].face_, c); |
1060 | if(glyph_index != 0) break; |
1061 | } |
1062 | |
1063 | if(glyph_index == 0) return bbox; |
1064 | |
1065 | FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, |
1066 | FT_LOAD_DEFAULT); |
1067 | if(error != 0) return bbox; |
1068 | |
1069 | FT_Glyph glyph; |
1070 | error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph); |
1071 | if(error != 0) return bbox; |
1072 | |
1073 | FT_BBox ft_bbox; |
1074 | FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox); |
1075 | |
1076 | FT_Done_Glyph(glyph); |
1077 | |
1078 | bbox = ft_bbox; |
1079 | bbox.advance_ = faces_[f].face_->glyph->advance; |
1080 | |
1081 | // In order to be accurate regarding the placement of text not |
1082 | // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box |
1083 | // of the raster format has to be projected back into the |
1084 | // view's coordinates |
1085 | GLint viewport[4]; |
1086 | GLdouble modelview[16], projection[16]; |
1087 | |
1088 | glGetIntegerv(GL_VIEWPORT, viewport); |
1089 | glGetDoublev(GL_MODELVIEW_MATRIX, modelview); |
1090 | glGetDoublev(GL_PROJECTION_MATRIX, projection); |
1091 | |
1092 | // Well, first we have to get the Origin, since that is the basis |
1093 | // of the bounding box |
1094 | GLdouble x0, y0, z0; |
1095 | gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0); |
1096 | |
1097 | GLdouble x, y, z; |
1098 | gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z); |
1099 | bbox.x_min_ = (float) (x - x0); |
1100 | bbox.y_min_ = (float) (y - y0); |
1101 | |
1102 | gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z); |
1103 | bbox.x_max_ = (float) (x - x0); |
1104 | bbox.y_max_ = (float) (y - y0); |
1105 | |
1106 | gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z); |
1107 | bbox.advance_.dx_ = (float) (x - x0); |
1108 | bbox.advance_.dy_ = (float) (y - y0); |
1109 | |
1110 | return bbox; |
1111 | } |
1112 | |
1113 | GLuint Raster::compileGlyph (FT_Face face, FT_UInt glyph_index) |
1114 | { |
1115 | GLuint dlist = glGenLists(1); |
1116 | glNewList(dlist, GL_COMPILE); |
1117 | |
1118 | renderGlyph(face, glyph_index); |
1119 | |
1120 | glEndList(); |
1121 | |
1122 | return dlist; |
1123 | } |
1124 | |
1125 | void Raster::setCharSize (void) |
1126 | { |
1127 | FT_Error error; |
1128 | for(unsigned int i=0; i<faces_.size(); i++) |
1129 | { |
1130 | error = FT_Set_Char_Size(faces_[i].face_,(FT_F26Dot6)(point_size_ * 64),(FT_F26Dot6)(point_size_ * 64),resolution_,resolution_); |
1131 | if(error != 0) return; |
1132 | } |
1133 | |
1134 | if(rotation_reference_glyph_ != 0) setRotationOffset(); |
1135 | } |
1136 | |
1137 | void Raster::setRotationOffset (void) |
1138 | { |
1139 | FT_Error error = FT_Load_Glyph(rotation_reference_face_, rotation_reference_glyph_, FT_LOAD_RENDER); |
1140 | |
1141 | if(error != 0) return; |
1142 | |
1143 | rotation_offset_y_ = rotation_reference_face_->glyph->bitmap.rows / 2.0f; |
1144 | } |
1145 | |
1146 | void Raster::clearCaches (void) |
1147 | { |
1148 | GDLI fgi = glyph_dlists_.begin(); |
1149 | |
1150 | for(; fgi != glyph_dlists_.end(); ++fgi) |
1151 | { |
1152 | glDeleteLists(fgi->second, 1); |
1153 | } |
1154 | |
1155 | glyph_dlists_.clear(); |
1156 | } |
1157 | |
1158 | Monochrome::Monochrome (const char* filename, float point_size, FT_UInt resolution) |
1159 | : Raster(filename, point_size, resolution) |
1160 | { |
1161 | return; |
1162 | } |
1163 | |
1164 | Monochrome::Monochrome (FT_Face face, float point_size, FT_UInt resolution) |
1165 | : Raster(face, point_size, resolution) |
1166 | { |
1167 | return; |
1168 | } |
1169 | |
1170 | Monochrome::~Monochrome (void) |
1171 | { |
1172 | return; |
1173 | } |
1174 | |
1175 | GLubyte* Monochrome::invertBitmap (const FT_Bitmap& bitmap) |
1176 | { |
1177 | // In FreeType 2.0.9, the pitch of bitmaps was rounded up to an |
1178 | // even number. In general, this disagrees with what we had been |
1179 | // using for OpenGL. |
1180 | int width = bitmap.width / 8 + ((bitmap.width & 7)> 0 ? 1 : 0); |
1181 | |
1182 | GLubyte* inverse = new GLubyte[ bitmap.rows * width ]; |
1183 | GLubyte* inverse_ptr = inverse; |
1184 | |
1185 | for(unsigned int r=0; r<bitmap.rows; r++) |
1186 | { |
1187 | GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * (bitmap.rows - r - 1)]; |
1188 | |
1189 | memmove(inverse_ptr, bitmap_ptr, width); |
1190 | inverse_ptr += width; |
1191 | bitmap_ptr += width; |
1192 | } |
1193 | |
1194 | return inverse; |
1195 | } |
1196 | |
1197 | void Monochrome::renderGlyph (FT_Face face, FT_UInt glyph_index) |
1198 | { |
1199 | // Start by retrieving the glyph's data. |
1200 | FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); |
1201 | |
1202 | if(error != 0) return; |
1203 | |
1204 | FT_Glyph original_glyph; |
1205 | FT_Glyph glyph; |
1206 | |
1207 | error = FT_Get_Glyph(face->glyph, &original_glyph); |
1208 | |
1209 | if(error != 0) return; |
1210 | |
1211 | error = FT_Glyph_Copy(original_glyph, &glyph); |
1212 | |
1213 | FT_Done_Glyph(original_glyph); |
1214 | |
1215 | if(error != 0) return; |
1216 | |
1217 | // If the individual characters are rotated (as distinct from string |
1218 | // rotation), then apply that extra rotation here. This is equivalent |
1219 | // to the sequence |
1220 | // glTranslate(x_center,y_center); |
1221 | // glRotate(angle); |
1222 | // glTranslate(-x_center,-y_center); |
1223 | // which is used for the polygonal styles. The deal with the raster |
1224 | // styles is that you must retain the advance from the string rotation |
1225 | // so that the glyphs are laid out properly. So, we make a copy of |
1226 | // the string rotated glyph, and then rotate that and add back an |
1227 | // additional offset to (in effect) restore the proper origin and |
1228 | // advance of the glyph. |
1229 | |
1230 | if(character_rotation_z_ != 0.) |
1231 | { |
1232 | FT_Matrix rotation_matrix; |
1233 | FT_Vector sinus; |
1234 | |
1235 | FT_Vector_Unit(&sinus, (FT_Angle)(character_rotation_z_ * 0x10000L)); |
1236 | |
1237 | rotation_matrix.xx = sinus.x; |
1238 | rotation_matrix.xy = -sinus.y; |
1239 | rotation_matrix.yx = sinus.y; |
1240 | rotation_matrix.yy = sinus.x; |
1241 | |
1242 | FT_Vector original_offset, rotation_offset; |
1243 | |
1244 | original_offset.x = (face->glyph->metrics.width / 2 + face->glyph->metrics.horiBearingX)/ 64 * 0x10000L; |
1245 | original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L); |
1246 | |
1247 | rotation_offset = original_offset; |
1248 | |
1249 | FT_Vector_Rotate(&rotation_offset, (FT_Angle)(character_rotation_z_ * 0x10000L)); |
1250 | |
1251 | rotation_offset.x = original_offset.x - rotation_offset.x; |
1252 | rotation_offset.y = original_offset.y - rotation_offset.y; |
1253 | |
1254 | rotation_offset.x /= 1024; |
1255 | rotation_offset.y /= 1024; |
1256 | |
1257 | error = FT_Glyph_Transform(glyph, &rotation_matrix, &rotation_offset); |
1258 | } |
1259 | |
1260 | error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_MONO, 0, 1); |
1261 | |
1262 | if(error != 0) |
1263 | { |
1264 | FT_Done_Glyph(glyph); |
1265 | return; |
1266 | } |
1267 | |
1268 | FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph; |
1269 | |
1270 | // Evidently, in FreeType2, you can only get "upside-down" bitmaps and |
1271 | // OpenGL won't invert a bitmap with PixelZoom, so we have to invert the |
1272 | // glyph's bitmap ourselves. |
1273 | |
1274 | GLubyte* inverted_bitmap = invertBitmap(bitmap_glyph->bitmap); |
1275 | |
1276 | glBitmap(bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows, |
1277 | (GLfloat) -bitmap_glyph->left, |
1278 | (GLfloat) (bitmap_glyph->bitmap.rows - bitmap_glyph->top), |
1279 | face->glyph->advance.x / 64.0f, |
1280 | face->glyph->advance.y / 64.0f, |
1281 | inverted_bitmap); |
1282 | |
1283 | FT_Done_Glyph(glyph); |
1284 | |
1285 | delete[] inverted_bitmap; |
1286 | } |
1287 | |
1288 | } // close OGLFT namespace |
1289 | |
1290 | |