1 ///@file 2 /// A text on the Canvas 3 // 4 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> 5 // 6 // This library is free software; you can redistribute it and/or 7 // modify it under the terms of the GNU Library General Public 8 // License as published by the Free Software Foundation; either 9 // version 2 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 // Library General Public License for more details. 15 // 16 // You should have received a copy of the GNU Library General Public 17 // License along with this library; if not, write to the Free Software 18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 20 #include <simgear_config.h> 21 22 #include "CanvasText.hxx" 23 #include <simgear/canvas/Canvas.hxx> 24 #include <simgear/canvas/CanvasSystemAdapter.hxx> 25 #include <simgear/scene/util/parse_color.hxx> 26 #include <osg/Version> 27 #include <osgDB/Registry> 28 #include <osgText/Text> 29 30 namespace simgear 31 { 32 namespace canvas 33 { 34 class Text::TextOSG: 35 public osgText::Text 36 { 37 public: 38 TextOSG(canvas::Text* text); 39 40 void setFontResolution(int res); 41 void setCharacterAspect(float aspect); 42 void setLineHeight(float factor); 43 void setFill(const std::string& fill); 44 void setStroke(const std::string& color); 45 void setBackgroundColor(const std::string& fill); 46 47 float lineHeight() const; 48 49 /// Get the number of lines 50 size_t lineCount() const; 51 52 /// Get line @a i 53 TextLine lineAt(size_t i) const; 54 55 /// Get nearest line to given y-coordinate 56 #if OSG_VERSION_LESS_THAN(3,6,5) 57 TextLine nearestLine(float pos_y) const; 58 SGVec2i sizeForWidth(int w) const; 59 #else 60 TextLine nearestLine(float pos_y); 61 SGVec2i sizeForWidth(int w); 62 #endif 63 64 #if OSG_VERSION_LESS_THAN(3,3,2) 65 osg::BoundingBox computeBound() const override; 66 #else 67 osg::BoundingBox computeBoundingBox() const override; 68 #endif 69 70 protected: 71 friend class TextLine; 72 73 canvas::Text *_text_element; 74 75 #if OSG_VERSION_LESS_THAN(3,5,6) 76 void computePositions(unsigned int contextID) const override; 77 #else 78 void computePositionsImplementation() override; 79 #endif 80 }; 81 82 class TextLine 83 { 84 public: 85 TextLine(); 86 TextLine(size_t line, Text::TextOSG const* text); 87 88 /// Number of characters on this line 89 size_t size() const; 90 bool empty() const; 91 92 osg::Vec2 cursorPos(size_t i) const; 93 osg::Vec2 nearestCursor(float x) const; 94 95 protected: 96 typedef Text::TextOSG::GlyphQuads GlyphQuads; 97 98 Text::TextOSG const *_text; 99 GlyphQuads const *_quads; 100 101 size_t _line, 102 _begin, 103 _end; 104 }; 105 106 //---------------------------------------------------------------------------- TextLine()107 TextLine::TextLine(): 108 _text(NULL), 109 _quads(NULL), 110 _line(0), 111 _begin(-1), 112 _end(-1) 113 { 114 115 } 116 117 //---------------------------------------------------------------------------- TextLine(size_t line,Text::TextOSG const * text)118 TextLine::TextLine(size_t line, Text::TextOSG const* text): 119 _text(text), 120 _quads(NULL), 121 _line(line), 122 _begin(-1), 123 _end(-1) 124 { 125 if( !text || text->_textureGlyphQuadMap.empty() || !_text->lineCount() ) 126 return; 127 128 _quads = &text->_textureGlyphQuadMap.begin()->second; 129 130 #if OSG_VERSION_LESS_THAN(3,5,6) 131 GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers; 132 GlyphQuads::LineNumbers::const_iterator begin_it = 133 std::lower_bound(line_numbers.begin(), line_numbers.end(), _line); 134 135 if( begin_it == line_numbers.end() || *begin_it != _line ) 136 // empty line or past last line 137 return; 138 139 _begin = begin_it - line_numbers.begin(); 140 _end = std::upper_bound(begin_it, line_numbers.end(), _line) 141 - line_numbers.begin(); 142 #else 143 // TODO: Need 3.5.6 version of this 144 #endif 145 } 146 147 //---------------------------------------------------------------------------- size() const148 size_t TextLine::size() const 149 { 150 return _end - _begin; 151 } 152 153 //---------------------------------------------------------------------------- empty() const154 bool TextLine::empty() const 155 { 156 return _end == _begin; 157 } 158 159 //---------------------------------------------------------------------------- cursorPos(size_t i) const160 osg::Vec2 TextLine::cursorPos(size_t i) const 161 { 162 if( !_quads ) 163 return osg::Vec2(0, 0); 164 165 if( i > size() ) 166 // Position after last character if out of range (TODO better exception?) 167 i = size(); 168 169 osg::Vec2 pos(0, _text->_offset.y() + _line * _text->lineHeight()); 170 171 if( empty() ) 172 return pos; 173 174 #if OSG_VERSION_GREATER_OR_EQUAL(3,5,6) 175 // TODO: need 3.5.6 version of this. 176 #else 177 #if OSG_VERSION_LESS_THAN(3,3,5) 178 GlyphQuads::Coords2 const& coords = _quads->_coords; 179 #else 180 GlyphQuads::Coords2 refCoords = _quads->_coords; 181 GlyphQuads::Coords2::element_type &coords = *refCoords.get(); 182 #endif 183 184 size_t global_i = _begin + i; 185 186 if (global_i == _begin) 187 // before first character of line 188 pos.x() = coords[_begin * 4].x(); 189 else if (global_i == _end) 190 // After Last character of line 191 pos.x() = coords[(_end - 1) * 4 + 2].x(); 192 else 193 { 194 float prev_l = coords[(global_i - 1) * 4].x(), 195 prev_r = coords[(global_i - 1) * 4 + 2].x(), 196 cur_l = coords[global_i * 4].x(); 197 198 if (prev_l == prev_r) 199 // If previous character width is zero set to begin of next character 200 // (Happens eg. with spaces) 201 pos.x() = cur_l; 202 else 203 // position at center between characters 204 pos.x() = 0.5 * (prev_r + cur_l); 205 } 206 #endif 207 208 return pos; 209 } 210 211 //---------------------------------------------------------------------------- nearestCursor(float x) const212 osg::Vec2 TextLine::nearestCursor(float x) const 213 { 214 if (empty()) 215 return cursorPos(0); 216 217 #if OSG_VERSION_GREATER_OR_EQUAL(3,5,6) 218 // TODO: need 3.5.7 version of this. 219 return cursorPos(0); 220 #else 221 #if OSG_VERSION_LESS_THAN(3,3,5) 222 GlyphQuads::Coords2 const& coords = _quads->_coords; 223 #else 224 GlyphQuads::Coords2 refCoords = _quads->_coords; 225 GlyphQuads::Coords2::element_type &coords = *refCoords.get(); 226 #endif 227 228 GlyphQuads::Glyphs const& glyphs = _quads->_glyphs; 229 230 float const HIT_FRACTION = 0.6; 231 float const character_width = _text->getCharacterHeight() 232 * _text->getCharacterAspectRatio(); 233 234 size_t i = _begin; 235 for(; i < _end; ++i) 236 { 237 // Get threshold for mouse x position for setting cursor before or after 238 // current character 239 float threshold = coords[i * 4].x() 240 + HIT_FRACTION * glyphs[i]->getHorizontalAdvance() 241 * character_width; 242 243 if( x <= threshold ) 244 break; 245 } 246 247 return cursorPos(i - _begin); 248 #endif 249 } 250 251 //---------------------------------------------------------------------------- TextOSG(canvas::Text * text)252 Text::TextOSG::TextOSG(canvas::Text* text): 253 _text_element(text) 254 { 255 setBackdropImplementation(NO_DEPTH_BUFFER); 256 } 257 258 //---------------------------------------------------------------------------- setFontResolution(int res)259 void Text::TextOSG::setFontResolution(int res) 260 { 261 TextBase::setFontResolution(res, res); 262 } 263 264 //---------------------------------------------------------------------------- setCharacterAspect(float aspect)265 void Text::TextOSG::setCharacterAspect(float aspect) 266 { 267 setCharacterSize(getCharacterHeight(), aspect); 268 } 269 270 //---------------------------------------------------------------------------- setLineHeight(float factor)271 void Text::TextOSG::setLineHeight(float factor) 272 { 273 setLineSpacing(factor - 1); 274 } 275 276 //---------------------------------------------------------------------------- setFill(const std::string & fill)277 void Text::TextOSG::setFill(const std::string& fill) 278 { 279 // if( fill == "none" ) 280 // TODO No text 281 // else 282 osg::Vec4 color; 283 if( parseColor(fill, color) ) 284 setColor( color ); 285 } 286 287 //---------------------------------------------------------------------------- setStroke(const std::string & stroke)288 void Text::TextOSG::setStroke(const std::string& stroke) 289 { 290 osg::Vec4 color; 291 if( stroke == "none" || !parseColor(stroke, color) ) 292 setBackdropType(NONE); 293 else 294 { 295 setBackdropType(OUTLINE); 296 setBackdropColor(color); 297 } 298 } 299 300 //---------------------------------------------------------------------------- setBackgroundColor(const std::string & fill)301 void Text::TextOSG::setBackgroundColor(const std::string& fill) 302 { 303 osg::Vec4 color; 304 if( parseColor(fill, color) ) 305 setBoundingBoxColor( color ); 306 } 307 308 //---------------------------------------------------------------------------- lineHeight() const309 float Text::TextOSG::lineHeight() const 310 { 311 return (1 + _lineSpacing) * _characterHeight; 312 } 313 314 //---------------------------------------------------------------------------- lineCount() const315 size_t Text::TextOSG::lineCount() const 316 { 317 return _lineCount; 318 } 319 320 //---------------------------------------------------------------------------- lineAt(size_t i) const321 TextLine Text::TextOSG::lineAt(size_t i) const 322 { 323 return TextLine(i, this); 324 } 325 326 //---------------------------------------------------------------------------- 327 #if OSG_VERSION_LESS_THAN(3,6,5) nearestLine(float pos_y) const328 TextLine Text::TextOSG::nearestLine(float pos_y) const 329 { 330 osgText::Font const* font = getActiveFont(); 331 #else 332 TextLine Text::TextOSG::nearestLine(float pos_y) 333 { 334 auto font = getActiveFont(); 335 #endif 336 337 if( !font || lineCount() <= 0 ) 338 return TextLine(0, this); 339 340 float asc = .9f, desc = -.2f; 341 font->getVerticalSize(asc, desc); 342 343 float first_line_y = _offset.y() 344 - (1 + _lineSpacing / 2 + desc) * _characterHeight; 345 346 size_t line_num = std::min<size_t>( 347 std::max<size_t>(0, (pos_y - first_line_y) / lineHeight()), 348 lineCount() - 1 349 ); 350 351 return TextLine(line_num, this); 352 } 353 354 //---------------------------------------------------------------------------- 355 // simplified version of osgText::Text::computeGlyphRepresentation() to 356 // just calculate the size for a given weight. Glpyh calculations/creating 357 // is not necessary for this... 358 #if OSG_VERSION_LESS_THAN(3,6,5) 359 SGVec2i Text::TextOSG::sizeForWidth(int w) const 360 #else 361 SGVec2i Text::TextOSG::sizeForWidth(int w) 362 #endif 363 { 364 if( _text.empty() ) 365 return SGVec2i(0, 0); 366 367 #if OSG_VERSION_LESS_THAN(3,6,5) 368 osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont()); 369 #else 370 auto activefont = getActiveFont(); 371 #endif 372 373 if( !activefont ) 374 return SGVec2i(-1, -1); 375 376 float max_width_safe = _maximumWidth; 377 const_cast<TextOSG*>(this)->_maximumWidth = w; 378 379 SGRecti bb; 380 381 osg::Vec2 startOfLine_coords(0.0f,0.0f); 382 osg::Vec2 cursor(startOfLine_coords); 383 osg::Vec2 local(0.0f,0.0f); 384 385 unsigned int previous_charcode = 0; 386 unsigned int line_length = 0; 387 bool horizontal = _layout != VERTICAL; 388 bool kerning = true; 389 390 float hr = _characterHeight; 391 float wr = hr / getCharacterAspectRatio(); 392 393 // osg should really care more about const :-/ 394 osgText::String& text = const_cast<osgText::String&>(_text); 395 typedef osgText::String::iterator TextIterator; 396 397 for( TextIterator itr = text.begin(); itr != text.end(); ) 398 { 399 // record the start of the current line 400 TextIterator startOfLine_itr = itr; 401 402 // find the end of the current line. 403 osg::Vec2 endOfLine_coords(cursor); 404 TextIterator endOfLine_itr = 405 const_cast<TextOSG*>(this)->computeLastCharacterOnLine( 406 endOfLine_coords, itr, text.end() 407 ); 408 409 line_length = endOfLine_itr - startOfLine_itr; 410 411 // Set line position to correct alignment. 412 switch( _layout ) 413 { 414 case LEFT_TO_RIGHT: 415 { 416 switch( _alignment ) 417 { 418 // nothing to be done for these 419 //case LEFT_TOP: 420 //case LEFT_CENTER: 421 //case LEFT_BOTTOM: 422 //case LEFT_BASE_LINE: 423 //case LEFT_BOTTOM_BASE_LINE: 424 // break; 425 case CENTER_TOP: 426 case CENTER_CENTER: 427 case CENTER_BOTTOM: 428 case CENTER_BASE_LINE: 429 case CENTER_BOTTOM_BASE_LINE: 430 cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f; 431 break; 432 case RIGHT_TOP: 433 case RIGHT_CENTER: 434 case RIGHT_BOTTOM: 435 case RIGHT_BASE_LINE: 436 case RIGHT_BOTTOM_BASE_LINE: 437 cursor.x() = cursor.x() - endOfLine_coords.x(); 438 break; 439 default: 440 break; 441 } 442 break; 443 } 444 case RIGHT_TO_LEFT: 445 { 446 switch( _alignment ) 447 { 448 case LEFT_TOP: 449 case LEFT_CENTER: 450 case LEFT_BOTTOM: 451 case LEFT_BASE_LINE: 452 case LEFT_BOTTOM_BASE_LINE: 453 cursor.x() = 2 * cursor.x() - endOfLine_coords.x(); 454 break; 455 case CENTER_TOP: 456 case CENTER_CENTER: 457 case CENTER_BOTTOM: 458 case CENTER_BASE_LINE: 459 case CENTER_BOTTOM_BASE_LINE: 460 cursor.x() = cursor.x() 461 + (cursor.x() - endOfLine_coords.x()) * 0.5f; 462 break; 463 // nothing to be done for these 464 //case RIGHT_TOP: 465 //case RIGHT_CENTER: 466 //case RIGHT_BOTTOM: 467 //case RIGHT_BASE_LINE: 468 //case RIGHT_BOTTOM_BASE_LINE: 469 // break; 470 default: 471 break; 472 } 473 break; 474 } 475 case VERTICAL: 476 { 477 switch( _alignment ) 478 { 479 // TODO: current behaviour top baselines lined up in both cases - need to implement 480 // top of characters alignment - Question is this necessary? 481 // ... otherwise, nothing to be done for these 6 cases 482 //case LEFT_TOP: 483 //case CENTER_TOP: 484 //case RIGHT_TOP: 485 // break; 486 //case LEFT_BASE_LINE: 487 //case CENTER_BASE_LINE: 488 //case RIGHT_BASE_LINE: 489 // break; 490 case LEFT_CENTER: 491 case CENTER_CENTER: 492 case RIGHT_CENTER: 493 cursor.y() = cursor.y() 494 + (cursor.y() - endOfLine_coords.y()) * 0.5f; 495 break; 496 case LEFT_BOTTOM_BASE_LINE: 497 case CENTER_BOTTOM_BASE_LINE: 498 case RIGHT_BOTTOM_BASE_LINE: 499 cursor.y() = cursor.y() - (line_length * _characterHeight); 500 break; 501 case LEFT_BOTTOM: 502 case CENTER_BOTTOM: 503 case RIGHT_BOTTOM: 504 cursor.y() = 2 * cursor.y() - endOfLine_coords.y(); 505 break; 506 default: 507 break; 508 } 509 break; 510 } 511 } 512 513 if( itr != endOfLine_itr ) 514 { 515 516 for(;itr != endOfLine_itr;++itr) 517 { 518 unsigned int charcode = *itr; 519 520 osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode); 521 if( glyph ) 522 { 523 float width = (float) (glyph->getWidth()) * wr; 524 float height = (float) (glyph->getHeight()) * hr; 525 526 if( _layout == RIGHT_TO_LEFT ) 527 { 528 cursor.x() -= glyph->getHorizontalAdvance() * wr; 529 } 530 531 // adjust cursor position w.r.t any kerning. 532 if( kerning && previous_charcode ) 533 { 534 switch( _layout ) 535 { 536 case LEFT_TO_RIGHT: 537 { 538 #if OSG_VERSION_LESS_THAN(3,5,2) 539 osg::Vec2 delta(activefont->getKerning(previous_charcode, 540 charcode, 541 _kerningType)); 542 #else 543 osg::Vec2 delta(activefont->getKerning(_fontSize, 544 previous_charcode, 545 charcode, 546 _kerningType)); 547 #endif 548 cursor.x() += delta.x() * wr; 549 cursor.y() += delta.y() * hr; 550 break; 551 } 552 case RIGHT_TO_LEFT: 553 { 554 #if OSG_VERSION_LESS_THAN(3,5,2) 555 osg::Vec2 delta(activefont->getKerning(charcode, 556 previous_charcode, 557 _kerningType)); 558 #else 559 osg::Vec2 delta(activefont->getKerning(_fontSize, charcode, 560 previous_charcode, 561 _kerningType)); 562 #endif 563 cursor.x() -= delta.x() * wr; 564 cursor.y() -= delta.y() * hr; 565 break; 566 } 567 case VERTICAL: 568 break; // no kerning when vertical. 569 } 570 } 571 572 local = cursor; 573 osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing() 574 : glyph->getVerticalBearing() ); 575 local.x() += bearing.x() * wr; 576 local.y() += bearing.y() * hr; 577 578 // set up the coords of the quad 579 osg::Vec2 upLeft = local + osg::Vec2(0.f, height); 580 osg::Vec2 lowLeft = local; 581 osg::Vec2 lowRight = local + osg::Vec2(width, 0.f); 582 osg::Vec2 upRight = local + osg::Vec2(width, height); 583 584 // move the cursor onto the next character. 585 // also expand bounding box 586 switch( _layout ) 587 { 588 case LEFT_TO_RIGHT: 589 cursor.x() += glyph->getHorizontalAdvance() * wr; 590 bb.expandBy(lowLeft.x(), lowLeft.y()); 591 bb.expandBy(upRight.x(), upRight.y()); 592 break; 593 case VERTICAL: 594 cursor.y() -= glyph->getVerticalAdvance() * hr; 595 bb.expandBy(upLeft.x(), upLeft.y()); 596 bb.expandBy(lowRight.x(), lowRight.y()); 597 break; 598 case RIGHT_TO_LEFT: 599 bb.expandBy(lowRight.x(), lowRight.y()); 600 bb.expandBy(upLeft.x(), upLeft.y()); 601 break; 602 } 603 previous_charcode = charcode; 604 } 605 } 606 607 // skip over spaces and return. 608 while( itr != text.end() && *itr == ' ' ) 609 ++itr; 610 if( itr != text.end() && *itr == '\n' ) 611 ++itr; 612 } 613 else 614 { 615 ++itr; 616 } 617 618 // move to new line. 619 switch( _layout ) 620 { 621 case LEFT_TO_RIGHT: 622 { 623 startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing); 624 cursor = startOfLine_coords; 625 previous_charcode = 0; 626 break; 627 } 628 case RIGHT_TO_LEFT: 629 { 630 startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing); 631 cursor = startOfLine_coords; 632 previous_charcode = 0; 633 break; 634 } 635 case VERTICAL: 636 { 637 startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing) 638 / getCharacterAspectRatio(); 639 cursor = startOfLine_coords; 640 previous_charcode = 0; 641 break; 642 } 643 } 644 } 645 646 const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe; 647 648 return bb.size(); 649 } 650 651 //---------------------------------------------------------------------------- 652 #if OSG_VERSION_LESS_THAN(3,3,2) 653 osg::BoundingBox Text::TextOSG::computeBound() const 654 #else 655 osg::BoundingBox Text::TextOSG::computeBoundingBox() const 656 #endif 657 { 658 #if OSG_VERSION_LESS_THAN(3,3,2) 659 osg::BoundingBox bb = osgText::Text::computeBound(); 660 #else 661 osg::BoundingBox bb = osgText::Text::computeBoundingBox(); 662 #endif 663 664 #if OSG_VERSION_LESS_THAN(3,1,0) 665 if( bb.valid() ) 666 { 667 // TODO bounding box still doesn't seem always right (eg. with center 668 // horizontal alignment not completely accurate) 669 bb._min.y() += _offset.y(); 670 bb._max.y() += _offset.y(); 671 } 672 #endif 673 674 return bb; 675 } 676 677 #if OSG_VERSION_LESS_THAN(3,5,6) 678 void Text::TextOSG::computePositions(unsigned int contextID) const 679 { 680 if( _textureGlyphQuadMap.empty() || _layout == VERTICAL ) 681 return osgText::Text::computePositions(contextID); 682 683 // TODO check when it can be larger 684 assert( _textureGlyphQuadMap.size() == 1 ); 685 686 const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second; 687 const GlyphQuads::Glyphs& glyphs = quads._glyphs; 688 #if OSG_VERSION_LESS_THAN(3,3,5) 689 GlyphQuads::Coords2 const& coords = quads._coords; 690 #else 691 GlyphQuads::Coords2 refCoords = quads._coords; 692 GlyphQuads::Coords2::element_type &coords = *refCoords.get(); 693 #endif 694 695 const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers; 696 697 float wr = _characterHeight / getCharacterAspectRatio(); 698 699 size_t cur_line = static_cast<size_t>(-1); 700 for(size_t i = 0; i < glyphs.size(); ++i) 701 { 702 // Check horizontal offsets 703 704 bool first_char = cur_line != line_numbers[i]; 705 cur_line = line_numbers[i]; 706 707 bool last_char = (i + 1 == glyphs.size()) 708 || (cur_line != line_numbers[i + 1]); 709 710 if( first_char || last_char ) 711 { 712 // From osg/src/osgText/Text.cpp: 713 // 714 // osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...); 715 // osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...); 716 // osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin, ...); 717 // osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin, ...); 718 719 float left = coords[i * 4].x(), 720 right = coords[i * 4 + 2].x(), 721 width = glyphs[i]->getWidth() * wr; 722 723 // (local + width + fHoriz) - (local - fHoriz) = width + 2*fHoriz | -width 724 float margin = 0.5f * (right - left - width), 725 cursor_x = left + margin 726 - glyphs[i]->getHorizontalBearing().x() * wr; 727 728 if( first_char ) 729 { 730 if( cur_line == 0 || cursor_x < _textBB._min.x() ) 731 _textBB._min.x() = cursor_x; 732 } 733 734 if( last_char ) 735 { 736 float cursor_w = cursor_x + glyphs[i]->getHorizontalAdvance() * wr; 737 738 if( cur_line == 0 || cursor_w > _textBB._max.x() ) 739 _textBB._max.x() = cursor_w; 740 } 741 } 742 } 743 744 return osgText::Text::computePositions(contextID); 745 } 746 747 #else 748 void Text::TextOSG::computePositionsImplementation() 749 { 750 TextBase::computePositionsImplementation(); 751 } 752 #endif 753 //---------------------------------------------------------------------------- 754 755 //---------------------------------------------------------------------------- 756 const std::string Text::TYPE_NAME = "text"; 757 758 //---------------------------------------------------------------------------- 759 void Text::staticInit() 760 { 761 if( isInit<Text>() ) 762 return; 763 764 osg::ref_ptr<TextOSG> Text::*text = &Text::_text; 765 766 addStyle("fill", "color", &TextOSG::setFill, text); 767 addStyle("background", "color", &TextOSG::setBackgroundColor, text); 768 addStyle("stroke", "color", &TextOSG::setStroke, text); 769 addStyle("character-size", 770 "numeric", 771 static_cast< 772 void (TextOSG::*)(float) 773 > (&TextOSG::setCharacterSize), 774 text); 775 addStyle("character-aspect-ratio", 776 "numeric", 777 &TextOSG::setCharacterAspect, text); 778 addStyle("line-height", "numeric", &TextOSG::setLineHeight, text); 779 addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text); 780 addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text); 781 // TEXT = 1 default 782 // BOUNDINGBOX = 2 783 // FILLEDBOUNDINGBOX = 4 784 // ALIGNMENT = 8 785 addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text); 786 addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text); 787 addStyle("font", "", &Text::setFont); 788 addStyle("alignment", "", &Text::setAlignment); 789 addStyle("text", "", &Text::setText, false); 790 791 osgDB::Registry* reg = osgDB::Registry::instance(); 792 if( !reg->getReaderWriterForExtension("ttf") ) 793 SG_LOG(SG_GL, SG_ALERT, "canvas::Text: Missing 'ttf' font reader"); 794 } 795 796 //---------------------------------------------------------------------------- 797 Text::Text( const CanvasWeakPtr& canvas, 798 const SGPropertyNode_ptr& node, 799 const Style& parent_style, 800 ElementWeakPtr parent ): 801 Element(canvas, node, parent_style, parent), 802 _text( new Text::TextOSG(this) ) 803 { 804 staticInit(); 805 806 setDrawable(_text); 807 _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS); 808 _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION); 809 _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS)); 810 811 setupStyle(); 812 } 813 814 //---------------------------------------------------------------------------- 815 Text::~Text() 816 { 817 818 } 819 820 //---------------------------------------------------------------------------- 821 void Text::setText(const char* text) 822 { 823 _text->setText(text, osgText::String::ENCODING_UTF8); 824 } 825 826 //---------------------------------------------------------------------------- 827 void Text::setFont(const char* name) 828 { 829 _text->setFont( Canvas::getSystemAdapter()->getFont(name) ); 830 } 831 832 //---------------------------------------------------------------------------- 833 void Text::setAlignment(const char* align) 834 { 835 const std::string align_string(align); 836 if( 0 ) return; 837 #define ENUM_MAPPING(enum_val, string_val) \ 838 else if( align_string == string_val )\ 839 _text->setAlignment( osgText::Text::enum_val ); 840 #include "text-alignment.hxx" 841 #undef ENUM_MAPPING 842 else 843 { 844 if( !align_string.empty() ) 845 SG_LOG 846 ( 847 SG_GENERAL, 848 SG_WARN, 849 "canvas::Text: unknown alignment '" << align_string << "'" 850 ); 851 _text->setAlignment(osgText::Text::LEFT_BASE_LINE); 852 } 853 } 854 855 //---------------------------------------------------------------------------- 856 #if 0 857 const char* Text::getAlignment() const 858 { 859 switch( _text->getAlignment() ) 860 { 861 #define ENUM_MAPPING(enum_val, string_val) \ 862 case osgText::Text::enum_val:\ 863 return string_val; 864 #include "text-alignment.hxx" 865 #undef ENUM_MAPPING 866 default: 867 return "unknown"; 868 } 869 } 870 #endif 871 872 //---------------------------------------------------------------------------- 873 int Text::heightForWidth(int w) const 874 { 875 return _text->sizeForWidth(w).y(); 876 } 877 878 //---------------------------------------------------------------------------- 879 int Text::maxWidth() const 880 { 881 return _text->sizeForWidth(INT_MAX).x(); 882 } 883 884 //---------------------------------------------------------------------------- 885 size_t Text::lineCount() const 886 { 887 return _text->lineCount(); 888 } 889 890 //---------------------------------------------------------------------------- 891 size_t Text::lineLength(size_t line) const 892 { 893 return _text->lineAt(line).size(); 894 } 895 896 //---------------------------------------------------------------------------- 897 osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const 898 { 899 return _text->nearestLine(pos.y()).nearestCursor(pos.x()); 900 } 901 902 //---------------------------------------------------------------------------- 903 osg::Vec2 Text::getCursorPos(size_t line, size_t character) const 904 { 905 return _text->lineAt(line).cursorPos(character); 906 } 907 908 //---------------------------------------------------------------------------- 909 osg::StateSet* Text::getOrCreateStateSet() 910 { 911 if( !_scene_group.valid() ) 912 return nullptr; 913 914 // Only check for StateSet on Transform, as the text stateset is shared 915 // between all text instances using the same font (texture). 916 return _scene_group->getOrCreateStateSet(); 917 } 918 919 } // namespace canvas 920 } // namespace simgear 921