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 $
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.
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.
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.
27 int wstrlen(const wchar_t * s)
36 FT_Library ft_library;
39 FT_Error error = FT_Init_FreeType(&ft_library);
40 if(error != 0) std::cerr << "[OGLFT] Could not initialize the FreeType library." << std::endl;
45 FT_Error error = FT_Done_FreeType(ft_library);
46 if(error != 0) std::cerr << "[OGLFT] Could not terminate the FreeType library." << std::endl;
51 Face::Face (const char* filename, float point_size, FT_UInt resolution)
52 : point_size_(point_size), resolution_(resolution)
56 FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face);
63 // As of FreeType 2.1: only a UNICODE charmap is automatically activated.
64 // If no charmap is activated automatically, just use the first one.
65 if(ft_face->charmap == 0 && ft_face->num_charmaps > 0) FT_Select_Charmap(ft_face, ft_face->charmaps[0]->encoding);
67 faces_.push_back(FaceData(ft_face));
72 // Go with a face that the user has already opened.
73 Face::Face (FT_Face face, float point_size, FT_UInt resolution)
74 : point_size_(point_size), resolution_(resolution)
78 // As of FreeType 2.1: only a UNICODE charmap is automatically activated.
79 // If no charmap is activated automatically, just use the first one.
80 if(face->charmap == 0 && face->num_charmaps > 0) FT_Select_Charmap(face, face->charmaps[0]->encoding);
82 faces_.push_back(FaceData(face, false));
87 // Standard initialization behavior once the font file is opened.
88 void Face::init (void)
90 // By default, each glyph is compiled into a display list the first
91 // time it is encountered
92 compile_mode_ = COMPILE;
94 // By default, all drawing is wrapped with push/pop matrix so that the
95 // MODELVIEW matrix is not modified. If advance_ is set, then subsequent
96 // drawings follow from the advance of the last glyph rendered.
99 // Initialize the default colors
100 foreground_color_[R] = 0.; foreground_color_[G] = 0.; foreground_color_[B] = 0.; foreground_color_[A] = 1.;
101 background_color_[R] = 1.; background_color_[G] = 1.; background_color_[B] = 1.; background_color_[A] = 0.;
103 // The default positioning of the text is at the origin of the first glyph
104 horizontal_justification_ = ORIGIN;
105 vertical_justification_ = BASELINE;
107 // By default, strings are rendered in their nominal direction
108 string_rotation_ = 0;
110 // setCharacterRotationReference calls the virtual function clearCaches()
111 // so it is up to a subclass to set the real default
112 rotation_reference_glyph_ = 0;
113 rotation_reference_face_ = 0;
114 rotation_offset_y_ = 0.;
119 for(unsigned int i=0; i<faces_.size(); i++)
120 if(faces_[i].free_on_exit_)
121 FT_Done_Face(faces_[i].face_);
124 // Add another Face to select characters from
125 bool Face::addAuxiliaryFace (const char* filename)
129 FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face);
131 if(error != 0) return false;
133 faces_.push_back(FaceData(ft_face));
139 // Add another Face to select characters from
140 bool Face::addAuxiliaryFace (FT_Face face)
142 faces_.push_back(FaceData(face, false));
149 // Note: Changing the point size also clears the display list cache
150 void Face::setPointSize (float point_size)
152 if(point_size != point_size_)
154 point_size_ = point_size;
160 // Note: Changing the resolution also clears the display list cache
161 void Face::setResolution (FT_UInt resolution)
163 if(resolution != resolution_)
165 resolution_ = resolution;
171 // Note: Changing the background color also clears the display list cache.
172 void Face::setBackgroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
174 if(background_color_[R] != red||background_color_[G] != green||background_color_[B] != blue||background_color_[A] != alpha)
176 background_color_[R] = red;
177 background_color_[G] = green;
178 background_color_[B] = blue;
179 background_color_[A] = alpha;
183 // Note: Changing the foreground color also clears the display list cache.
184 void Face::setForegroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
186 if(foreground_color_[R] != red||foreground_color_[G] != green||foreground_color_[B] != blue||foreground_color_[A] != alpha)
188 foreground_color_[R] = red;
189 foreground_color_[G] = green;
190 foreground_color_[B] = blue;
191 foreground_color_[A] = alpha;
195 // Note: Changing the foreground color also clears the display list cache.
196 void Face::setForegroundColor (const GLfloat foreground_color[4])
198 foreground_color_[R] = foreground_color[R];
199 foreground_color_[G] = foreground_color[G];
200 foreground_color_[B] = foreground_color[B];
201 foreground_color_[A] = foreground_color[A];
204 // Note: Changing the background color also clears the display list cache.
205 void Face::setBackgroundColor (const GLfloat background_color[4])
207 background_color_[R] = background_color[R];
208 background_color_[G] = background_color[G];
209 background_color_[B] = background_color[B];
210 background_color_[A] = background_color[A];
213 // Note: Changing the string rotation angle clears the display list cache
214 void Face::setStringRotation (GLfloat string_rotation)
216 if(string_rotation != string_rotation_)
218 string_rotation_ = string_rotation;
222 // Note that this affects ALL glyphs accessed through
223 // the Face, both the vector and the raster glyphs. Very nice!
224 if (string_rotation_ != 0)
227 if (string_rotation_<0)
228 angle = 360.0f - fmod(fabs(string_rotation_), 360.f);
230 angle = fmod(string_rotation_, 360.f);
232 FT_Matrix rotation_matrix;
235 FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));
237 rotation_matrix.xx = sinus.x;
238 rotation_matrix.xy = -sinus.y;
239 rotation_matrix.yx = sinus.y;
240 rotation_matrix.yy = sinus.x;
242 for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, &rotation_matrix, 0);
244 else for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, 0, 0);
248 // Note: Changing the rotation reference character clears the display list cache.
249 void Face::setCharacterRotationReference (unsigned char c)
252 FT_UInt glyph_index = 0;
254 for(f=0; f<faces_.size(); f++)
256 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
257 if(glyph_index != 0) break;
260 if(f<faces_.size() && glyph_index != rotation_reference_glyph_)
262 FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);
264 if(error != 0) return;
266 rotation_reference_glyph_ = glyph_index;
267 rotation_reference_face_ = faces_[f].face_;
274 BBox Face::measure (const char* s)
281 bbox = measure((unsigned char)c);
283 for(c = *s; c != 0; c = *++s)
285 BBox char_bbox = measure((unsigned char)c);
289 // make sure the origin is at 0,0
290 if (bbox.x_min_ != 0)
292 bbox.x_max_ -= bbox.x_min_;
295 if (bbox.y_min_ != 0)
297 bbox.y_max_ -= bbox.y_min_;
304 BBox Face::measureRaw (const char* s)
308 for(char c = *s; c != 0; c = *++s)
313 FT_UInt glyph_index = 0;
315 for(f=0; f<faces_.size(); f++)
317 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
318 if(glyph_index != 0) break;
321 if(glyph_index == 0) continue;
323 FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);
324 if(error != 0) continue;
327 error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);
328 if(error != 0) continue;
331 FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);
333 FT_Done_Glyph(glyph);
336 char_bbox.advance_ = faces_[f].face_->glyph->advance;
344 BBox Face::measure (const wchar_t* s)
351 bbox = measure(s[0]);
352 for(i = 1; i < wstrlen(s); i++)
354 BBox char_bbox = measure(s[i]);
358 // make sure the origin is at 0,0
359 if (bbox.x_min_ != 0)
361 bbox.x_max_ -= bbox.x_min_;
364 if (bbox.y_min_ != 0)
366 bbox.y_max_ -= bbox.y_min_;
372 BBox Face::measure (const wchar_t* format, double number)
374 return measure(format, number);
377 BBox Face::measureRaw (const wchar_t* s)
382 for(i = 0; i < wstrlen(s); i++)
387 FT_UInt glyph_index = 0;
389 for(f=0; f<faces_.size(); f++)
391 glyph_index = FT_Get_Char_Index(faces_[f].face_, s[i]);
392 if(glyph_index != 0) break;
400 FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);
401 if(error != 0) continue;
404 error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);
405 if(error != 0) continue;
408 FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);
410 FT_Done_Glyph(glyph);
413 char_bbox.advance_ = faces_[f].face_->glyph->advance;
421 // Measure the bounding box as if the (latin1) string were not rotated
422 BBox Face::measure_nominal (const char* s)
424 if(string_rotation_ == 0.) return measure(s);
426 for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, 0, 0);
428 BBox bbox = measure(s);
431 if(string_rotation_<0.)
432 angle = 360.0f - fmod(fabs(string_rotation_), 360.f);
434 angle = fmod(string_rotation_, 360.f);
436 FT_Matrix rotation_matrix;
439 FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));
441 rotation_matrix.xx = sinus.x;
442 rotation_matrix.xy = -sinus.y;
443 rotation_matrix.yx = sinus.y;
444 rotation_matrix.yy = sinus.x;
446 for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);
451 // Measure the bounding box as if the (UNICODE) string were not rotated
452 BBox Face::measure_nominal (const wchar_t* s)
454 if(string_rotation_ == 0.)return measure(s);
456 for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, 0, 0);
458 BBox bbox = measure(s);
461 if(string_rotation_<0.0)
462 angle = 360.0f - fmod(fabs(string_rotation_), 360.f);
464 angle = fmod(string_rotation_, 360.f);
466 FT_Matrix rotation_matrix;
469 FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));
471 rotation_matrix.xx = sinus.x;
472 rotation_matrix.xy = -sinus.y;
473 rotation_matrix.yx = sinus.y;
474 rotation_matrix.yy = sinus.x;
476 for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);
481 // Compile a (latin1) character glyph into a display list and cache
484 GLuint Face::compile (unsigned char c)
486 // See if we've done it already
487 GDLCI fgi = glyph_dlists_.find(c);
489 if(fgi != glyph_dlists_.end())return fgi->second;
492 FT_UInt glyph_index = 0;
494 for(f=0; f<faces_.size(); f++)
496 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
497 if(glyph_index != 0) break;
500 if(glyph_index == 0)return 0;
502 GLuint dlist = compileGlyph(faces_[f].face_, glyph_index);
503 glyph_dlists_[ c ] = dlist;
509 // Compile a (UNICODE) character glyph into a display list and cache
511 GLuint Face::compile (const wchar_t c)
513 // See if we've done it already
514 GDLCI fgi = glyph_dlists_.find(c);
516 if(fgi != glyph_dlists_.end())return fgi->second;
519 FT_UInt glyph_index = 0;
521 for(f=0; f<faces_.size(); f++)
523 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
524 if(glyph_index != 0) break;
527 if(glyph_index == 0)return 0;
529 GLuint dlist = compileGlyph(faces_[f].face_, glyph_index);
531 glyph_dlists_[ c ] = dlist;
536 // Assume the MODELVIEW matrix is already set and draw the (latin1)
537 // string. Note: this routine now ignores almost all settings:
538 // including the position (both modelview and raster), color,
539 // justification and advance settings. Consider this to be the raw
540 // drawing routine for which you are responsible for most of the
542 void Face::draw (const char* s)
544 DLCI character_display_list = character_display_lists_.begin();
546 for(char c = *s; c != 0; c = *++s)
548 if(character_display_list != character_display_lists_.end())
550 glCallList(*character_display_list);
551 character_display_list++;
553 draw((unsigned char)c);
557 // Assume the MODELVIEW matrix is already set and draw the (UNICODE)
558 // string. Note: this routine now ignores almost all settings:
559 // including the position (both modelview and raster), color,
560 // justification and advance settings. Consider this to be the raw
561 // drawing routine for which you are responsible for most of the
563 void Face::draw (const wchar_t* s)
565 DLCI character_display_list = character_display_lists_.begin();
568 for(i = 0; i < wstrlen(s); i++)
570 if(character_display_list != character_display_lists_.end())
572 glCallList(*character_display_list);
573 character_display_list++;
579 // Assume the MODELVIEW matrix is already setup and draw the
580 // (latin1) character.
581 void Face::draw (unsigned char c)
583 // See if we've done it already
584 GDLCI fgi = glyph_dlists_.find(c);
586 if(fgi != glyph_dlists_.end())
588 glCallList(fgi->second);
593 FT_UInt glyph_index = 0;
595 for(f=0; f<faces_.size(); f++)
597 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
598 if(glyph_index != 0) break;
601 if(glyph_index == 0) return;
603 if(compile_mode_ == COMPILE)
605 GLuint dlist = compile(c);
608 else renderGlyph(faces_[f].face_, glyph_index);
611 // Assume the MODELVIEW matrix is already setup and draw the
612 // (UNICODE) character.
614 void Face::draw (const wchar_t c)
616 // See if we've done it already
617 GDLCI fgi = glyph_dlists_.find(c);
619 if(fgi != glyph_dlists_.end())
621 glCallList(fgi->second);
626 FT_UInt glyph_index = 0;
628 for(f=0; f<faces_.size(); f++)
630 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
631 if(glyph_index != 0) break;
634 if(glyph_index == 0) return;
636 if(compile_mode_ == COMPILE)
638 GLuint dlist = compile(c);
641 else renderGlyph(faces_[f].face_, glyph_index);
644 // Draw the (latin1) character at the given position. The MODELVIEW
645 // matrix is modified by the glyph advance.
646 void Face::draw (GLfloat x, GLfloat y, unsigned char c)
648 glTranslatef(x, y, 0.);
650 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
652 glRasterPos3i(0, 0, 0);
657 // Draw the (latin1) character at the given position. The MODELVIEW
658 // matrix is modified by the glyph advance.
659 void Face::draw (GLfloat x, GLfloat y, GLfloat z, unsigned char c)
661 glTranslatef(x, y, z);
663 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
665 glRasterPos3i(0, 0, 0);
670 // Draw the (UNICODE) character at the given position. The MODELVIEW
671 // matrix is modified by the glyph advance.
672 void Face::draw (GLfloat x, GLfloat y, wchar_t c)
674 glTranslatef(x, y, 0.);
676 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B],
677 foreground_color_[A]);
679 glRasterPos3i(0, 0, 0);
684 // Draw the (UNICODE) character at the given position. The MODELVIEW
685 // matrix is modified by the glyph advance.
686 void Face::draw (GLfloat x, GLfloat y, GLfloat z, wchar_t c)
688 glTranslatef(x, y, z);
690 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
692 glRasterPos3i(0, 0, 0);
698 // Draw the (latin1) string at the given position.
699 void Face::draw (GLfloat x, GLfloat y, const char* s, float *sizebox)
701 // sizebox is xmin,ymin, xmax,ymax
702 if(!advance_) glPushMatrix();
704 if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE)
708 GLfloat dx = 0, dy = 0;
710 switch (horizontal_justification_)
712 case LEFT: dx = -sizebox[0] + 1; break;
713 case CENTER: dx = -(sizebox[0] + sizebox[2])/ 2.0f; break;
714 case RIGHT: dx = -sizebox[2] - 1; break;
717 switch (vertical_justification_)
719 case BOTTOM: dy = -sizebox[1] + 1; break;
720 case MIDDLE: dy = -(sizebox[1] + sizebox[3])/ 2.0f; break;
721 case TOP: dy = -sizebox[3] - 1; break;
725 // There is probably a less expensive way to compute this
726 glRotatef(string_rotation_, 0., 0., 1.);
727 glTranslatef(dx, dy, 0);
728 glRotatef(-string_rotation_, 0., 0., 1.);
731 glTranslatef(x, y, 0.);
733 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
735 glRasterPos3i(0, 0, 0);
739 if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix();
741 if(!advance_) glPopMatrix();
744 // Draw the (latin1) string at the given position.
745 void Face::draw (GLfloat x, GLfloat y, GLfloat z, const char* s)
747 if(!advance_) glPushMatrix();
749 if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE)
753 BBox bbox = measure_nominal(s);
755 GLfloat dx = 0, dy = 0;
757 switch (horizontal_justification_)
759 case LEFT: dx = -bbox.x_min_; break;
760 case CENTER: dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break;
761 case RIGHT: dx = -bbox.x_max_; break;
764 switch (vertical_justification_)
766 case BOTTOM: dy = -bbox.y_min_; break;
767 case MIDDLE: dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break;
768 case TOP: dy = -bbox.y_max_; break;
772 // There is probably a less expensive way to compute this
773 glRotatef(string_rotation_, 0., 0., 1.);
774 glTranslatef(dx, dy, 0);
775 glRotatef(-string_rotation_, 0., 0., 1.);
778 glTranslatef(x, y, z);
780 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
782 glRasterPos3i(0, 0, 0);
786 if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix();
788 if(!advance_) glPopMatrix();
791 // Draw the (UNICODE) string at the given position.
792 void Face::draw (GLfloat x, GLfloat y, const wchar_t* s)
797 if(horizontal_justification_!=ORIGIN||vertical_justification_!=BASELINE)
801 BBox bbox = measure_nominal(s);
803 GLfloat dx = 0, dy = 0;
805 switch (horizontal_justification_)
808 dx = -bbox.x_min_; break;
810 dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break;
812 dx = -bbox.x_max_; break;
816 switch (vertical_justification_)
819 dy = -bbox.y_min_; break;
821 dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break;
823 dy = -bbox.y_max_; break;
828 // There is probably a less expensive way to compute this
829 glRotatef(string_rotation_, 0., 0., 1.);
830 glTranslatef(dx, dy, 0);
831 glRotatef(-string_rotation_, 0., 0., 1.);
834 glTranslatef(x, y, 0.);
836 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B],
837 foreground_color_[A]);
839 glRasterPos3i(0, 0, 0);
843 if(horizontal_justification_ != ORIGIN ||
844 vertical_justification_ != BASELINE)
851 // Draw the (UNICODE) string at the given position.
852 void Face::draw (GLfloat x, GLfloat y, GLfloat z, const wchar_t* s)
854 if(!advance_) glPushMatrix();
856 if(horizontal_justification_!= ORIGIN||vertical_justification_!= BASELINE)
860 // In 3D, we need to exert more care in the computation of the
861 // bounding box of the text. NOTE: Needs to be fixed up for
862 // polygonal faces, too...
865 // Code from measure_nominal, but changed to use measureRaw instead
866 if(string_rotation_ == 0.) bbox = measureRaw(s);
869 for(unsigned int f=0; f<faces_.size(); f++)
870 FT_Set_Transform(faces_[f].face_, 0, 0);
872 bbox = measureRaw(s);
875 if(string_rotation_<0.0)
876 angle = 360.0f - fmod(fabs(string_rotation_), 360.f);
878 angle = fmod(string_rotation_, 360.f);
880 FT_Matrix rotation_matrix;
883 FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));
885 rotation_matrix.xx = sinus.x;
886 rotation_matrix.xy = -sinus.y;
887 rotation_matrix.yx = sinus.y;
888 rotation_matrix.yy = sinus.x;
890 for(unsigned int f=0; f<faces_.size(); f++)
891 FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);
894 GLfloat dx = 0, dy = 0;
896 switch (horizontal_justification_)
899 dx = bbox.x_min_; break;
901 dx = (bbox.x_min_ + bbox.x_max_)/ 2; break;
903 dx = bbox.x_max_; break;
907 switch (vertical_justification_)
910 dy = bbox.y_min_; break;
912 dy = (bbox.y_min_ + bbox.y_max_)/2; break;
914 dy = bbox.y_max_; break;
920 GLdouble modelview[16], projection[16];
922 glGetIntegerv(GL_VIEWPORT, viewport);
923 glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
924 glGetDoublev(GL_PROJECTION_MATRIX, projection);
927 gluUnProject(0, 0, 0, modelview, projection, viewport, &x0, &y0, &z0);
929 GLdouble dx_m, dy_m, dz_m;
930 gluUnProject(dx, dy, 0., modelview, projection, viewport,&dx_m,&dy_m,&dz_m);
932 glTranslated(x0-dx_m, y0-dy_m, z0-dz_m);
935 glTranslatef(x, y, z);
936 glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);
937 glRasterPos3i(0, 0, 0);
940 if(horizontal_justification_!=ORIGIN||vertical_justification_!= BASELINE)
947 Raster::Raster (const char* filename, float point_size, FT_UInt resolution)
948 : Face(filename, point_size, resolution)
950 if(!isValid()) return;
955 Raster::Raster (FT_Face face, float point_size, FT_UInt resolution)
956 : Face(face, point_size, resolution)
961 void Raster::init (void)
963 character_rotation_z_ = 0;
965 setCharacterRotationReference('o');
968 Raster::~Raster (void)
973 void Raster::setCharacterRotationZ (GLfloat character_rotation_z)
975 if(character_rotation_z != character_rotation_z_)
977 character_rotation_z_ = character_rotation_z;
982 double Raster::height (void)const
984 if(faces_[0].face_->height > 0) return faces_[0].face_->height / 64.;
985 else return faces_[0].face_->size->metrics.y_ppem;
988 BBox Raster::measure (unsigned char c)
992 // For starters, just get the unscaled glyph bounding box
994 FT_UInt glyph_index = 0;
996 for(f=0; f<faces_.size(); f++)
998 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
999 if(glyph_index != 0) break;
1002 if(glyph_index == 0) return bbox;
1004 FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);
1005 if(error != 0) return bbox;
1008 error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);
1009 if(error != 0) return bbox;
1012 FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);
1014 FT_Done_Glyph(glyph);
1017 bbox.advance_ = faces_[f].face_->glyph->advance;
1019 // In order to be accurate regarding the placement of text not
1020 // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box
1021 // of the raster format has to be projected back into the
1022 // view's coordinates
1024 GLdouble modelview[16], projection[16];
1026 glGetIntegerv(GL_VIEWPORT, viewport);
1027 glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
1028 glGetDoublev(GL_PROJECTION_MATRIX, projection);
1030 // Well, first we have to get the Origin, since that is the basis
1031 // of the bounding box
1032 GLdouble x0, y0, z0;
1033 gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0);
1036 gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z);
1037 bbox.x_min_ = (float) (x - x0);
1038 bbox.y_min_ = (float) (y - y0);
1040 gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z);
1041 bbox.x_max_ = (float) (x - x0);
1042 bbox.y_max_ = (float) (y - y0);
1044 gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z);
1045 bbox.advance_.dx_ = (float) (x - x0);
1046 bbox.advance_.dy_ = (float) (y - y0);
1051 BBox Raster::measure (wchar_t c)
1055 // For starters, just get the unscaled glyph bounding box
1057 FT_UInt glyph_index = 0;
1059 for(f=0; f<faces_.size(); f++)
1061 glyph_index = FT_Get_Char_Index(faces_[f].face_, c);
1062 if(glyph_index != 0) break;
1065 if(glyph_index == 0) return bbox;
1067 FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index,
1069 if(error != 0) return bbox;
1072 error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);
1073 if(error != 0) return bbox;
1076 FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);
1078 FT_Done_Glyph(glyph);
1081 bbox.advance_ = faces_[f].face_->glyph->advance;
1083 // In order to be accurate regarding the placement of text not
1084 // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box
1085 // of the raster format has to be projected back into the
1086 // view's coordinates
1088 GLdouble modelview[16], projection[16];
1090 glGetIntegerv(GL_VIEWPORT, viewport);
1091 glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
1092 glGetDoublev(GL_PROJECTION_MATRIX, projection);
1094 // Well, first we have to get the Origin, since that is the basis
1095 // of the bounding box
1096 GLdouble x0, y0, z0;
1097 gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0);
1100 gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z);
1101 bbox.x_min_ = (float) (x - x0);
1102 bbox.y_min_ = (float) (y - y0);
1104 gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z);
1105 bbox.x_max_ = (float) (x - x0);
1106 bbox.y_max_ = (float) (y - y0);
1108 gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z);
1109 bbox.advance_.dx_ = (float) (x - x0);
1110 bbox.advance_.dy_ = (float) (y - y0);
1115 GLuint Raster::compileGlyph (FT_Face face, FT_UInt glyph_index)
1117 GLuint dlist = glGenLists(1);
1118 glNewList(dlist, GL_COMPILE);
1120 renderGlyph(face, glyph_index);
1127 void Raster::setCharSize (void)
1130 for(unsigned int i=0; i<faces_.size(); i++)
1132 error = FT_Set_Char_Size(faces_[i].face_,(FT_F26Dot6)(point_size_ * 64),(FT_F26Dot6)(point_size_ * 64),resolution_,resolution_);
1133 if(error != 0) return;
1136 if(rotation_reference_glyph_ != 0) setRotationOffset();
1139 void Raster::setRotationOffset (void)
1141 FT_Error error = FT_Load_Glyph(rotation_reference_face_, rotation_reference_glyph_, FT_LOAD_RENDER);
1143 if(error != 0) return;
1145 rotation_offset_y_ = rotation_reference_face_->glyph->bitmap.rows / 2.0f;
1148 void Raster::clearCaches (void)
1150 GDLI fgi = glyph_dlists_.begin();
1152 for(; fgi != glyph_dlists_.end(); ++fgi)
1154 glDeleteLists(fgi->second, 1);
1157 glyph_dlists_.clear();
1160 Monochrome::Monochrome (const char* filename, float point_size, FT_UInt resolution)
1161 : Raster(filename, point_size, resolution)
1166 Monochrome::Monochrome (FT_Face face, float point_size, FT_UInt resolution)
1167 : Raster(face, point_size, resolution)
1172 Monochrome::~Monochrome (void)
1177 GLubyte* Monochrome::invertBitmap (const FT_Bitmap& bitmap)
1179 // In FreeType 2.0.9, the pitch of bitmaps was rounded up to an
1180 // even number. In general, this disagrees with what we had been
1181 // using for OpenGL.
1182 int width = bitmap.width / 8 + ((bitmap.width & 7)> 0 ? 1 : 0);
1184 GLubyte* inverse = new GLubyte[ bitmap.rows * width ];
1185 GLubyte* inverse_ptr = inverse;
1187 for(int r=0; r<bitmap.rows; r++)
1189 GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * (bitmap.rows - r - 1)];
1191 memmove(inverse_ptr, bitmap_ptr, width);
1192 inverse_ptr += width;
1193 bitmap_ptr += width;
1199 void Monochrome::renderGlyph (FT_Face face, FT_UInt glyph_index)
1201 // Start by retrieving the glyph's data.
1202 FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
1204 if(error != 0) return;
1206 FT_Glyph original_glyph;
1209 error = FT_Get_Glyph(face->glyph, &original_glyph);
1211 if(error != 0) return;
1213 error = FT_Glyph_Copy(original_glyph, &glyph);
1215 FT_Done_Glyph(original_glyph);
1217 if(error != 0) return;
1219 // If the individual characters are rotated (as distinct from string
1220 // rotation), then apply that extra rotation here. This is equivalent
1222 // glTranslate(x_center,y_center);
1224 // glTranslate(-x_center,-y_center);
1225 // which is used for the polygonal styles. The deal with the raster
1226 // styles is that you must retain the advance from the string rotation
1227 // so that the glyphs are laid out properly. So, we make a copy of
1228 // the string rotated glyph, and then rotate that and add back an
1229 // additional offset to (in effect) restore the proper origin and
1230 // advance of the glyph.
1232 if(character_rotation_z_ != 0.)
1234 FT_Matrix rotation_matrix;
1237 FT_Vector_Unit(&sinus, (FT_Angle)(character_rotation_z_ * 0x10000L));
1239 rotation_matrix.xx = sinus.x;
1240 rotation_matrix.xy = -sinus.y;
1241 rotation_matrix.yx = sinus.y;
1242 rotation_matrix.yy = sinus.x;
1244 FT_Vector original_offset, rotation_offset;
1246 original_offset.x = (face->glyph->metrics.width / 2 + face->glyph->metrics.horiBearingX)/ 64 * 0x10000L;
1247 original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L);
1249 rotation_offset = original_offset;
1251 FT_Vector_Rotate(&rotation_offset, (FT_Angle)(character_rotation_z_ * 0x10000L));
1253 rotation_offset.x = original_offset.x - rotation_offset.x;
1254 rotation_offset.y = original_offset.y - rotation_offset.y;
1256 rotation_offset.x /= 1024;
1257 rotation_offset.y /= 1024;
1259 error = FT_Glyph_Transform(glyph, &rotation_matrix, &rotation_offset);
1262 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_MONO, 0, 1);
1266 FT_Done_Glyph(glyph);
1270 FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph;
1272 // Evidently, in FreeType2, you can only get "upside-down" bitmaps and
1273 // OpenGL won't invert a bitmap with PixelZoom, so we have to invert the
1274 // glyph's bitmap ourselves.
1276 GLubyte* inverted_bitmap = invertBitmap(bitmap_glyph->bitmap);
1278 glBitmap(bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows,
1279 (GLfloat) -bitmap_glyph->left,
1280 (GLfloat) (bitmap_glyph->bitmap.rows - bitmap_glyph->top),
1281 face->glyph->advance.x / 64.0f,
1282 face->glyph->advance.y / 64.0f,
1285 FT_Done_Glyph(glyph);
1287 delete[] inverted_bitmap;
1290 } // close OGLFT namespace