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
30int wstrlen(const wchar_t * s)
31{
32 int r = 0;
33 while (*s++) r++;
34 return r;
35}
36
37namespace 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