This is another bug in osg. The problem now is with the relationship
between text and font objects. When a text object draws a new character
with the font, it changes the font's resolution to the text object's
"font resolution" prior to creating new characters. Future uses of those
characters from that font use the resolution already set. This font
resolution behavior is order dependent and explains the differing
behavior when initializing text objects in different orders.

The resolution control should be removed from the text object and placed
(solely) in the font. I.e. text objects should not be calling
setFontResolution on their fonts because this will affect other text
using the same font. Alternatively, the font interface can be changed so
that a single font object can manage multiple resolutions.

To get around the problem, you can try using a different font object for
each text object that uses a different resolution. To fix the example
code, you should simply create a unique font object for each text
object.


-----Original Message-----
From: Jeremy Moles [mailto:[EMAIL PROTECTED] 
Sent: Thursday, March 06, 2008 1:25 PM
To: OpenSceneGraph Users; Mark Sciabica
Subject: Re: [osg-users] Font Quality / osgWidget

On Thu, 2008-03-06 at 11:18 -0700, Mark Sciabica wrote:
> OK, here's the file as an attachment.

I applied your changes and got very noticeable results. :)

Something strange though:

Notice in the two screenshots attached, I switch between rendering
15-16's and then 16-15's. The first rendered size is always nice annd
crisp; the second rendered text size is always "smudgy" compared to the
first. Furthermore, this has nothing to do with font sizes, as both
modes demonstrate the effect. I'm not entirely familiar with the effect
of "sub-pixel hinting", but perhaps this is a result of it...

Maybe I'll be able to track this down in the next few days, so that
Robert won't have any bugs to contend with later. :) At any rate, things
are looking good, and I'm really glad to be so close to really nice font
clarity...

