From 36cb7a752b5d86d636aebd2379773923e0cbed1a Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Fri, 13 Jun 2014 00:04:57 +0200 Subject: [PATCH] canvas::Text: add heightForWidth method. --- simgear/canvas/elements/CanvasText.cxx | 282 +++++++++++++++++++++++++ simgear/canvas/elements/CanvasText.hxx | 1 + simgear/math/SGRect.hxx | 12 ++ 3 files changed, 295 insertions(+) diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index eb399e16..8beed49c 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -40,6 +40,7 @@ namespace canvas void setFill(const std::string& fill); void setBackgroundColor(const std::string& fill); + int heightForWidth(int w) const; osg::Vec2 handleHit(const osg::Vec2f& pos); virtual osg::BoundingBox computeBound() const; @@ -95,6 +96,281 @@ namespace canvas setBoundingBoxColor( color ); } + //---------------------------------------------------------------------------- + // simplified version of osgText::Text::computeGlyphRepresentation() to + // just calculate the height for a given weight. Glpyh calculations/creating + // is not necessary for this... + int Text::TextOSG::heightForWidth(int w) const + { + if( _text.empty() ) + return 0; + + osgText::Font* activefont = const_cast(getActiveFont()); + if( !activefont ) + return -1; + + float max_width_safe = _maximumWidth; + const_cast(this)->_maximumWidth = w; + + SGRecti bb; + + osg::Vec2 startOfLine_coords(0.0f,0.0f); + osg::Vec2 cursor(startOfLine_coords); + osg::Vec2 local(0.0f,0.0f); + + unsigned int previous_charcode = 0; + unsigned int line_length = 0; + bool horizontal = _layout != VERTICAL; + bool kerning = true; + + float hr = _characterHeight; + float wr = hr / getCharacterAspectRatio(); + + // osg should really care more about const :-/ + osgText::String& text = const_cast(_text); + typedef osgText::String::iterator TextIterator; + + for( TextIterator itr = text.begin(); itr != text.end(); ) + { + // record the start of the current line + TextIterator startOfLine_itr = itr; + + // find the end of the current line. + osg::Vec2 endOfLine_coords(cursor); + TextIterator endOfLine_itr = + const_cast(this)->computeLastCharacterOnLine( + endOfLine_coords, itr, text.end() + ); + + line_length = endOfLine_itr - startOfLine_itr; + + // Set line position to correct alignment. + switch( _layout ) + { + case LEFT_TO_RIGHT: + { + switch( _alignment ) + { + // nothing to be done for these + //case LEFT_TOP: + //case LEFT_CENTER: + //case LEFT_BOTTOM: + //case LEFT_BASE_LINE: + //case LEFT_BOTTOM_BASE_LINE: + // break; + case CENTER_TOP: + case CENTER_CENTER: + case CENTER_BOTTOM: + case CENTER_BASE_LINE: + case CENTER_BOTTOM_BASE_LINE: + cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f; + break; + case RIGHT_TOP: + case RIGHT_CENTER: + case RIGHT_BOTTOM: + case RIGHT_BASE_LINE: + case RIGHT_BOTTOM_BASE_LINE: + cursor.x() = cursor.x() - endOfLine_coords.x(); + break; + default: + break; + } + break; + } + case RIGHT_TO_LEFT: + { + switch( _alignment ) + { + case LEFT_TOP: + case LEFT_CENTER: + case LEFT_BOTTOM: + case LEFT_BASE_LINE: + case LEFT_BOTTOM_BASE_LINE: + cursor.x() = 2 * cursor.x() - endOfLine_coords.x(); + break; + case CENTER_TOP: + case CENTER_CENTER: + case CENTER_BOTTOM: + case CENTER_BASE_LINE: + case CENTER_BOTTOM_BASE_LINE: + cursor.x() = cursor.x() + + (cursor.x() - endOfLine_coords.x()) * 0.5f; + break; + // nothing to be done for these + //case RIGHT_TOP: + //case RIGHT_CENTER: + //case RIGHT_BOTTOM: + //case RIGHT_BASE_LINE: + //case RIGHT_BOTTOM_BASE_LINE: + // break; + default: + break; + } + break; + } + case VERTICAL: + { + switch( _alignment ) + { + // TODO: current behaviour top baselines lined up in both cases - need to implement + // top of characters alignment - Question is this necessary? + // ... otherwise, nothing to be done for these 6 cases + //case LEFT_TOP: + //case CENTER_TOP: + //case RIGHT_TOP: + // break; + //case LEFT_BASE_LINE: + //case CENTER_BASE_LINE: + //case RIGHT_BASE_LINE: + // break; + case LEFT_CENTER: + case CENTER_CENTER: + case RIGHT_CENTER: + cursor.y() = cursor.y() + + (cursor.y() - endOfLine_coords.y()) * 0.5f; + break; + case LEFT_BOTTOM_BASE_LINE: + case CENTER_BOTTOM_BASE_LINE: + case RIGHT_BOTTOM_BASE_LINE: + cursor.y() = cursor.y() - (line_length * _characterHeight); + break; + case LEFT_BOTTOM: + case CENTER_BOTTOM: + case RIGHT_BOTTOM: + cursor.y() = 2 * cursor.y() - endOfLine_coords.y(); + break; + default: + break; + } + break; + } + } + + if( itr != endOfLine_itr ) + { + + for(;itr != endOfLine_itr;++itr) + { + unsigned int charcode = *itr; + + osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode); + if( glyph ) + { + float width = (float) (glyph->getWidth()) * wr; + float height = (float) (glyph->getHeight()) * hr; + + if( _layout == RIGHT_TO_LEFT ) + { + cursor.x() -= glyph->getHorizontalAdvance() * wr; + } + + // adjust cursor position w.r.t any kerning. + if( kerning && previous_charcode ) + { + switch( _layout ) + { + case LEFT_TO_RIGHT: + { + osg::Vec2 delta( activefont->getKerning( previous_charcode, + charcode, + _kerningType ) ); + cursor.x() += delta.x() * wr; + cursor.y() += delta.y() * hr; + break; + } + case RIGHT_TO_LEFT: + { + osg::Vec2 delta( activefont->getKerning( charcode, + previous_charcode, + _kerningType ) ); + cursor.x() -= delta.x() * wr; + cursor.y() -= delta.y() * hr; + break; + } + case VERTICAL: + break; // no kerning when vertical. + } + } + + local = cursor; + osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing() + : glyph->getVerticalBearing() ); + local.x() += bearing.x() * wr; + local.y() += bearing.y() * hr; + + // set up the coords of the quad + osg::Vec2 upLeft = local + osg::Vec2(0.f, height); + osg::Vec2 lowLeft = local; + osg::Vec2 lowRight = local + osg::Vec2(width, 0.f); + osg::Vec2 upRight = local + osg::Vec2(width, height); + + // move the cursor onto the next character. + // also expand bounding box + switch( _layout ) + { + case LEFT_TO_RIGHT: + cursor.x() += glyph->getHorizontalAdvance() * wr; + bb.expandBy(lowLeft.x(), lowLeft.y()); + bb.expandBy(upRight.x(), upRight.y()); + break; + case VERTICAL: + cursor.y() -= glyph->getVerticalAdvance() * hr; + bb.expandBy(upLeft.x(), upLeft.y()); + bb.expandBy(lowRight.x(), lowRight.y()); + break; + case RIGHT_TO_LEFT: + bb.expandBy(lowRight.x(), lowRight.y()); + bb.expandBy(upLeft.x(), upLeft.y()); + break; + } + previous_charcode = charcode; + } + } + + // skip over spaces and return. + while( itr != text.end() && *itr == ' ' ) + ++itr; + if( itr != text.end() && *itr == '\n' ) + ++itr; + } + else + { + ++itr; + } + + // move to new line. + switch( _layout ) + { + case LEFT_TO_RIGHT: + { + startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing); + cursor = startOfLine_coords; + previous_charcode = 0; + break; + } + case RIGHT_TO_LEFT: + { + startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing); + cursor = startOfLine_coords; + previous_charcode = 0; + break; + } + case VERTICAL: + { + startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing) + / getCharacterAspectRatio(); + cursor = startOfLine_coords; + previous_charcode = 0; + break; + } + } + } + + const_cast(this)->_maximumWidth = max_width_safe; + + return bb.height(); + } + //---------------------------------------------------------------------------- osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos) { @@ -369,6 +645,12 @@ namespace canvas } #endif + //---------------------------------------------------------------------------- + int Text::heightForWidth(int w) const + { + return _text->heightForWidth(w); + } + //---------------------------------------------------------------------------- osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const { diff --git a/simgear/canvas/elements/CanvasText.hxx b/simgear/canvas/elements/CanvasText.hxx index aba96319..57ccd290 100644 --- a/simgear/canvas/elements/CanvasText.hxx +++ b/simgear/canvas/elements/CanvasText.hxx @@ -46,6 +46,7 @@ namespace canvas void setFont(const char* name); void setAlignment(const char* align); + int heightForWidth(int w) const; osg::Vec2 getNearestCursor(const osg::Vec2& pos) const; protected: diff --git a/simgear/math/SGRect.hxx b/simgear/math/SGRect.hxx index da5cdcfe..86dd482c 100644 --- a/simgear/math/SGRect.hxx +++ b/simgear/math/SGRect.hxx @@ -113,6 +113,18 @@ class SGRect void setTop(T t) { _min.y() = t; } void setBottom(T b) { _max.y() = b; } + /** + * Expand rectangle to include the given position + */ + void expandBy(T x, T y) + { + if( x < _min.x() ) _min.x() = x; + if( x > _max.x() ) _max.x() = x; + + if( y < _min.y() ) _min.y() = y; + if( y > _max.y() ) _max.y() = y; + } + /** * Move rect by vector */ -- 2.39.5