/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 #include "GlyphGeometry.h" using namespace osgText; using namespace std; #if 0 #define TEXTURE_IMAGE_NUM_CHANNELS 1 #define TEXTURE_IMAGE_FORMAT OSGTEXT_GLYPH_FORMAT #else #define TEXTURE_IMAGE_NUM_CHANNELS 4 #define TEXTURE_IMAGE_FORMAT GL_RGBA #endif GlyphTexture::GlyphTexture(): _margin(1), _marginRatio(0.02f), _interval(1), _usedY(0), _partUsedX(0), _partUsedY(0) { setWrap(WRAP_S, CLAMP_TO_EDGE); setWrap(WRAP_T, CLAMP_TO_EDGE); } GlyphTexture::~GlyphTexture() { } // return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. int GlyphTexture::compare(const osg::StateAttribute& rhs) const { if (this<&rhs) return -1; else if (this>&rhs) return 1; return 0; } bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY) { int maxAxis = osg::maximum(glyph->s(), glyph->t()); int margin_from_ratio = (int)((float)maxAxis * _marginRatio); int search_distance = glyph->getFontResolution().second/8; int margin = _margin + osg::maximum(margin_from_ratio, search_distance); int width = glyph->s()+2*margin; int height = glyph->t()+2*margin; int partUsedX = ((_partUsedX % _interval) == 0) ? _partUsedX : (((_partUsedX/_interval)+1)*_interval); int partUsedY = ((_partUsedY % _interval) == 0) ? _partUsedY : (((_partUsedY/_interval)+1)*_interval); int usedY = ((_usedY % _interval) == 0) ? _usedY : (((_usedY/_interval)+1)*_interval); // first check box (partUsedX, usedY) to (width,height) if (width <= (getTextureWidth()-partUsedX) && height <= (getTextureHeight()-usedY)) { // can fit in existing row. // record the position in which the texture will be stored. posX = partUsedX+margin; posY = usedY+margin; // move used markers on. _partUsedX = posX+width; if (_usedY+height>_partUsedY) _partUsedY = _usedY+height; return true; } // start an new row. if (width <= getTextureWidth() && height <= (getTextureHeight()-_partUsedY)) { // can fit next row. _partUsedX = 0; _usedY = partUsedY; posX = _partUsedX+margin; posY = _usedY+margin; // move used markers on. _partUsedX = posX+width; _partUsedY = _usedY+height; return true; } // doesn't fit into glyph. return false; } void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY) { OpenThreads::ScopedLock lock(_mutex); if (!_image.valid()) createImage(); _glyphs.push_back(glyph); // set up the details of where to place glyph's image in the texture. glyph->setTexture(this); glyph->setTexturePosition(posX,posY); glyph->setMinTexCoord( osg::Vec2( static_cast(posX)/static_cast(getTextureWidth()), static_cast(posY)/static_cast(getTextureHeight()) ) ); glyph->setMaxTexCoord( osg::Vec2( static_cast(posX+glyph->s())/static_cast(getTextureWidth()), static_cast(posY+glyph->t())/static_cast(getTextureHeight()) ) ); copyGlyphImage(glyph); } void GlyphTexture::copyGlyphImage(Glyph* glyph) { if (_glyphTextureFeatures==GREYSCALE) { // OSG_NOTICE<<"GlyphTexture::copyGlyphImage() greyscale copy"<copySubImage(glyph->getTexturePositionX(), glyph->getTexturePositionY(), 0, glyph); _image->dirty(); return; } // OSG_NOTICE<<"GlyphTexture::copyGlyphImage() generating signed distance field."<s(); int src_rows = glyph->t(); unsigned char* src_data = glyph->data(); int dest_columns = _image->s(); int dest_rows = _image->t(); unsigned char* dest_data = _image->data(glyph->getTexturePositionX(),glyph->getTexturePositionY()); int search_distance = glyph->getFontResolution().second/8; int left = -search_distance; int right = glyph->s()+search_distance; int lower = -search_distance; int upper = glyph->t()+search_distance; float multiplier = 1.0/255.0f; float max_distance = sqrtf(float(search_distance*search_distance)*2.0f); int num_channels = TEXTURE_IMAGE_NUM_CHANNELS; if ((left+glyph->getTexturePositionX())<0) left = -glyph->getTexturePositionX(); if ((right+glyph->getTexturePositionX())>=dest_columns) right = dest_columns-glyph->getTexturePositionX()-1; if ((lower+glyph->getTexturePositionY())<0) lower = -glyph->getTexturePositionY(); if ((upper+glyph->getTexturePositionY())>=dest_rows) upper = dest_rows-glyph->getTexturePositionY()-1; for(int dr=lower; dr<=upper; ++dr) { for(int dc=left; dc<=right; ++dc) { unsigned char value = 0; unsigned char center_value = 0; if (dr>=0 && dr=0 && dc0 && center_value<255) { if (center_value_f>=0.5f) { min_distance = center_value_f-0.5f; value = 128+(min_distance/max_distance)*127; } else { min_distance = 0.5f-center_value_f; value = 127-(min_distance/max_distance)*127; } } else { for(int radius=1; radius=0 && r=0 && cabs(dy)) ? D/float(abs(dx)) : D/float(abs(dy)); float local_distance = sqrtf(float(radius*radius)+float(span*span)); if (center_value==0) local_distance += (0.5f-local_value_f)*local_multiplier; else local_distance += (local_value_f - 0.5f)*local_multiplier; } } { // top int dx = radius; int dy = span; int c = dc+dx; int r = dr+dy; unsigned char local_value = 0; if (r>=0 && r=0 && cabs(dy)) ? D/float(abs(dx)) : D/float(abs(dy)); float local_distance = sqrtf(float(radius*radius)+float(span*span)); if (center_value==0) local_distance += (0.5f-local_value_f)*local_multiplier; else local_distance += (local_value_f - 0.5f)*local_multiplier; } } { // right int dx = radius; int dy = -span; int c = dc+dx; int r = dr+dy; unsigned char local_value = 0; if (r>=0 && r=0 && cabs(dy)) ? D/float(abs(dx)) : D/float(abs(dy)); float local_distance = sqrtf(float(radius*radius)+float(span*span)); if (center_value==0) local_distance += (0.5f-local_value_f)*local_multiplier; else local_distance += (local_value_f - 0.5f)*local_multiplier; if (local_distance=0 && r=0 && cabs(dy)) ? D/float(abs(dx)) : D/float(abs(dy)); float local_distance = sqrtf(float(radius*radius)+float(span*span)); if (center_value==0) local_distance += (0.5f-local_value_f)*local_multiplier; else local_distance += (local_value_f - 0.5f)*local_multiplier; if (local_distance=0.5) { value = 128+(min_distance/max_distance)*127; } else { value = 127-(min_distance/max_distance)*127; } } unsigned char* dest_ptr = dest_data + (dr*dest_columns + dc)*num_channels; if (num_channels==4) { // signed distance field value *(dest_ptr++) = value; float outline_distance = max_distance/4.0f; // compute the alpha value of outline, one texel thick unsigned char outline = center_value; if (center_value<255) { if (min_distanceget()); } } } osg::Image* GlyphTexture::createImage() { if (!_image) { OSG_NOTICE<<"GlyphTexture::createImage() : Creating image 0x"<allocateImage(getTextureWidth(), getTextureHeight(), 1, imageFormat, GL_UNSIGNED_BYTE); memset(_image->data(), 0, _image->getTotalSizeInBytes()); for(GlyphRefList::iterator itr = _glyphs.begin(); itr != _glyphs.end(); ++itr) { Glyph* glyph = itr->get(); copyGlyphImage(glyph); } } return _image.get(); } // all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK. Glyph::Glyph(Font* font, unsigned int glyphCode): _font(font), _glyphCode(glyphCode), _width(1.0f), _height(1.0f), _horizontalBearing(0.0f,0.f), _horizontalAdvance(0.f), _verticalBearing(0.0f,0.f), _verticalAdvance(0.f), _texture(0), _texturePosX(0), _texturePosY(0), _minTexCoord(0.0f,0.0f), _maxTexCoord(0.0f,0.0f) { setThreadSafeRefUnref(true); } Glyph::~Glyph() { } void Glyph::setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; } const osg::Vec2& Glyph::getHorizontalBearing() const { return _horizontalBearing; } void Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; } float Glyph::getHorizontalAdvance() const { return _horizontalAdvance; } void Glyph::setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; } const osg::Vec2& Glyph::getVerticalBearing() const { return _verticalBearing; } void Glyph::setVerticalAdvance(float advance) { _verticalAdvance=advance; } float Glyph::getVerticalAdvance() const { return _verticalAdvance; } void Glyph::setTexture(GlyphTexture* texture) { _texture = texture; } GlyphTexture* Glyph::getTexture() { return _texture; } const GlyphTexture* Glyph::getTexture() const { return _texture; } void Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; } int Glyph::getTexturePositionX() const { return _texturePosX; } int Glyph::getTexturePositionY() const { return _texturePosY; } void Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; } const osg::Vec2& Glyph::getMinTexCoord() const { return _minTexCoord; } void Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; } const osg::Vec2& Glyph::getMaxTexCoord() const { return _maxTexCoord; } Glyph3D::Glyph3D(Font* font, unsigned int glyphCode): osg::Referenced(true), _font(font), _glyphCode(glyphCode), _width(1.0f), _height(1.0f), _horizontalBearing(0,0), _horizontalAdvance(0), _verticalBearing(0,0), _verticalAdvance(0) {} void Glyph3D::setThreadSafeRefUnref(bool threadSafe) { for(GlyphGeometries::iterator itr = _glyphGeometries.begin(); itr != _glyphGeometries.end(); ++itr) { (*itr)->setThreadSafeRefUnref(threadSafe); } } GlyphGeometry* Glyph3D::getGlyphGeometry(const Style* style) { for(GlyphGeometries::iterator itr = _glyphGeometries.begin(); itr != _glyphGeometries.end(); ++itr) { GlyphGeometry* glyphGeometry = itr->get(); if (glyphGeometry->match(style)) { OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) found matching GlyphGeometry."< glyphGeometry = new GlyphGeometry(); glyphGeometry->setup(this, style); _glyphGeometries.push_back(glyphGeometry); return glyphGeometry.get(); } GlyphGeometry::GlyphGeometry() { } void GlyphGeometry::setThreadSafeRefUnref(bool threadSafe) { if (_geode.valid()) _geode->setThreadSafeRefUnref(threadSafe); } void GlyphGeometry::setup(const Glyph3D* glyph, const Style* style) { float creaseAngle = 30.0f; bool smooth = true; osg::ref_ptr shellGeometry; if (!style) { OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) creating default glyph geometry."<getBevel(); bool outline = style->getOutlineRatio()>0.0f; float width = style->getThicknessRatio(); if (bevel) { osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, *bevel, width); _geometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width); shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0; } else { _geometry = osgText::computeTextGeometry(glyph, width); } } if (!_geometry) { OSG_INFO<<"Warning: GlyphGeometry::setup(const Glyph* glyph, const Style* style) failed."<addDrawable(_geometry.get()); if (shellGeometry.valid()) _geode->addDrawable(shellGeometry.get()); // create the normals if (smooth) { osgUtil::SmoothingVisitor::smooth(*_geometry, osg::DegreesToRadians(creaseAngle)); } _vertices = dynamic_cast(_geometry->getVertexArray()); _normals = dynamic_cast(_geometry->getNormalArray()); for(osg::Geometry::PrimitiveSetList::iterator itr = _geometry->getPrimitiveSetList().begin(); itr != _geometry->getPrimitiveSetList().end(); ++itr) { osg::PrimitiveSet* prim = itr->get(); if (prim->getName()=="front") _frontPrimitiveSetList.push_back(prim); else if (prim->getName()=="back") _backPrimitiveSetList.push_back(prim); else if (prim->getName()=="wall") _wallPrimitiveSetList.push_back(prim); } } bool GlyphGeometry::match(const Style* style) const { if (_style == style) return true; if (!_style || !style) return false; return (*_style==*style); }