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