diff --git a/examples/osgtext/osgtext.cpp b/examples/osgtext/osgtext.cpp index 4ae541636..475cd3c07 100644 --- a/examples/osgtext/osgtext.cpp +++ b/examples/osgtext/osgtext.cpp @@ -76,6 +76,7 @@ osg::Group* createHUDText() // right to left layouts would be used for hebrew or arabic fonts. text->setLayout(osgText::Text::RIGHT_TO_LEFT); + text->setAlignment(osgText::Text::RIGHT_BASE_LINE); text->setText("text->setLayout(osgText::Text::RIGHT_TO_LEFT);"); geode->addDrawable(text); @@ -218,18 +219,22 @@ osg::Group* createHUDText() typedef std::pair AlignmentPair; typedef std::vector AlignmentList; AlignmentList alignmentList; - alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_TOP,"text->setAlignment(osgText::Text::LEFT_TOP);")); - alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_CENTER,"text->setAlignment(osgText::Text::LEFT_CENTER);")); - alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BOTTOM,"text->setAlignment(osgText::Text::LEFT_BOTTOM);")); - alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_TOP,"text->setAlignment(osgText::Text::CENTER_TOP);")); - alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_CENTER,"text->setAlignment(osgText::Text::CENTER_CENTER);")); - alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BOTTOM,"text->setAlignment(osgText::Text::CENTER_BOTTOM);")); - alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_TOP,"text->setAlignment(osgText::Text::RIGHT_TOP);")); - alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_CENTER,"text->setAlignment(osgText::Text::RIGHT_CENTER);")); - alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BOTTOM,"text->setAlignment(osgText::Text::RIGHT_BOTTOM);")); - alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BASE_LINE,"text->setAlignment(osgText::Text::BASE_LINE);")); - alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BASE_LINE,"text->setAlignment(osgText::Text::CENTER_BASE_LINE);")); - alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BASE_LINE,"text->setAlignment(osgText::Text::RIGHT_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_TOP,"text->setAlignment(\nosgText::Text::LEFT_TOP);")); + alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_CENTER,"text->setAlignment(\nosgText::Text::LEFT_CENTER);")); + alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BOTTOM,"text->setAlignment(\nosgText::Text::LEFT_BOTTOM);")); + alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_TOP,"text->setAlignment(\nosgText::Text::CENTER_TOP);")); + alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_CENTER,"text->setAlignment(\nosgText::Text::CENTER_CENTER);")); + alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BOTTOM,"text->setAlignment(\nosgText::Text::CENTER_BOTTOM);")); + alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_TOP,"text->setAlignment(\nosgText::Text::RIGHT_TOP);")); + alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_CENTER,"text->setAlignment(\nosgText::Text::RIGHT_CENTER);")); + alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BOTTOM,"text->setAlignment(\nosgText::Text::RIGHT_BOTTOM);")); + alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BASE_LINE,"text->setAlignment(\nosgText::Text::LEFT_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BASE_LINE,"text->setAlignment(\nosgText::Text::CENTER_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BASE_LINE,"text->setAlignment(\nosgText::Text::RIGHT_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::LEFT_BOTTOM_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::CENTER_BOTTOM_BASE_LINE);")); + alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::RIGHT_BOTTOM_BASE_LINE);")); + osg::Sequence* sequence = new osg::Sequence; { @@ -558,3 +563,4 @@ int main( int argc, char **argv ) return 0; } + diff --git a/include/osgText/Text b/include/osgText/Text index 4c2a1b67f..f8313b122 100644 --- a/include/osgText/Text +++ b/include/osgText/Text @@ -150,6 +150,11 @@ public: LEFT_BASE_LINE, CENTER_BASE_LINE, RIGHT_BASE_LINE, + + LEFT_BOTTOM_BASE_LINE, + CENTER_BOTTOM_BASE_LINE, + RIGHT_BOTTOM_BASE_LINE, + BASE_LINE = LEFT_BASE_LINE /// default. }; @@ -209,6 +214,9 @@ public: KerningType getKerningType() const { return _kerningType; } + /** Get the number of wrapped lines - only valid after computeGlyphRepresentation() has been called, returns -1 otherwise */ + unsigned int getLineCount() { return _lineCount; } + /** Draw the text.*/ virtual void drawImplementation(osg::State& state) const; @@ -293,7 +301,7 @@ protected: Font* getActiveFont(); const Font* getActiveFont() const; - String::iterator computeLastCharacterOnLine(osg::Vec2 cursor, String::iterator first,String::iterator last); + String::iterator computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last); // members which have public access. osg::ref_ptr _font; @@ -313,7 +321,8 @@ protected: Layout _layout; osg::Vec4 _color; unsigned int _drawMode; - KerningType _kerningType; + KerningType _kerningType; + unsigned int _lineCount; // iternal map used for rendering. Set up by the computeGlyphRepresentation() method. mutable TextureGlyphQuadMap _textureGlyphQuadMap; diff --git a/src/osgText/Text.cpp b/src/osgText/Text.cpp index ed853a8fb..2d8a9eb17 100644 --- a/src/osgText/Text.cpp +++ b/src/osgText/Text.cpp @@ -41,7 +41,8 @@ Text::Text(): _layout(LEFT_TO_RIGHT), _color(1.0f,1.0f,1.0f,1.0f), _drawMode(TEXT), - _kerningType(KERNING_DEFAULT) + _kerningType(KERNING_DEFAULT), + _lineCount(0) { setUseDisplayList(false); } @@ -64,7 +65,8 @@ Text::Text(const Text& text,const osg::CopyOp& copyop): _layout(text._layout), _color(text._color), _drawMode(text._drawMode), - _kerningType(text._kerningType) + _kerningType(text._kerningType), + _lineCount(text._lineCount) { computeGlyphRepresentation(); } @@ -259,7 +261,7 @@ const Font* Text::getActiveFont() const return _font.valid() ? _font.get() : DefaultFont::instance(); } -String::iterator Text::computeLastCharacterOnLine(osg::Vec2 cursor, String::iterator first,String::iterator last) +String::iterator Text::computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last) { Font* activefont = getActiveFont(); if (!activefont) return last; @@ -270,23 +272,37 @@ String::iterator Text::computeLastCharacterOnLine(osg::Vec2 cursor, String::iter bool kerning = true; unsigned int previous_charcode = 0; - for(String::iterator itr=first;itr!=last;++itr) + String::iterator lastChar = first; + + std::set deliminatorSet; + deliminatorSet.insert(' '); + deliminatorSet.insert('\n'); + deliminatorSet.insert(':'); + deliminatorSet.insert('/'); + deliminatorSet.insert(','); + deliminatorSet.insert(';'); + deliminatorSet.insert(':'); + deliminatorSet.insert('.'); + + for(bool outOfSpace=false;lastChar!=last;++lastChar) { - unsigned int charcode = *itr; + unsigned int charcode = *lastChar; if (charcode=='\n') { - return itr; + return lastChar; } Font::Glyph* glyph = activefont->getGlyph(charcode); if (glyph) { - float width = (float)(glyph->s()-2*activefont->getGlyphImageMargin()) * wr; - #ifdef TREES_CODE_FOR_MAKING_SPACES_EDITABLE - if (width == 0.0f) width = glyph->getHorizontalAdvance() * wr; - #endif + float width = (float)(glyph->s()-2*activefont->getGlyphImageMargin()) * wr; + //float height = (float)(glyph->t()-2*activefont->getGlyphImageMargin()) * hr; + #ifdef TREES_CODE_FOR_MAKING_SPACES_EDITABLE + if (width == 0.0f) width = glyph->getHorizontalAdvance() * wr; + //if (height == 0.0f) height = glyph->getVerticalAdvance() * hr; + #endif if (_layout==RIGHT_TO_LEFT) { @@ -314,26 +330,28 @@ String::iterator Text::computeLastCharacterOnLine(osg::Vec2 cursor, String::iter } case VERTICAL: break; // no kerning when vertical. - } - } - // check to see if we are still within line if not move to next line. + } // check to see if we are still within line if not move to next line. + } + switch(_layout) { case LEFT_TO_RIGHT: { - if (_maximumWidth>0.0f && cursor.x()+width>_maximumWidth) return itr; + if (_maximumWidth>0.0f && cursor.x()+width>_maximumWidth) outOfSpace=true; break; } case RIGHT_TO_LEFT: { - if (_maximumWidth>0.0f && cursor.x()<-_maximumWidth) return itr; + if (_maximumWidth>0.0f && cursor.x()<-_maximumWidth) outOfSpace=true; break; } case VERTICAL: - if (_maximumHeight>0.0f && cursor.y()<-_maximumHeight) return itr; + if (_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true; break; } + // => word boundary detection & wrapping + if (outOfSpace) break; // move the cursor onto the next character. switch(_layout) @@ -342,12 +360,33 @@ String::iterator Text::computeLastCharacterOnLine(osg::Vec2 cursor, String::iter case VERTICAL: cursor.y() -= glyph->getVerticalAdvance() *hr; break; case RIGHT_TO_LEFT: break; // nop. } + + previous_charcode = charcode; + } } - return last; -} + + // word boundary detection & wrapping + if (lastChar!=last) + { + if (deliminatorSet.count(*lastChar)==0) + { + String::iterator lastValidChar = lastChar; + while (lastValidChar!=first && deliminatorSet.count(*lastValidChar)==0) + { + --lastValidChar; + } + if (first!=lastValidChar) + { + ++lastValidChar; + lastChar = lastValidChar; + } + } + } + return lastChar; +} void Text::computeGlyphRepresentation() { @@ -355,6 +394,7 @@ void Text::computeGlyphRepresentation() if (!activefont) return; _textureGlyphQuadMap.clear(); + _lineCount = 0; if (_text.empty()) { @@ -363,62 +403,140 @@ void Text::computeGlyphRepresentation() return; } - osg::Vec2 startOfLine(0.0f,0.0f); - osg::Vec2 cursor(startOfLine); + 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 linelength = 0; bool horizontal = _layout!=VERTICAL; bool kerning = true; + + unsigned int lineNumber = 0; activefont->setFontResolution(_fontWidth,_fontHeight); float hr = _characterHeight/(float)activefont->getFontHeight(); float wr = hr/_characterAspectRatio; - std::set deliminatorSet; - deliminatorSet.insert(' '); - deliminatorSet.insert('\n'); - deliminatorSet.insert(':'); - deliminatorSet.insert('/'); - deliminatorSet.insert(','); - deliminatorSet.insert(';'); - deliminatorSet.insert(':'); - deliminatorSet.insert('.'); - - unsigned int lineNumber = 0; - for(String::iterator itr=_text.begin(); itr!=_text.end(); ) { + // record the start of the current line + String::iterator startOfLine_itr = itr; // find the end of the current line. - String::iterator endOfLine = computeLastCharacterOnLine(cursor, itr,_text.end()); + osg::Vec2 endOfLine_coords(cursor); + String::iterator endOfLine_itr = computeLastCharacterOnLine(endOfLine_coords, itr,_text.end()); + linelength = endOfLine_itr - startOfLine_itr; - if (itr!=endOfLine) + // Set line position to correct alignment. + switch(_layout) { - if (endOfLine!=_text.end()) + case LEFT_TO_RIGHT: + { + switch(_alignment) { - if (deliminatorSet.count(*endOfLine)==0) - { - String::iterator lastValidChar = endOfLine; - while (lastValidChar!=itr && deliminatorSet.count(*lastValidChar)==0) - { - --lastValidChar; - } - if (itr!=lastValidChar) - { - ++lastValidChar; - endOfLine = lastValidChar; - } - } - } + // 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 aligment - Question is this neccesary? + // ... 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() - (linelength * _characterHeight); + break; + case LEFT_BOTTOM: + case CENTER_BOTTOM: + case RIGHT_BOTTOM: + cursor.y() = 2*cursor.y() - endOfLine_coords.y(); + break; + default: + break; + } + break; + } + } - for(;itr!=endOfLine;++itr) + if (itr!=endOfLine_itr) + { + + for(;itr!=endOfLine_itr;++itr) { - unsigned int charcode = *itr; Font::Glyph* glyph = activefont->getGlyph(charcode); @@ -431,6 +549,7 @@ void Text::computeGlyphRepresentation() if (width == 0.0f) width = glyph->getHorizontalAdvance() * wr; if (height == 0.0f) height = glyph->getVerticalAdvance() * hr; #endif + if (_layout==RIGHT_TO_LEFT) { cursor.x() -= glyph->getHorizontalAdvance() * wr; @@ -461,13 +580,10 @@ void Text::computeGlyphRepresentation() } local = cursor; - - osg::Vec2 bearing(horizontal?glyph->getHorizontalBearing():glyph->getVerticalBearing()); local.x() += bearing.x() * wr; local.y() += bearing.y() * hr; - GlyphQuads& glyphquad = _textureGlyphQuadMap[glyph->getTexture()->getStateSet()]; glyphquad._glyphs.push_back(glyph); @@ -517,23 +633,27 @@ void Text::computeGlyphRepresentation() { case LEFT_TO_RIGHT: { - startOfLine.y() -= _characterHeight; - cursor = startOfLine; + startOfLine_coords.y() -= _characterHeight; + cursor = startOfLine_coords; previous_charcode = 0; + _lineCount++; break; } case RIGHT_TO_LEFT: { - startOfLine.y() -= _characterHeight; - cursor = startOfLine; + startOfLine_coords.y() -= _characterHeight; + cursor = startOfLine_coords; previous_charcode = 0; + _lineCount++; break; } case VERTICAL: { - startOfLine.x() += _characterHeight/_characterAspectRatio; - cursor = startOfLine; + startOfLine_coords.x() += _characterHeight/_characterAspectRatio; + cursor = startOfLine_coords; previous_charcode = 0; + // because _lineCount is the max vertical no. of characters.... + _lineCount = (_lineCount >linelength)?_lineCount:linelength; } break; } @@ -556,126 +676,7 @@ void Text::computeGlyphRepresentation() _textBB.expandBy(osg::Vec3(citr->x(),citr->y(),0.0f)); } } - - if (lineNumber>1) - { - // account for line justification - typedef std::vector LineDimensions; - LineDimensions minLineCoords(lineNumber, osg::Vec2(FLT_MAX,FLT_MAX)); - LineDimensions maxLineCoords(lineNumber, osg::Vec2(-FLT_MAX,-FLT_MAX)); - - // osg::notify(osg::NOTICE)<<"lineNumber="<second; - const GlyphQuads::Coords2& coords = glyphquad._coords; - - unsigned int coordIndex = 0; - for(GlyphQuads::LineNumbers::const_iterator litr = glyphquad._lineNumbers.begin(); - litr != glyphquad._lineNumbers.end(); - ++litr) - { - unsigned int line = *litr; - osg::Vec2& minLineCoord = minLineCoords[line]; - osg::Vec2& maxLineCoord = maxLineCoords[line]; - for(unsigned int ci=0;ci<4;++ci) - { - const osg::Vec2& coord = coords[coordIndex++]; - if (coord.x()maxLineCoord.x()) maxLineCoord.x()=coord.x(); - if (coord.y()>maxLineCoord.y()) maxLineCoord.y()=coord.y(); - } - } - } - - // osg::notify(osg::NOTICE)<<"Text dimensions min="<<_textBB.xMin()<<" "<<_textBB.yMin()<<" max="<<_textBB.xMax()<<" "<<_textBB.yMax()< LineOffsets; - LineOffsets lineOffsets(lineNumber,osg::Vec2(0.0f,0.0f)); - for(unsigned int li=0;li