Path: blob/master/libmupen64plus/mupen64plus-core/src/osd/OGLFT.cpp
2 views
/*1* OGLFT: A library for drawing text with OpenGL using the FreeType library2* Copyright (C) 2002 lignum Computing, Inc. <[email protected]>3* $Id: OGLFT.cpp,v 1.11 2003/10/01 14:21:18 allen Exp $4*5* This library is free software; you can redistribute it and/or6* modify it under the terms of the GNU Lesser General Public7* License as published by the Free Software Foundation; either8* version 2.1 of the License, or (at your option) any later version.9*10* This library is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU13* Lesser General Public License for more details.14*15* You should have received a copy of the GNU Lesser General Public16* License along with this library; if not, write17* Free Software Foundation, Inc.,18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.19*/2021#include <iostream>22#include <iomanip>23#include <string.h>24#include "OGLFT.h"2526int wstrlen(const wchar_t * s)27{28int r = 0;29while (*s++) r++;30return r;31}3233namespace OGLFT34{35FT_Library ft_library;36bool Init_FT(void)37{38FT_Error error = FT_Init_FreeType(&ft_library);39if(error != 0) std::cerr << "[OGLFT] Could not initialize the FreeType library." << std::endl;40return (error == 0);41}42bool Uninit_FT(void)43{44FT_Error error = FT_Done_FreeType(ft_library);45if(error != 0) std::cerr << "[OGLFT] Could not terminate the FreeType library." << std::endl;46return (error == 0);47}4849// Load a new face50Face::Face (const char* filename, float point_size, FT_UInt resolution)51: point_size_(point_size), resolution_(resolution)52{53valid_ = true;54FT_Face ft_face;55FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face);56if(error != 0)57{58valid_ = false;59return;60}6162// As of FreeType 2.1: only a UNICODE charmap is automatically activated.63// If no charmap is activated automatically, just use the first one.64if(ft_face->charmap == 0 && ft_face->num_charmaps > 0) FT_Select_Charmap(ft_face, ft_face->charmaps[0]->encoding);6566faces_.push_back(FaceData(ft_face));6768init();69}7071// Go with a face that the user has already opened.72Face::Face (FT_Face face, float point_size, FT_UInt resolution)73: point_size_(point_size), resolution_(resolution)74{75valid_ = true;7677// As of FreeType 2.1: only a UNICODE charmap is automatically activated.78// If no charmap is activated automatically, just use the first one.79if(face->charmap == 0 && face->num_charmaps > 0) FT_Select_Charmap(face, face->charmaps[0]->encoding);8081faces_.push_back(FaceData(face, false));8283init();84}8586// Standard initialization behavior once the font file is opened.87void Face::init (void)88{89// By default, each glyph is compiled into a display list the first90// time it is encountered91compile_mode_ = COMPILE;9293// By default, all drawing is wrapped with push/pop matrix so that the94// MODELVIEW matrix is not modified. If advance_ is set, then subsequent95// drawings follow from the advance of the last glyph rendered.96advance_ = false;9798// Initialize the default colors99foreground_color_[R] = 0.; foreground_color_[G] = 0.; foreground_color_[B] = 0.; foreground_color_[A] = 1.;100background_color_[R] = 1.; background_color_[G] = 1.; background_color_[B] = 1.; background_color_[A] = 0.;101102// The default positioning of the text is at the origin of the first glyph103horizontal_justification_ = ORIGIN;104vertical_justification_ = BASELINE;105106// By default, strings are rendered in their nominal direction107string_rotation_ = 0;108109// setCharacterRotationReference calls the virtual function clearCaches()110// so it is up to a subclass to set the real default111rotation_reference_glyph_ = 0;112rotation_reference_face_ = 0;113rotation_offset_y_ = 0.;114}115116Face::~Face (void)117{118for(unsigned int i=0; i<faces_.size(); i++)119if(faces_[i].free_on_exit_)120FT_Done_Face(faces_[i].face_);121}122123// Add another Face to select characters from124bool Face::addAuxiliaryFace (const char* filename)125{126FT_Face ft_face;127128FT_Error error = FT_New_Face(ft_library, filename, 0, &ft_face);129130if(error != 0) return false;131132faces_.push_back(FaceData(ft_face));133setCharSize();134135return true;136}137138// Add another Face to select characters from139bool Face::addAuxiliaryFace (FT_Face face)140{141faces_.push_back(FaceData(face, false));142143setCharSize();144145return true;146}147148// Note: Changing the point size also clears the display list cache149void Face::setPointSize (float point_size)150{151if(point_size != point_size_)152{153point_size_ = point_size;154clearCaches();155setCharSize();156}157}158159// Note: Changing the resolution also clears the display list cache160void Face::setResolution (FT_UInt resolution)161{162if(resolution != resolution_)163{164resolution_ = resolution;165clearCaches();166setCharSize();167}168}169170// Note: Changing the background color also clears the display list cache.171void Face::setBackgroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)172{173if(background_color_[R] != red||background_color_[G] != green||background_color_[B] != blue||background_color_[A] != alpha)174{175background_color_[R] = red;176background_color_[G] = green;177background_color_[B] = blue;178background_color_[A] = alpha;179}180}181182// Note: Changing the foreground color also clears the display list cache.183void Face::setForegroundColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)184{185if(foreground_color_[R] != red||foreground_color_[G] != green||foreground_color_[B] != blue||foreground_color_[A] != alpha)186{187foreground_color_[R] = red;188foreground_color_[G] = green;189foreground_color_[B] = blue;190foreground_color_[A] = alpha;191}192}193194// Note: Changing the foreground color also clears the display list cache.195void Face::setForegroundColor (const GLfloat foreground_color[4])196{197foreground_color_[R] = foreground_color[R];198foreground_color_[G] = foreground_color[G];199foreground_color_[B] = foreground_color[B];200foreground_color_[A] = foreground_color[A];201}202203// Note: Changing the background color also clears the display list cache.204void Face::setBackgroundColor (const GLfloat background_color[4])205{206background_color_[R] = background_color[R];207background_color_[G] = background_color[G];208background_color_[B] = background_color[B];209background_color_[A] = background_color[A];210}211212// Note: Changing the string rotation angle clears the display list cache213void Face::setStringRotation (GLfloat string_rotation)214{215if(string_rotation != string_rotation_)216{217string_rotation_ = string_rotation;218219clearCaches();220221// Note that this affects ALL glyphs accessed through222// the Face, both the vector and the raster glyphs. Very nice!223if (string_rotation_ != 0)224{225float angle;226if (string_rotation_<0)227angle = 360.0f - fmod(fabs(string_rotation_), 360.f);228else229angle = fmod(string_rotation_, 360.f);230231FT_Matrix rotation_matrix;232FT_Vector sinus;233234FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));235236rotation_matrix.xx = sinus.x;237rotation_matrix.xy = -sinus.y;238rotation_matrix.yx = sinus.y;239rotation_matrix.yy = sinus.x;240241for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, &rotation_matrix, 0);242}243else for(unsigned int i=0; i<faces_.size(); i++) FT_Set_Transform(faces_[i].face_, 0, 0);244}245}246247// Note: Changing the rotation reference character clears the display list cache.248void Face::setCharacterRotationReference (unsigned char c)249{250unsigned int f;251FT_UInt glyph_index = 0;252253for(f=0; f<faces_.size(); f++)254{255glyph_index = FT_Get_Char_Index(faces_[f].face_, c);256if(glyph_index != 0) break;257}258259if(f<faces_.size() && glyph_index != rotation_reference_glyph_)260{261FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);262263if(error != 0) return;264265rotation_reference_glyph_ = glyph_index;266rotation_reference_face_ = faces_[f].face_;267setRotationOffset();268269clearCaches();270}271}272273BBox Face::measure (const char* s)274{275BBox bbox;276char c;277278if((c = *s++) != 0)279{280bbox = measure((unsigned char)c);281282for(c = *s; c != 0; c = *++s)283{284BBox char_bbox = measure((unsigned char)c);285bbox += char_bbox;286}287}288// make sure the origin is at 0,0289if (bbox.x_min_ != 0)290{291bbox.x_max_ -= bbox.x_min_;292bbox.x_min_ = 0;293}294if (bbox.y_min_ != 0)295{296bbox.y_max_ -= bbox.y_min_;297bbox.y_min_ = 0;298}299300return bbox;301}302303BBox Face::measureRaw (const char* s)304{305BBox bbox;306307for(char c = *s; c != 0; c = *++s)308{309BBox char_bbox;310311unsigned int f;312FT_UInt glyph_index = 0;313314for(f=0; f<faces_.size(); f++)315{316glyph_index = FT_Get_Char_Index(faces_[f].face_, c);317if(glyph_index != 0) break;318}319320if(glyph_index == 0) continue;321322FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);323if(error != 0) continue;324325FT_Glyph glyph;326error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);327if(error != 0) continue;328329FT_BBox ft_bbox;330FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);331332FT_Done_Glyph(glyph);333334char_bbox = ft_bbox;335char_bbox.advance_ = faces_[f].face_->glyph->advance;336337bbox += char_bbox;338}339340return bbox;341}342343BBox Face::measure (const wchar_t* s)344{345BBox bbox;346int i;347348if(wstrlen(s) > 0)349{350bbox = measure(s[0]);351for(i = 1; i < wstrlen(s); i++)352{353BBox char_bbox = measure(s[i]);354bbox += char_bbox;355}356}357// make sure the origin is at 0,0358if (bbox.x_min_ != 0)359{360bbox.x_max_ -= bbox.x_min_;361bbox.x_min_ = 0;362}363if (bbox.y_min_ != 0)364{365bbox.y_max_ -= bbox.y_min_;366bbox.y_min_ = 0;367}368return bbox;369}370371BBox Face::measure (const wchar_t* format, double number)372{373return measure(format, number);374}375376BBox Face::measureRaw (const wchar_t* s)377{378BBox bbox;379int i;380381for(i = 0; i < wstrlen(s); i++)382{383BBox char_bbox;384385unsigned int f;386FT_UInt glyph_index = 0;387388for(f=0; f<faces_.size(); f++)389{390glyph_index = FT_Get_Char_Index(faces_[f].face_, s[i]);391if(glyph_index != 0) break;392}393394if(glyph_index == 0)395{396continue;397}398399FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);400if(error != 0) continue;401402FT_Glyph glyph;403error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);404if(error != 0) continue;405406FT_BBox ft_bbox;407FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);408409FT_Done_Glyph(glyph);410411char_bbox = ft_bbox;412char_bbox.advance_ = faces_[f].face_->glyph->advance;413414bbox += char_bbox;415}416417return bbox;418}419420// Measure the bounding box as if the (latin1) string were not rotated421BBox Face::measure_nominal (const char* s)422{423if(string_rotation_ == 0.) return measure(s);424425for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, 0, 0);426427BBox bbox = measure(s);428429float angle;430if(string_rotation_<0.)431angle = 360.0f - fmod(fabs(string_rotation_), 360.f);432else433angle = fmod(string_rotation_, 360.f);434435FT_Matrix rotation_matrix;436FT_Vector sinus;437438FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));439440rotation_matrix.xx = sinus.x;441rotation_matrix.xy = -sinus.y;442rotation_matrix.yx = sinus.y;443rotation_matrix.yy = sinus.x;444445for(unsigned int f=0; f<faces_.size(); f++) FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);446447return bbox;448}449450// Measure the bounding box as if the (UNICODE) string were not rotated451BBox Face::measure_nominal (const wchar_t* s)452{453if(string_rotation_ == 0.)return measure(s);454455for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, 0, 0);456457BBox bbox = measure(s);458459float angle;460if(string_rotation_<0.0)461angle = 360.0f - fmod(fabs(string_rotation_), 360.f);462else463angle = fmod(string_rotation_, 360.f);464465FT_Matrix rotation_matrix;466FT_Vector sinus;467468FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));469470rotation_matrix.xx = sinus.x;471rotation_matrix.xy = -sinus.y;472rotation_matrix.yx = sinus.y;473rotation_matrix.yy = sinus.x;474475for(unsigned int f=0; f<faces_.size(); f++)FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);476477return bbox;478}479480// Compile a (latin1) character glyph into a display list and cache481// it for later482483GLuint Face::compile (unsigned char c)484{485// See if we've done it already486GDLCI fgi = glyph_dlists_.find(c);487488if(fgi != glyph_dlists_.end())return fgi->second;489490unsigned int f;491FT_UInt glyph_index = 0;492493for(f=0; f<faces_.size(); f++)494{495glyph_index = FT_Get_Char_Index(faces_[f].face_, c);496if(glyph_index != 0) break;497}498499if(glyph_index == 0)return 0;500501GLuint dlist = compileGlyph(faces_[f].face_, glyph_index);502glyph_dlists_[ c ] = dlist;503504return dlist;505}506507508// Compile a (UNICODE) character glyph into a display list and cache509// it for later510GLuint Face::compile (const wchar_t c)511{512// See if we've done it already513GDLCI fgi = glyph_dlists_.find(c);514515if(fgi != glyph_dlists_.end())return fgi->second;516517unsigned int f;518FT_UInt glyph_index = 0;519520for(f=0; f<faces_.size(); f++)521{522glyph_index = FT_Get_Char_Index(faces_[f].face_, c);523if(glyph_index != 0) break;524}525526if(glyph_index == 0)return 0;527528GLuint dlist = compileGlyph(faces_[f].face_, glyph_index);529530glyph_dlists_[ c ] = dlist;531532return dlist;533}534535// Assume the MODELVIEW matrix is already set and draw the (latin1)536// string. Note: this routine now ignores almost all settings:537// including the position (both modelview and raster), color,538// justification and advance settings. Consider this to be the raw539// drawing routine for which you are responsible for most of the540// setup.541void Face::draw (const char* s)542{543DLCI character_display_list = character_display_lists_.begin();544545for(char c = *s; c != 0; c = *++s)546{547if(character_display_list != character_display_lists_.end())548{549glCallList(*character_display_list);550character_display_list++;551}552draw((unsigned char)c);553}554}555556// Assume the MODELVIEW matrix is already set and draw the (UNICODE)557// string. Note: this routine now ignores almost all settings:558// including the position (both modelview and raster), color,559// justification and advance settings. Consider this to be the raw560// drawing routine for which you are responsible for most of the561// setup.562void Face::draw (const wchar_t* s)563{564DLCI character_display_list = character_display_lists_.begin();565int i;566567for(i = 0; i < wstrlen(s); i++)568{569if(character_display_list != character_display_lists_.end())570{571glCallList(*character_display_list);572character_display_list++;573}574draw(s[i]);575}576}577578// Assume the MODELVIEW matrix is already setup and draw the579// (latin1) character.580void Face::draw (unsigned char c)581{582// See if we've done it already583GDLCI fgi = glyph_dlists_.find(c);584585if(fgi != glyph_dlists_.end())586{587glCallList(fgi->second);588return;589}590591unsigned int f;592FT_UInt glyph_index = 0;593594for(f=0; f<faces_.size(); f++)595{596glyph_index = FT_Get_Char_Index(faces_[f].face_, c);597if(glyph_index != 0) break;598}599600if(glyph_index == 0) return;601602if(compile_mode_ == COMPILE)603{604GLuint dlist = compile(c);605glCallList(dlist);606}607else renderGlyph(faces_[f].face_, glyph_index);608}609610// Assume the MODELVIEW matrix is already setup and draw the611// (UNICODE) character.612613void Face::draw (const wchar_t c)614{615// See if we've done it already616GDLCI fgi = glyph_dlists_.find(c);617618if(fgi != glyph_dlists_.end())619{620glCallList(fgi->second);621return;622}623624unsigned int f;625FT_UInt glyph_index = 0;626627for(f=0; f<faces_.size(); f++)628{629glyph_index = FT_Get_Char_Index(faces_[f].face_, c);630if(glyph_index != 0) break;631}632633if(glyph_index == 0) return;634635if(compile_mode_ == COMPILE)636{637GLuint dlist = compile(c);638glCallList(dlist);639}640else renderGlyph(faces_[f].face_, glyph_index);641}642643// Draw the (latin1) character at the given position. The MODELVIEW644// matrix is modified by the glyph advance.645void Face::draw (GLfloat x, GLfloat y, unsigned char c)646{647glTranslatef(x, y, 0.);648649glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);650651glRasterPos3i(0, 0, 0);652653draw(c);654}655656// Draw the (latin1) character at the given position. The MODELVIEW657// matrix is modified by the glyph advance.658void Face::draw (GLfloat x, GLfloat y, GLfloat z, unsigned char c)659{660glTranslatef(x, y, z);661662glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);663664glRasterPos3i(0, 0, 0);665666draw(c);667}668669// Draw the (UNICODE) character at the given position. The MODELVIEW670// matrix is modified by the glyph advance.671void Face::draw (GLfloat x, GLfloat y, wchar_t c)672{673glTranslatef(x, y, 0.);674675glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B],676foreground_color_[A]);677678glRasterPos3i(0, 0, 0);679680draw(c);681}682683// Draw the (UNICODE) character at the given position. The MODELVIEW684// matrix is modified by the glyph advance.685void Face::draw (GLfloat x, GLfloat y, GLfloat z, wchar_t c)686{687glTranslatef(x, y, z);688689glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);690691glRasterPos3i(0, 0, 0);692693draw(c);694}695696697// Draw the (latin1) string at the given position.698void Face::draw (GLfloat x, GLfloat y, const char* s, float *sizebox)699{700// sizebox is xmin,ymin, xmax,ymax701if(!advance_) glPushMatrix();702703if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE)704{705glPushMatrix();706707GLfloat dx = 0, dy = 0;708709switch (horizontal_justification_)710{711case LEFT: dx = -sizebox[0] + 1; break;712case CENTER: dx = -(sizebox[0] + sizebox[2])/ 2.0f; break;713case RIGHT: dx = -sizebox[2] - 1; break;714default: break;715}716switch (vertical_justification_)717{718case BOTTOM: dy = -sizebox[1] + 1; break;719case MIDDLE: dy = -(sizebox[1] + sizebox[3])/ 2.0f; break;720case TOP: dy = -sizebox[3] - 1; break;721default: break;722}723724// There is probably a less expensive way to compute this725glRotatef(string_rotation_, 0., 0., 1.);726glTranslatef(dx, dy, 0);727glRotatef(-string_rotation_, 0., 0., 1.);728}729730glTranslatef(x, y, 0.);731732glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);733734glRasterPos3i(0, 0, 0);735736draw(s);737738if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix();739740if(!advance_) glPopMatrix();741}742743// Draw the (latin1) string at the given position.744void Face::draw (GLfloat x, GLfloat y, GLfloat z, const char* s)745{746if(!advance_) glPushMatrix();747748if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE)749{750glPushMatrix();751752BBox bbox = measure_nominal(s);753754GLfloat dx = 0, dy = 0;755756switch (horizontal_justification_)757{758case LEFT: dx = -bbox.x_min_; break;759case CENTER: dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break;760case RIGHT: dx = -bbox.x_max_; break;761default: break;762}763switch (vertical_justification_)764{765case BOTTOM: dy = -bbox.y_min_; break;766case MIDDLE: dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break;767case TOP: dy = -bbox.y_max_; break;768default: break;769}770771// There is probably a less expensive way to compute this772glRotatef(string_rotation_, 0., 0., 1.);773glTranslatef(dx, dy, 0);774glRotatef(-string_rotation_, 0., 0., 1.);775}776777glTranslatef(x, y, z);778779glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);780781glRasterPos3i(0, 0, 0);782783draw(s);784785if(horizontal_justification_ != ORIGIN || vertical_justification_ != BASELINE) glPopMatrix();786787if(!advance_) glPopMatrix();788}789790// Draw the (UNICODE) string at the given position.791void Face::draw (GLfloat x, GLfloat y, const wchar_t* s)792{793if(!advance_)794glPushMatrix();795796if(horizontal_justification_!=ORIGIN||vertical_justification_!=BASELINE)797{798glPushMatrix();799800BBox bbox = measure_nominal(s);801802GLfloat dx = 0, dy = 0;803804switch (horizontal_justification_)805{806case LEFT:807dx = -bbox.x_min_; break;808case CENTER:809dx = -(bbox.x_min_ + bbox.x_max_)/ 2.0f; break;810case RIGHT:811dx = -bbox.x_max_; break;812default:813break;814}815switch (vertical_justification_)816{817case BOTTOM:818dy = -bbox.y_min_; break;819case MIDDLE:820dy = -(bbox.y_min_ + bbox.y_max_)/ 2.0f; break;821case TOP:822dy = -bbox.y_max_; break;823default:824break;825}826827// There is probably a less expensive way to compute this828glRotatef(string_rotation_, 0., 0., 1.);829glTranslatef(dx, dy, 0);830glRotatef(-string_rotation_, 0., 0., 1.);831}832833glTranslatef(x, y, 0.);834835glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B],836foreground_color_[A]);837838glRasterPos3i(0, 0, 0);839840draw(s);841842if(horizontal_justification_ != ORIGIN ||843vertical_justification_ != BASELINE)844glPopMatrix();845846if(!advance_)847glPopMatrix();848}849850// Draw the (UNICODE) string at the given position.851void Face::draw (GLfloat x, GLfloat y, GLfloat z, const wchar_t* s)852{853if(!advance_) glPushMatrix();854855if(horizontal_justification_!= ORIGIN||vertical_justification_!= BASELINE)856{857glPushMatrix();858859// In 3D, we need to exert more care in the computation of the860// bounding box of the text. NOTE: Needs to be fixed up for861// polygonal faces, too...862863BBox bbox;864// Code from measure_nominal, but changed to use measureRaw instead865if(string_rotation_ == 0.) bbox = measureRaw(s);866else867{868for(unsigned int f=0; f<faces_.size(); f++)869FT_Set_Transform(faces_[f].face_, 0, 0);870871bbox = measureRaw(s);872873float angle;874if(string_rotation_<0.0)875angle = 360.0f - fmod(fabs(string_rotation_), 360.f);876else877angle = fmod(string_rotation_, 360.f);878879FT_Matrix rotation_matrix;880FT_Vector sinus;881882FT_Vector_Unit(&sinus, (FT_Angle)(angle * 0x10000L));883884rotation_matrix.xx = sinus.x;885rotation_matrix.xy = -sinus.y;886rotation_matrix.yx = sinus.y;887rotation_matrix.yy = sinus.x;888889for(unsigned int f=0; f<faces_.size(); f++)890FT_Set_Transform(faces_[f].face_, &rotation_matrix, 0);891}892893GLfloat dx = 0, dy = 0;894895switch (horizontal_justification_)896{897case LEFT:898dx = bbox.x_min_; break;899case CENTER:900dx = (bbox.x_min_ + bbox.x_max_)/ 2; break;901case RIGHT:902dx = bbox.x_max_; break;903default:904break;905}906switch (vertical_justification_)907{908case BOTTOM:909dy = bbox.y_min_; break;910case MIDDLE:911dy = (bbox.y_min_ + bbox.y_max_)/2; break;912case TOP:913dy = bbox.y_max_; break;914default:915break;916}917918GLint viewport[4];919GLdouble modelview[16], projection[16];920921glGetIntegerv(GL_VIEWPORT, viewport);922glGetDoublev(GL_MODELVIEW_MATRIX, modelview);923glGetDoublev(GL_PROJECTION_MATRIX, projection);924925GLdouble x0, y0, z0;926gluUnProject(0, 0, 0, modelview, projection, viewport, &x0, &y0, &z0);927928GLdouble dx_m, dy_m, dz_m;929gluUnProject(dx, dy, 0., modelview, projection, viewport,&dx_m,&dy_m,&dz_m);930931glTranslated(x0-dx_m, y0-dy_m, z0-dz_m);932}933934glTranslatef(x, y, z);935glColor4f(foreground_color_[R], foreground_color_[G], foreground_color_[B], foreground_color_[A]);936glRasterPos3i(0, 0, 0);937draw(s);938939if(horizontal_justification_!=ORIGIN||vertical_justification_!= BASELINE)940glPopMatrix();941942if(!advance_)943glPopMatrix();944}945946Raster::Raster (const char* filename, float point_size, FT_UInt resolution)947: Face(filename, point_size, resolution)948{949if(!isValid()) return;950951init();952}953954Raster::Raster (FT_Face face, float point_size, FT_UInt resolution)955: Face(face, point_size, resolution)956{957init();958}959960void Raster::init (void)961{962character_rotation_z_ = 0;963setCharSize();964setCharacterRotationReference('o');965}966967Raster::~Raster (void)968{969clearCaches();970}971972void Raster::setCharacterRotationZ (GLfloat character_rotation_z)973{974if(character_rotation_z != character_rotation_z_)975{976character_rotation_z_ = character_rotation_z;977clearCaches();978}979}980981double Raster::height (void)const982{983if(faces_[0].face_->height > 0) return faces_[0].face_->height / 64.;984else return faces_[0].face_->size->metrics.y_ppem;985}986987BBox Raster::measure (unsigned char c)988{989BBox bbox;990991// For starters, just get the unscaled glyph bounding box992unsigned int f;993FT_UInt glyph_index = 0;994995for(f=0; f<faces_.size(); f++)996{997glyph_index = FT_Get_Char_Index(faces_[f].face_, c);998if(glyph_index != 0) break;999}10001001if(glyph_index == 0) return bbox;10021003FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index, FT_LOAD_DEFAULT);1004if(error != 0) return bbox;10051006FT_Glyph glyph;1007error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);1008if(error != 0) return bbox;10091010FT_BBox ft_bbox;1011FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);10121013FT_Done_Glyph(glyph);10141015bbox = ft_bbox;1016bbox.advance_ = faces_[f].face_->glyph->advance;10171018// In order to be accurate regarding the placement of text not1019// aligned at the glyph's origin (CENTER/MIDDLE), the bounding box1020// of the raster format has to be projected back into the1021// view's coordinates1022GLint viewport[4];1023GLdouble modelview[16], projection[16];10241025glGetIntegerv(GL_VIEWPORT, viewport);1026glGetDoublev(GL_MODELVIEW_MATRIX, modelview);1027glGetDoublev(GL_PROJECTION_MATRIX, projection);10281029// Well, first we have to get the Origin, since that is the basis1030// of the bounding box1031GLdouble x0, y0, z0;1032gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0);10331034GLdouble x, y, z;1035gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z);1036bbox.x_min_ = (float) (x - x0);1037bbox.y_min_ = (float) (y - y0);10381039gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z);1040bbox.x_max_ = (float) (x - x0);1041bbox.y_max_ = (float) (y - y0);10421043gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z);1044bbox.advance_.dx_ = (float) (x - x0);1045bbox.advance_.dy_ = (float) (y - y0);10461047return bbox;1048}10491050BBox Raster::measure (wchar_t c)1051{1052BBox bbox;10531054// For starters, just get the unscaled glyph bounding box1055unsigned int f;1056FT_UInt glyph_index = 0;10571058for(f=0; f<faces_.size(); f++)1059{1060glyph_index = FT_Get_Char_Index(faces_[f].face_, c);1061if(glyph_index != 0) break;1062}10631064if(glyph_index == 0) return bbox;10651066FT_Error error = FT_Load_Glyph(faces_[f].face_, glyph_index,1067FT_LOAD_DEFAULT);1068if(error != 0) return bbox;10691070FT_Glyph glyph;1071error = FT_Get_Glyph(faces_[f].face_->glyph, &glyph);1072if(error != 0) return bbox;10731074FT_BBox ft_bbox;1075FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &ft_bbox);10761077FT_Done_Glyph(glyph);10781079bbox = ft_bbox;1080bbox.advance_ = faces_[f].face_->glyph->advance;10811082// In order to be accurate regarding the placement of text not1083// aligned at the glyph's origin (CENTER/MIDDLE), the bounding box1084// of the raster format has to be projected back into the1085// view's coordinates1086GLint viewport[4];1087GLdouble modelview[16], projection[16];10881089glGetIntegerv(GL_VIEWPORT, viewport);1090glGetDoublev(GL_MODELVIEW_MATRIX, modelview);1091glGetDoublev(GL_PROJECTION_MATRIX, projection);10921093// Well, first we have to get the Origin, since that is the basis1094// of the bounding box1095GLdouble x0, y0, z0;1096gluUnProject(0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0);10971098GLdouble x, y, z;1099gluUnProject(bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, &x, &y, &z);1100bbox.x_min_ = (float) (x - x0);1101bbox.y_min_ = (float) (y - y0);11021103gluUnProject(bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, &x, &y, &z);1104bbox.x_max_ = (float) (x - x0);1105bbox.y_max_ = (float) (y - y0);11061107gluUnProject(bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, viewport, &x, &y, &z);1108bbox.advance_.dx_ = (float) (x - x0);1109bbox.advance_.dy_ = (float) (y - y0);11101111return bbox;1112}11131114GLuint Raster::compileGlyph (FT_Face face, FT_UInt glyph_index)1115{1116GLuint dlist = glGenLists(1);1117glNewList(dlist, GL_COMPILE);11181119renderGlyph(face, glyph_index);11201121glEndList();11221123return dlist;1124}11251126void Raster::setCharSize (void)1127{1128FT_Error error;1129for(unsigned int i=0; i<faces_.size(); i++)1130{1131error = FT_Set_Char_Size(faces_[i].face_,(FT_F26Dot6)(point_size_ * 64),(FT_F26Dot6)(point_size_ * 64),resolution_,resolution_);1132if(error != 0) return;1133}11341135if(rotation_reference_glyph_ != 0) setRotationOffset();1136}11371138void Raster::setRotationOffset (void)1139{1140FT_Error error = FT_Load_Glyph(rotation_reference_face_, rotation_reference_glyph_, FT_LOAD_RENDER);11411142if(error != 0) return;11431144rotation_offset_y_ = rotation_reference_face_->glyph->bitmap.rows / 2.0f;1145}11461147void Raster::clearCaches (void)1148{1149GDLI fgi = glyph_dlists_.begin();11501151for(; fgi != glyph_dlists_.end(); ++fgi)1152{1153glDeleteLists(fgi->second, 1);1154}11551156glyph_dlists_.clear();1157}11581159Monochrome::Monochrome (const char* filename, float point_size, FT_UInt resolution)1160: Raster(filename, point_size, resolution)1161{1162return;1163}11641165Monochrome::Monochrome (FT_Face face, float point_size, FT_UInt resolution)1166: Raster(face, point_size, resolution)1167{1168return;1169}11701171Monochrome::~Monochrome (void)1172{1173return;1174}11751176GLubyte* Monochrome::invertBitmap (const FT_Bitmap& bitmap)1177{1178// In FreeType 2.0.9, the pitch of bitmaps was rounded up to an1179// even number. In general, this disagrees with what we had been1180// using for OpenGL.1181int width = bitmap.width / 8 + ((bitmap.width & 7)> 0 ? 1 : 0);11821183GLubyte* inverse = new GLubyte[ bitmap.rows * width ];1184GLubyte* inverse_ptr = inverse;11851186for(int r=0; r<bitmap.rows; r++)1187{1188GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * (bitmap.rows - r - 1)];11891190memmove(inverse_ptr, bitmap_ptr, width);1191inverse_ptr += width;1192bitmap_ptr += width;1193}11941195return inverse;1196}11971198void Monochrome::renderGlyph (FT_Face face, FT_UInt glyph_index)1199{1200// Start by retrieving the glyph's data.1201FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);12021203if(error != 0) return;12041205FT_Glyph original_glyph;1206FT_Glyph glyph;12071208error = FT_Get_Glyph(face->glyph, &original_glyph);12091210if(error != 0) return;12111212error = FT_Glyph_Copy(original_glyph, &glyph);12131214FT_Done_Glyph(original_glyph);12151216if(error != 0) return;12171218// If the individual characters are rotated (as distinct from string1219// rotation), then apply that extra rotation here. This is equivalent1220// to the sequence1221// glTranslate(x_center,y_center);1222// glRotate(angle);1223// glTranslate(-x_center,-y_center);1224// which is used for the polygonal styles. The deal with the raster1225// styles is that you must retain the advance from the string rotation1226// so that the glyphs are laid out properly. So, we make a copy of1227// the string rotated glyph, and then rotate that and add back an1228// additional offset to (in effect) restore the proper origin and1229// advance of the glyph.12301231if(character_rotation_z_ != 0.)1232{1233FT_Matrix rotation_matrix;1234FT_Vector sinus;12351236FT_Vector_Unit(&sinus, (FT_Angle)(character_rotation_z_ * 0x10000L));12371238rotation_matrix.xx = sinus.x;1239rotation_matrix.xy = -sinus.y;1240rotation_matrix.yx = sinus.y;1241rotation_matrix.yy = sinus.x;12421243FT_Vector original_offset, rotation_offset;12441245original_offset.x = (face->glyph->metrics.width / 2 + face->glyph->metrics.horiBearingX)/ 64 * 0x10000L;1246original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L);12471248rotation_offset = original_offset;12491250FT_Vector_Rotate(&rotation_offset, (FT_Angle)(character_rotation_z_ * 0x10000L));12511252rotation_offset.x = original_offset.x - rotation_offset.x;1253rotation_offset.y = original_offset.y - rotation_offset.y;12541255rotation_offset.x /= 1024;1256rotation_offset.y /= 1024;12571258error = FT_Glyph_Transform(glyph, &rotation_matrix, &rotation_offset);1259}12601261error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_MONO, 0, 1);12621263if(error != 0)1264{1265FT_Done_Glyph(glyph);1266return;1267}12681269FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph;12701271// Evidently, in FreeType2, you can only get "upside-down" bitmaps and1272// OpenGL won't invert a bitmap with PixelZoom, so we have to invert the1273// glyph's bitmap ourselves.12741275GLubyte* inverted_bitmap = invertBitmap(bitmap_glyph->bitmap);12761277glBitmap(bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows,1278(GLfloat) -bitmap_glyph->left,1279(GLfloat) (bitmap_glyph->bitmap.rows - bitmap_glyph->top),1280face->glyph->advance.x / 64.0f,1281face->glyph->advance.y / 64.0f,1282inverted_bitmap);12831284FT_Done_Glyph(glyph);12851286delete[] inverted_bitmap;1287}12881289} // close OGLFT namespace1290129112921293