> -----Original Message-----
> From: [EMAIL PROTECTED]
> [mailto:[EMAIL PROTECTED] On Behalf Of
Jeremy
> Moles
> Sent: Thursday, March 06, 2008 11:11 AM
> To: OpenSceneGraph Users
> Subject: Re: [osg-users] Font Quality / osgWidget
> 
> Great info!
> 
> Is there any way we can get this modification as an attachment, and
not
> inline? The likelihood of Robert looking at it seriously is very low
if
> it's not easily detachable from the e-mail. :)
> 
> But again, this is great, great info! Thanks a ton!
> 
> On Thu, 2008-03-06 at 10:09 -0800, Mark Sciabica wrote:
> > I ran into the same problem and I agree that the appearance of the
> > clipped text is quite ugly. Unfortunately, the problem lies within
the
> > design of osgText and not your usage of it.
> > 
> > Texels sampled within the character cell are linearly blended with
one
> > another, giving an antialiasing effect internal to the character
cell.
> > However, when a character cell has a texel set at its edge, no
> > blending takes place outside that edge because the texture isn't
drawn
> > outside the quad.
> > 
> > My solution was to modify osgText to draw larger quads for the
> > character cells (by one texel in each direction), adjusting texture
> > coordinates accordingly. Side effects are that character cells now
> > overlap so depth testing needs to be disabled (or some sort of
> > stenciling operation performed), and extra margin should be
allocated
> > for each character in the texture (using Font::setGlyphImageMargin).
> > 
> > Another possible "solution" is to disable texture filtering, so all
> > parts of each character will have the same clipped appearance. If
> > you're already using some sort of full screen antialiasing this
might
> > not look too bad.
> > 
> > Below is my modified version of Text::computeGlyphRepresentation()
> > from osgText/Text.cpp. The modified code is controlled by #ifdefs.
> > 
> > Robert: Given the restrictions (depth writing needs to be disabled),
> > is this something you would want to add to osg? Perhaps configurable
> > as an optional method of text rendering? If so, I'll submit the
> > changed files to the submissions list.
> > 
> > void Text::computeGlyphRepresentation()
> > {
> >     Font* activefont = getActiveFont();
> >     if (!activefont) return;
> > 
> >     _textureGlyphQuadMap.clear();
> >     _lineCount = 0;
> > 
> >     if (_text.empty())
> >     {
> >         _textBB.set(0,0,0,0,0,0);//no size text
> >         computePositions(); //to reset the origin
> >         return;
> >     }
> > 
> >     OpenThreads::ScopedLock<Font::FontMutex>
> > lock(*(Font::getSerializeFontCallsMutex()));
> > 
> >     // initialize bounding box, it will be expanded during glyph
> > position calculation
> >     _textBB.init();
> > 
> >     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;
> > 
> >     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.
> >             osg::Vec2 endOfLine_coords(cursor);
> >             String::iterator endOfLine_itr =
> > computeLastCharacterOnLine(endOfLine_coords, itr,_text.end());
> > 
> >             linelength = 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 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;
> >           }
> >         }
> > 
> >         if (itr!=endOfLine_itr)
> >         {
> > 
> >             for(;itr!=endOfLine_itr;++itr)
> >             {
> >                 unsigned int charcode = *itr;
> > 
> >                 Font::Glyph* glyph = activefont->getGlyph(charcode);
> >                 if (glyph)
> >                 {
> >                     float width = (float)(glyph->s()) * wr;
> >                     float height = (float)(glyph->t()) * 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()];
> > 
> >                     glyphquad._glyphs.push_back(glyph);
> >                     glyphquad._lineNumbers.push_back(lineNumber);
> > #define FixClippingProblem
> > #if !defined(FixClippingProblem)
> >                     // 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.
> >                     // also expand bounding box
> >                     switch(_layout)
> >                     {
> >                       case LEFT_TO_RIGHT:
> >                           cursor.x() +=
glyph->getHorizontalAdvance()
> > * wr;
> >  
> > _textBB.expandBy(osg::Vec3(local.x(),local.y(),0.0f)); //lower left
> > corner
> >  
> > _textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f));
//upper
> > right corner
> >                           break;
> >                       case VERTICAL:
> >                           cursor.y() -= glyph->getVerticalAdvance()
> > *hr;
> >  
> > _textBB.expandBy(osg::Vec3(local.x(),local.y()+height,0.0f));
//upper
> > left corner
> >                           _textBB.expandBy(osg::Vec3(local.x()
> > +width,cursor.y(),0.0f)); //lower right corner
> >                           break;
> >                       case RIGHT_TO_LEFT:
> >                           _textBB.expandBy(osg::Vec3(local.x()
> > +width,local.y(),0.0f)); //lower right corner
> >  
> > _textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f));
//upper
> > left corner
> >                           break;
> >                     }
> > #else // Use modified text rendering
> > // Character quads are enlarged, and texture coordinates
> > correspondingly expanded
> > // to allow blending of text outside of defined character cell.
> > // Extra margin should be allocated in the glyph's texture since
we're
> > using an extra texel
> > // of blank space. Also, depth writing should be disabled so
adjacent
> > characters  are guaranteed
> > // not to clip each other.
> >                     // set up the tex coords of the quad
> >                     osg::Vec2 mintc = glyph->getMinTexCoord();
> >                     osg::Vec2 maxtc = glyph->getMaxTexCoord();
> >                     osg::Vec2 vDiff = maxtc - mintc;
> >                     float fHorizTCMargin = 1.0 /
glyph->getTexture()-
> > >getTextureWidth();
> >                     float fVertTCMargin = 1.0 / glyph->getTexture()-
> > >getTextureHeight();
> >                     float fHorizQuadMargin = vDiff.x() == 0 ? 0 :
> > width * fHorizTCMargin / vDiff.x();
> >                     float fVertQuadMargin = vDiff.y() == 0 ? 0 :
> > height * fVertTCMargin / vDiff.y();
> >                     mintc.x() -= fHorizTCMargin;
> >                     mintc.y() -= fVertTCMargin;
> >                     maxtc.x() += fHorizTCMargin;
> >                     maxtc.y() += fVertTCMargin;
> > 
> >  
> > 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()));
> > 
> >                     // set up the coords of the quad
> >                     osg::Vec2 upLeft = local+osg::Vec2(0.0f-
> > fHorizQuadMargin,height+fVertQuadMargin);
> >                     osg::Vec2 lowLeft = local+osg::Vec2(0.0f-
> > fHorizQuadMargin,0.0f-fVertQuadMargin);
> >                     osg::Vec2 lowRight = local+osg::Vec2(width
> > +fHorizQuadMargin,0.0f-fVertQuadMargin);
> >                     osg::Vec2 upRight = local+osg::Vec2(width
> > +fHorizQuadMargin,height+fVertQuadMargin);
> >                     glyphquad._coords.push_back(upLeft);
> >                     glyphquad._coords.push_back(lowLeft);
> >                     glyphquad._coords.push_back(lowRight);
> >                     glyphquad._coords.push_back(upRight);
> > 
> >                     // move the cursor onto the next character.
> >                     // also expand bounding box
> >                     switch(_layout)
> >                     {
> >                       case LEFT_TO_RIGHT:
> >                           cursor.x() +=
glyph->getHorizontalAdvance()
> > * wr;
> >                           _textBB.expandBy(osg::Vec3(lowLeft.x(),
> > lowLeft.y(), 0.0f)); //lower left corner
> >                           _textBB.expandBy(osg::Vec3(upRight.x(),
> > upRight.y(), 0.0f)); //upper right corner
> >                           break;
> >                       case VERTICAL:
> >                           cursor.y() -= glyph->getVerticalAdvance()
> > *hr;
> >  
> > _textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper
left
> > corner
> >  
> > _textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower
> > right corner
> >                           break;
> >                       case RIGHT_TO_LEFT:
> >  
> > _textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower
> > right corner
> >  
> > _textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper
left
> > corner
> >                           break;
> >                     }
> > #endif // else defined(FixClippingProblem)
> >                     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_coords.y() -= _characterHeight * (1.0 +
> > _lineSpacing);
> >             cursor = startOfLine_coords;
> >             previous_charcode = 0;
> >             _lineCount++;
> >             break;
> >           }
> >           case RIGHT_TO_LEFT:
> >           {
> >             startOfLine_coords.y() -= _characterHeight * (1.0 +
> > _lineSpacing);
> >             cursor = startOfLine_coords;
> >             previous_charcode = 0;
> >             _lineCount++;
> >             break;
> >           }
> >           case VERTICAL:
> >           {
> >             startOfLine_coords.x() += _characterHeight/
> > _characterAspectRatio * (1.0 + _lineSpacing);
> >             cursor = startOfLine_coords;
> >             previous_charcode = 0;
> >             // because _lineCount is the max vertical no. of
> > characters....
> >             _lineCount = (_lineCount >linelength)?
> > _lineCount:linelength;
> >           }
> >           break;
> >         }
> > 
> >         ++lineNumber;
> > 
> >     }
> > 
> >     computePositions();
> >     computeBackdropBoundingBox();
> >     computeColorGradients();
> > }
> > 
> > _______________________________________________
> > osg-users mailing list
> > osg-users@lists.openscenegraph.org
> >
>
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.or
> g
> > 
> 
> _______________________________________________
> osg-users mailing list
> osg-users@lists.openscenegraph.org
>
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.or
> g
> _______________________________________________
> osg-users mailing list
> osg-users@lists.openscenegraph.org
>
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.or
g
_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to