/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include "DefaultFont.h" using namespace osgText; //#define TREES_CODE_FOR_MAKING_SPACES_EDITABLE Text::Text(): _fontWidth(32), _fontHeight(32), _characterHeight(32), _characterAspectRatio(1.0f), _characterSizeMode(OBJECT_COORDS), _maximumWidth(0.0f), _maximumHeight(0.0f), _alignment(BASE_LINE), _autoRotateToScreen(false), _layout(LEFT_TO_RIGHT), _color(1.0f,1.0f,1.0f,1.0f), _drawMode(TEXT), _kerningType(KERNING_DEFAULT) { setUseDisplayList(false); } Text::Text(const Text& text,const osg::CopyOp& copyop): osg::Drawable(text,copyop), _font(text._font), _fontWidth(text._fontWidth), _fontHeight(text._fontHeight), _characterHeight(text._characterHeight), _characterAspectRatio(text._characterAspectRatio), _characterSizeMode(text._characterSizeMode), _maximumWidth(text._maximumWidth), _maximumHeight(text._maximumHeight), _text(text._text), _position(text._position), _alignment(text._alignment), _rotation(text._rotation), _autoRotateToScreen(text._autoRotateToScreen), _layout(text._layout), _color(text._color), _drawMode(text._drawMode), _kerningType(text._kerningType) { computeGlyphRepresentation(); } Text::~Text() { } void Text::setFont(Font* font) { if (_font==font) return; _font = font; computeGlyphRepresentation(); } void Text::setFont(const std::string& fontfile) { setFont(readFontFile(fontfile)); } void Text::setFontResolution(unsigned int width, unsigned int height) { _fontWidth = width; _fontHeight = height; computeGlyphRepresentation(); } void Text::setCharacterSize(float height,float aspectRatio) { _characterHeight = height; _characterAspectRatio = aspectRatio; computeGlyphRepresentation(); } void Text::setMaximumWidth(float maximumWidth) { _maximumWidth = maximumWidth; computeGlyphRepresentation(); } void Text::setMaximumHeight(float maximumHeight) { _maximumHeight = maximumHeight; computeGlyphRepresentation(); } void Text::setText(const String& text) { if (_text==text) return; _text = text; computeGlyphRepresentation(); } void Text::setText(const std::string& text) { setText(String(text)); } void Text::setText(const std::string& text,String::Encoding encoding) { setText(String(text,encoding)); } void Text::setText(const wchar_t* text) { setText(String(text)); } void Text::setPosition(const osg::Vec3& pos) { if (_position==pos) return; _position = pos; computePositions(); } void Text::setAlignment(AlignmentType alignment) { if (_alignment==alignment) return; _alignment = alignment; computePositions(); } void Text::setAxisAlignment(AxisAlignment axis) { switch(axis) { case XZ_PLANE: setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); break; case REVERSED_XZ_PLANE: setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); break; case YZ_PLANE: setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); break; case REVERSED_YZ_PLANE: setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); break; case XY_PLANE: setRotation(osg::Quat()); // nop - already on XY plane. break; case REVERSED_XY_PLANE: setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))); break; case SCREEN: setAutoRotateToScreen(true); break; } } void Text::setRotation(const osg::Quat& quat) { _rotation = quat; computePositions(); } void Text::setAutoRotateToScreen(bool autoRotateToScreen) { if (_autoRotateToScreen==autoRotateToScreen) return; _autoRotateToScreen = autoRotateToScreen; } void Text::setLayout(Layout layout) { if (_layout==layout) return; _layout = layout; computeGlyphRepresentation(); } void Text::setColor(const osg::Vec4& color) { _color = color; } void Text::setDrawMode(unsigned int mode) { if (_drawMode==mode) return; _drawMode=mode; } bool Text::computeBound() const { _bbox.init(); if (_textBB.valid()) { for(unsigned int i=0;i<_autoTransformCache.size();++i) { osg::Matrix& matrix = _autoTransformCache[i]._matrix; _bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix); _bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix); _bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix); _bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix); } } _bbox_computed = true; return true; } Font* Text::getActiveFont() { return _font.valid() ? _font.get() : DefaultFont::instance(); } 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) { Font* activefont = getActiveFont(); if (!activefont) return last; float hr = _characterHeight/(float)activefont->getFontHeight(); float wr = hr/_characterAspectRatio; bool horizontal = _layout!=VERTICAL; bool kerning = true; unsigned int previous_charcode = 0; for(String::iterator itr=first;itr!=last;++itr) { unsigned int charcode = *itr; if (charcode=='\n') { return itr; } 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 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. } } osg::Vec2 bearing(horizontal?glyph->getHorizontalBearing():glyph->getVerticalBearing()); cursor.x() += bearing.x() * wr; cursor.y() += bearing.y() * hr; // 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; break; } case RIGHT_TO_LEFT: { if (_maximumWidth>0.0f && cursor.x()<-_maximumWidth) return itr; break; } case VERTICAL: if (_maximumHeight>0.0f && cursor.y()<-_maximumHeight) return itr; break; } // move the cursor onto the next character. switch(_layout) { case LEFT_TO_RIGHT: cursor.x() += glyph->getHorizontalAdvance() * wr; break; case VERTICAL: cursor.y() -= glyph->getVerticalAdvance() *hr; break; case RIGHT_TO_LEFT: break; // nop. } } } return last; } void Text::computeGlyphRepresentation() { Font* activefont = getActiveFont(); if (!activefont) return; _textureGlyphQuadMap.clear(); if (_text.empty()) { _textBB.set(0,0,0,0,0,0);//no size text computePositions(); //to reset the origin return; } osg::Vec2 startOfLine(0.0f,0.0f); osg::Vec2 cursor(startOfLine); osg::Vec2 local(0.0f,0.0f); unsigned int previous_charcode = 0; bool horizontal = _layout!=VERTICAL; bool kerning = true; 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(); ) { // find the end of the current line. String::iterator endOfLine = computeLastCharacterOnLine(cursor, itr,_text.end()); if (itr!=endOfLine) { if (endOfLine!=_text.end()) { if (deliminatorSet.count(*endOfLine)==0) { String::iterator lastValidChar = endOfLine; while (lastValidChar!=itr && deliminatorSet.count(*lastValidChar)==0) { --lastValidChar; } if (itr!=lastValidChar) { ++lastValidChar; endOfLine = lastValidChar; } } } for(;itr!=endOfLine;++itr) { unsigned int charcode = *itr; Font::Glyph* glyph = activefont->getGlyph(charcode); if (glyph) { 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) { 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; GlyphQuads& glyphquad = _textureGlyphQuadMap[glyph->getTexture()->getStateSet()]; glyphquad._glyphs.push_back(glyph); glyphquad._lineNumbers.push_back(lineNumber); // set up the coords of the quad glyphquad._coords.push_back(local+osg::Vec2(0.0f,height)); glyphquad._coords.push_back(local+osg::Vec2(0.0f,0.0f)); glyphquad._coords.push_back(local+osg::Vec2(width,0.0f)); glyphquad._coords.push_back(local+osg::Vec2(width,height)); // set up the tex coords of the quad const osg::Vec2& mintc = glyph->getMinTexCoord(); const osg::Vec2& maxtc = glyph->getMaxTexCoord(); glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),maxtc.y())); glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),mintc.y())); glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),mintc.y())); glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),maxtc.y())); // move the cursor onto the next character. switch(_layout) { case LEFT_TO_RIGHT: cursor.x() += glyph->getHorizontalAdvance() * wr; break; case VERTICAL: cursor.y() -= glyph->getVerticalAdvance() *hr; break; case RIGHT_TO_LEFT: break; // nop. } previous_charcode = charcode; } } } else { ++itr; } if (itr!=_text.end()) { // skip over return. if (*itr=='\n') ++itr; } // move to new line. switch(_layout) { case LEFT_TO_RIGHT: { startOfLine.y() -= _characterHeight; cursor = startOfLine; previous_charcode = 0; break; } case RIGHT_TO_LEFT: { startOfLine.y() -= _characterHeight; cursor = startOfLine; previous_charcode = 0; break; } case VERTICAL: { startOfLine.x() += _characterHeight/_characterAspectRatio; cursor = startOfLine; previous_charcode = 0; } break; } ++lineNumber; } // compute the bounding box _textBB.init(); for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin(); titr!=_textureGlyphQuadMap.end(); ++titr) { const GlyphQuads& glyphquad = titr->second; for(GlyphQuads::Coords2::const_iterator citr = glyphquad._coords.begin(); citr != glyphquad._coords.end(); ++citr) { _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