root/OpenSceneGraph/trunk/src/osgText/Text.cpp @ 8093

Revision 8093, 66.5 kB (checked in by robert, 6 years ago)

From Christian Kaser, "I discovered a bug that lead to a space being displayed at the start of the new line after an automatic line break (through setMaximumWidth()). The fix simply skips all spaces at the end of the line, before skipping a line break which was done already.

osgText/Text.cpp: Line 502

...
else
{

++itr;

}

if (itr!=_text.end())
{

// skip over spaces and return.
while (*itr==' ') ++itr; // New
if (*itr=='\n') ++itr;

}

// move to new line.
switch(_layout)
{
.."

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14
15#include <osgText/Text>
16
17#include <osg/Math>
18#include <osg/GL>
19#include <osg/Notify>
20#include <osg/PolygonOffset>
21#include <osg/TexEnv>
22
23#include <osgUtil/CullVisitor>
24
25#include <osgDB/ReadFile>
26
27#include "DefaultFont.h"
28
29using namespace osg;
30using namespace osgText;
31
32//#define TREES_CODE_FOR_MAKING_SPACES_EDITABLE
33
34Text::Text():
35    _color(1.0f,1.0f,1.0f,1.0f),
36    _backdropType(NONE),
37    _backdropImplementation(DEPTH_RANGE),
38    _backdropHorizontalOffset(0.07f),
39    _backdropVerticalOffset(0.07f),
40    _backdropColor(0.0f, 0.0f, 0.0f, 1.0f),
41    _colorGradientMode(SOLID),
42    _colorGradientTopLeft(1.0f, 0.0f, 0.0f, 1.0f),
43    _colorGradientBottomLeft(0.0f, 1.0f, 0.0f, 1.0f),
44    _colorGradientBottomRight(0.0f, 0.0f, 1.0f, 1.0f),
45    _colorGradientTopRight(1.0f, 1.0f, 1.0f, 1.0f)   
46{}
47
48Text::Text(const Text& text,const osg::CopyOp& copyop):
49    osgText::TextBase(text,copyop),
50    _font(text._font),
51    _color(text._color),
52    _backdropType(text._backdropType),
53    _backdropImplementation(text._backdropImplementation),
54    _backdropHorizontalOffset(text._backdropHorizontalOffset),
55    _backdropVerticalOffset(text._backdropVerticalOffset),
56    _backdropColor(text._backdropColor),
57    _colorGradientMode(text._colorGradientMode),
58    _colorGradientTopLeft(text._colorGradientTopLeft),
59    _colorGradientBottomLeft(text._colorGradientBottomLeft),
60    _colorGradientBottomRight(text._colorGradientBottomRight),
61    _colorGradientTopRight(text._colorGradientTopRight)
62{
63    computeGlyphRepresentation();
64}
65
66Text::~Text()
67{
68}
69
70void Text::setFont(osg::ref_ptr<Font> font)
71{
72    if (_font==font) return;
73   
74    osg::StateSet* previousFontStateSet = _font.valid() ? _font->getStateSet() : DefaultFont::instance()->getStateSet();
75    osg::StateSet* newFontStateSet = font.valid() ? font->getStateSet() : DefaultFont::instance()->getStateSet();
76   
77    if (getStateSet() == previousFontStateSet)
78    {
79        setStateSet( newFontStateSet );
80    }
81   
82    _font = font;
83   
84    computeGlyphRepresentation();
85}
86
87void Text::setFont(const std::string& fontfile)
88{
89    setFont(readRefFontFile(fontfile));
90}
91
92
93void Text::setColor(const osg::Vec4& color)
94{
95    _color = color;
96}
97
98
99Font* Text::getActiveFont()
100{
101    return _font.valid() ? _font.get() : DefaultFont::instance();
102}
103
104const Font* Text::getActiveFont() const
105{
106    return _font.valid() ? _font.get() : DefaultFont::instance();
107}
108
109String::iterator Text::computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last)
110{
111    Font* activefont = getActiveFont();
112    if (!activefont) return last;
113
114    float hr = _characterHeight/getFontHeight();
115    float wr = hr/_characterAspectRatio;
116
117    bool kerning = true;
118    unsigned int previous_charcode = 0;
119
120    String::iterator lastChar = first;
121
122    std::set<unsigned int> deliminatorSet;
123    deliminatorSet.insert(' ');
124    deliminatorSet.insert('\n');
125    deliminatorSet.insert(':');
126    deliminatorSet.insert('/');
127    deliminatorSet.insert(',');
128    deliminatorSet.insert(';');
129    deliminatorSet.insert(':');
130    deliminatorSet.insert('.');
131
132    for(bool outOfSpace=false;lastChar!=last;++lastChar)
133    {
134        unsigned int charcode = *lastChar;
135       
136        if (charcode=='\n')
137        {
138            return lastChar;
139        }
140
141        Font::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
142        if (glyph)
143        {
144
145           float width = (float)(glyph->s()) * wr;
146           //float height = (float)(glyph->t()) * hr;
147           #ifdef TREES_CODE_FOR_MAKING_SPACES_EDITABLE
148           if (width == 0.0f)  width = glyph->getHorizontalAdvance() * wr;
149           //if (height == 0.0f) height = glyph->getVerticalAdvance() * hr;
150           #endif
151
152            if (_layout==RIGHT_TO_LEFT)
153            {
154                cursor.x() -= glyph->getHorizontalAdvance() * wr;
155            }
156
157            // adjust cursor position w.r.t any kerning.
158            if (kerning && previous_charcode)
159            {
160                switch(_layout)
161                {
162                  case LEFT_TO_RIGHT:
163                  {
164                    osg::Vec2 delta(activefont->getKerning(_fontSize, previous_charcode,charcode,_kerningType));
165                    cursor.x() += delta.x() * wr;
166                    cursor.y() += delta.y() * hr;
167                    break;
168                  }
169                  case RIGHT_TO_LEFT:
170                  {
171                    osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,previous_charcode,_kerningType));
172                    cursor.x() -= delta.x() * wr;
173                    cursor.y() -= delta.y() * hr;
174                    break;
175                  }
176                  case VERTICAL:
177                    break; // no kerning when vertical.
178                }            // check to see if we are still within line if not move to next line.
179            }
180
181            switch(_layout)
182            {
183              case LEFT_TO_RIGHT:
184              {
185                if (_maximumWidth>0.0f && cursor.x()+width>_maximumWidth) outOfSpace=true;
186                if(_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
187                break;
188              }
189              case RIGHT_TO_LEFT:
190              {
191                if (_maximumWidth>0.0f && cursor.x()<-_maximumWidth) outOfSpace=true;
192                if(_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
193                break;
194              }
195              case VERTICAL:
196                if (_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
197                break;
198            }
199           
200            // => word boundary detection & wrapping
201            if (outOfSpace) break;
202
203            // move the cursor onto the next character.
204            switch(_layout)
205            {
206              case LEFT_TO_RIGHT: cursor.x() += glyph->getHorizontalAdvance() * wr; break;
207              case VERTICAL:      cursor.y() -= glyph->getVerticalAdvance() *hr; break;
208              case RIGHT_TO_LEFT: break; // nop.
209            }
210
211        previous_charcode = charcode;
212
213        }
214
215    }
216   
217    // word boundary detection & wrapping
218    if (lastChar!=last)
219    {
220        if (deliminatorSet.count(*lastChar)==0)
221        {
222            String::iterator lastValidChar = lastChar;
223            while (lastValidChar!=first && deliminatorSet.count(*lastValidChar)==0)
224            {
225                --lastValidChar;
226               
227                // Subtract off glyphs from the cursor position (to correctly center text)
228                Font::Glyph* glyph = activefont->getGlyph(_fontSize, *lastValidChar);
229                if (glyph)
230                {
231                    switch(_layout)
232                    {
233                    case LEFT_TO_RIGHT: cursor.x() -= glyph->getHorizontalAdvance() * wr; break;
234                    case VERTICAL:      cursor.y() += glyph->getVerticalAdvance() * hr; break;
235                    case RIGHT_TO_LEFT: break; // nop.
236                    }
237                }
238            }
239            if (first!=lastValidChar)
240            {
241                ++lastValidChar;
242                lastChar = lastValidChar;
243            }
244        }
245    }
246
247    return lastChar;
248}
249
250
251void Text::computeGlyphRepresentation()
252{
253    Font* activefont = getActiveFont();
254    if (!activefont) return;
255   
256    _textureGlyphQuadMap.clear();
257    _lineCount = 0;
258   
259    if (_text.empty())
260    {
261        _textBB.set(0,0,0,0,0,0);//no size text
262        TextBase::computePositions(); //to reset the origin
263        return;
264    }
265   
266    //OpenThreads::ScopedLock<Font::FontMutex> lock(*(activefont->getSerializeFontCallsMutex()));
267
268    // initialize bounding box, it will be expanded during glyph position calculation
269    _textBB.init();
270
271    osg::Vec2 startOfLine_coords(0.0f,0.0f);
272    osg::Vec2 cursor(startOfLine_coords);
273    osg::Vec2 local(0.0f,0.0f);
274   
275    unsigned int previous_charcode = 0;
276    unsigned int linelength = 0;
277    bool horizontal = _layout!=VERTICAL;
278    bool kerning = true;
279   
280    unsigned int lineNumber = 0;
281
282    float hr = _characterHeight/getFontHeight();
283    float wr = hr/_characterAspectRatio;
284
285    for(String::iterator itr=_text.begin();
286        itr!=_text.end();
287        )
288    {
289        // record the start of the current line
290            String::iterator startOfLine_itr = itr;
291
292            // find the end of the current line.
293            osg::Vec2 endOfLine_coords(cursor);
294            String::iterator endOfLine_itr = computeLastCharacterOnLine(endOfLine_coords, itr,_text.end());
295
296            linelength = endOfLine_itr - startOfLine_itr;
297
298            // Set line position to correct alignment.
299            switch(_layout)
300            {
301            case LEFT_TO_RIGHT:
302            {
303            switch(_alignment)
304            {
305              // nothing to be done for these
306              //case LEFT_TOP:
307              //case LEFT_CENTER:
308              //case LEFT_BOTTOM:
309              //case LEFT_BASE_LINE:
310              //case LEFT_BOTTOM_BASE_LINE:
311              //  break;
312              case CENTER_TOP:
313              case CENTER_CENTER:
314              case CENTER_BOTTOM:
315              case CENTER_BASE_LINE:
316              case CENTER_BOTTOM_BASE_LINE:
317                cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
318                break;
319              case RIGHT_TOP:
320              case RIGHT_CENTER:
321              case RIGHT_BOTTOM:
322              case RIGHT_BASE_LINE:
323              case RIGHT_BOTTOM_BASE_LINE:
324                cursor.x() = cursor.x() - endOfLine_coords.x();
325                break;
326              default:
327                break;
328              }
329            break;
330            }
331            case RIGHT_TO_LEFT:
332            {
333            switch(_alignment)
334            {
335              case LEFT_TOP:
336              case LEFT_CENTER:
337              case LEFT_BOTTOM:
338              case LEFT_BASE_LINE:
339              case LEFT_BOTTOM_BASE_LINE:
340                cursor.x() = 2*cursor.x() - endOfLine_coords.x();
341                break;
342              case CENTER_TOP:
343              case CENTER_CENTER:
344              case CENTER_BOTTOM:
345              case CENTER_BASE_LINE:
346              case CENTER_BOTTOM_BASE_LINE:
347                cursor.x() = cursor.x() + (cursor.x() - endOfLine_coords.x()) * 0.5f;
348                break;
349              // nothing to be done for these
350              //case RIGHT_TOP:
351              //case RIGHT_CENTER:
352              //case RIGHT_BOTTOM:
353              //case RIGHT_BASE_LINE:
354              //case RIGHT_BOTTOM_BASE_LINE:
355              //  break;
356              default:
357                break;
358            }
359            break;
360            }
361            case VERTICAL:
362            {
363            switch(_alignment)
364            {
365              // TODO: current behaviour top baselines lined up in both cases - need to implement
366              //       top of characters alignment - Question is this necessary?
367              // ... otherwise, nothing to be done for these 6 cases
368              //case LEFT_TOP:
369              //case CENTER_TOP:
370              //case RIGHT_TOP:
371              //  break;
372              //case LEFT_BASE_LINE:
373              //case CENTER_BASE_LINE:
374              //case RIGHT_BASE_LINE:
375              //  break;
376              case LEFT_CENTER:
377              case CENTER_CENTER:
378              case RIGHT_CENTER:
379                cursor.y() = cursor.y() + (cursor.y() - endOfLine_coords.y()) * 0.5f;
380                break;
381              case LEFT_BOTTOM_BASE_LINE:
382              case CENTER_BOTTOM_BASE_LINE:
383              case RIGHT_BOTTOM_BASE_LINE:
384                cursor.y() = cursor.y() - (linelength * _characterHeight);
385                break;
386              case LEFT_BOTTOM:
387              case CENTER_BOTTOM:
388              case RIGHT_BOTTOM:
389                cursor.y() = 2*cursor.y() - endOfLine_coords.y();
390                break;
391              default:
392                break;
393            }
394            break;
395          }
396        }
397
398        if (itr!=endOfLine_itr)
399        {
400
401            for(;itr!=endOfLine_itr;++itr)
402            {
403                unsigned int charcode = *itr;
404
405                Font::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
406                if (glyph)
407                {
408                    float width = (float)(glyph->s()) * wr;
409                    float height = (float)(glyph->t()) * hr;
410
411                    #ifdef TREES_CODE_FOR_MAKING_SPACES_EDITABLE
412                    if (width == 0.0f)  width = glyph->getHorizontalAdvance() * wr;
413                    if (height == 0.0f) height = glyph->getVerticalAdvance() * hr;
414                    #endif
415
416                    if (_layout==RIGHT_TO_LEFT)
417                    {
418                        cursor.x() -= glyph->getHorizontalAdvance() * wr;
419                    }
420
421                    // adjust cursor position w.r.t any kerning.
422                    if (kerning && previous_charcode)
423                    {
424                        switch(_layout)
425                        {
426                          case LEFT_TO_RIGHT:
427                          {
428                            osg::Vec2 delta(activefont->getKerning(_fontSize, previous_charcode,charcode,_kerningType));
429                            cursor.x() += delta.x() * wr;
430                            cursor.y() += delta.y() * hr;
431                            break;
432                          }
433                          case RIGHT_TO_LEFT:
434                          {
435                            osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,previous_charcode,_kerningType));
436                            cursor.x() -= delta.x() * wr;
437                            cursor.y() -= delta.y() * hr;
438                            break;
439                          }
440                          case VERTICAL:
441                            break; // no kerning when vertical.
442                        }
443                    }
444
445                    local = cursor;
446                    osg::Vec2 bearing(horizontal?glyph->getHorizontalBearing():glyph->getVerticalBearing());
447                    local.x() += bearing.x() * wr;
448                    local.y() += bearing.y() * hr;
449
450                    GlyphQuads& glyphquad = _textureGlyphQuadMap[glyph->getTexture()];
451
452                    glyphquad._glyphs.push_back(glyph);
453                    glyphquad._lineNumbers.push_back(lineNumber);
454
455                    // set up the coords of the quad
456                    glyphquad._coords.push_back(local+osg::Vec2(0.0f,height));
457                    glyphquad._coords.push_back(local+osg::Vec2(0.0f,0.0f));
458                    glyphquad._coords.push_back(local+osg::Vec2(width,0.0f));
459                    glyphquad._coords.push_back(local+osg::Vec2(width,height));
460
461                    // set up the tex coords of the quad
462                    const osg::Vec2& mintc = glyph->getMinTexCoord();
463                    const osg::Vec2& maxtc = glyph->getMaxTexCoord();
464
465                    glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),maxtc.y()));
466                    glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),mintc.y()));
467                    glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),mintc.y()));
468                    glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),maxtc.y()));
469
470                    // move the cursor onto the next character.
471                    // also expand bounding box
472                    switch(_layout)
473                    {
474                      case LEFT_TO_RIGHT:
475                          cursor.x() += glyph->getHorizontalAdvance() * wr;
476                          _textBB.expandBy(osg::Vec3(local.x(),local.y(),0.0f)); //lower left corner
477                          _textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f)); //upper right corner
478                          break;
479                      case VERTICAL:
480                          cursor.y() -= glyph->getVerticalAdvance() *hr;
481                          _textBB.expandBy(osg::Vec3(local.x(),local.y()+height,0.0f)); //upper left corner
482                          _textBB.expandBy(osg::Vec3(local.x()+width,cursor.y(),0.0f)); //lower right corner
483                          break;
484                      case RIGHT_TO_LEFT:
485                          _textBB.expandBy(osg::Vec3(local.x()+width,local.y(),0.0f)); //lower right corner
486                          _textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f)); //upper left corner
487                          break;
488                    }
489
490                    previous_charcode = charcode;
491
492                }
493            }
494        }
495        else
496        {
497            ++itr;
498        }
499                               
500        if (itr!=_text.end())
501        {
502            // skip over spaces and return.
503            while (*itr==' ') ++itr;
504            if (*itr=='\n') ++itr;
505        }
506               
507        // move to new line.
508        switch(_layout)
509        {
510          case LEFT_TO_RIGHT:
511          {
512            startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
513            cursor = startOfLine_coords;
514            previous_charcode = 0;
515            _lineCount++;
516            break;
517          }
518          case RIGHT_TO_LEFT:
519          {
520            startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
521            cursor = startOfLine_coords;
522            previous_charcode = 0;
523            _lineCount++;
524            break;
525          }
526          case VERTICAL:
527          {
528            startOfLine_coords.x() += _characterHeight/_characterAspectRatio * (1.0 + _lineSpacing);
529            cursor = startOfLine_coords;
530            previous_charcode = 0;
531            // because _lineCount is the max vertical no. of characters....
532            _lineCount = (_lineCount >linelength)?_lineCount:linelength;
533          }
534          break;
535        }
536       
537        ++lineNumber;
538
539    }
540   
541    TextBase::computePositions();
542    computeBackdropBoundingBox();
543    computeColorGradients();
544}
545
546// Returns false if there are no glyphs and the width/height values are invalid.
547// Also sets avg_width and avg_height to 0.0f if the value is invalid.
548// This method is used several times in a loop for the same object which will produce the same values.
549// Further optimization may try saving these values instead of recomputing them.
550bool Text::computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height) const
551{
552    float width = 0.0f;
553    float height = 0.0f;
554    float running_width = 0.0f;
555    float running_height = 0.0f;
556    avg_width = 0.0f;
557    avg_height = 0.0f;
558    int counter = 0;
559    unsigned int i;
560    bool is_valid_size = true;
561    // This section is going to try to compute the average width and height
562    // for a character among the text. The reason I shift by an
563    // average amount per-character instead of shifting each character
564    // by its per-instance amount is because it may look strange to see
565    // the individual backdrop text letters not space themselves the same
566    // way the foreground text does. Using one value gives uniformity.
567    // Note: This loop is repeated for each context. I think it may produce
568    // the same values regardless of context. This code be optimized by moving
569    // this loop outside the loop.
570    for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
571        const_titr!=_textureGlyphQuadMap.end();
572        ++const_titr)
573    {
574        const GlyphQuads& glyphquad = const_titr->second;
575        const GlyphQuads::Coords2& coords2 = glyphquad._coords;
576        for(i = 0; i < coords2.size(); i+=4)
577        {
578            width = coords2[i+2].x() - coords2[i].x();
579            height = coords2[i].y() - coords2[i+1].y();
580
581            running_width += width;
582            running_height += height;
583            counter++;
584        }
585    }
586    if(0 == counter)
587    {
588        is_valid_size = false;
589    }
590    else
591    {
592        avg_width = running_width/counter;
593        avg_height = running_height/counter;
594    }
595    return is_valid_size;
596}
597
598
599void Text::computePositions(unsigned int contextID) const
600{
601    switch(_alignment)
602    {
603    case LEFT_TOP:      _offset.set(_textBB.xMin(),_textBB.yMax(),_textBB.zMin()); break;
604    case LEFT_CENTER:   _offset.set(_textBB.xMin(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
605    case LEFT_BOTTOM:   _offset.set(_textBB.xMin(),_textBB.yMin(),_textBB.zMin()); break;
606
607    case CENTER_TOP:    _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMax(),_textBB.zMin()); break;
608    case CENTER_CENTER: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
609    case CENTER_BOTTOM: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMin(),_textBB.zMin()); break;
610
611    case RIGHT_TOP:     _offset.set(_textBB.xMax(),_textBB.yMax(),_textBB.zMin()); break;
612    case RIGHT_CENTER:  _offset.set(_textBB.xMax(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
613    case RIGHT_BOTTOM:  _offset.set(_textBB.xMax(),_textBB.yMin(),_textBB.zMin()); break;
614
615    case LEFT_BASE_LINE:  _offset.set(0.0f,0.0f,0.0f); break;
616    case CENTER_BASE_LINE:  _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,0.0f,0.0f); break;
617    case RIGHT_BASE_LINE:  _offset.set(_textBB.xMax(),0.0f,0.0f); break;
618   
619    case LEFT_BOTTOM_BASE_LINE:  _offset.set(0.0f,-_characterHeight*(_lineCount-1),0.0f); break;
620    case CENTER_BOTTOM_BASE_LINE:  _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,-_characterHeight*(_lineCount-1),0.0f); break;
621    case RIGHT_BOTTOM_BASE_LINE:  _offset.set(_textBB.xMax(),-_characterHeight*(_lineCount-1),0.0f); break;
622    }
623   
624    AutoTransformCache& atc = _autoTransformCache[contextID];
625    osg::Matrix& matrix = atc._matrix;
626
627    if (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)
628    {
629
630        matrix.makeTranslate(-_offset);
631
632        osg::Matrix rotate_matrix;
633        if (_autoRotateToScreen)
634        {
635            osg::Vec3d trans(atc._modelview.getTrans());
636            atc._modelview.setTrans(0.0f,0.0f,0.0f);
637
638            rotate_matrix.invert(atc._modelview);
639
640            atc._modelview.setTrans(trans);
641        }
642
643        if (!_rotation.zeroRotation() )
644        {
645            matrix.postMult(osg::Matrix::rotate(_rotation));
646        }
647
648        if (_characterSizeMode!=OBJECT_COORDS)
649        {
650
651            osg::Matrix M(rotate_matrix*osg::Matrix::translate(_position)*atc._modelview);
652            osg::Matrix& P = atc._projection;
653           
654            // compute the pixel size vector.
655                       
656            // pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix.
657            // here we do it in short hand with the knowledge of how the window matrix is formed
658            // note P23,P33 are multiplied by an implicit 1 which would come from the window matrix.
659            // Robert Osfield, June 2002.
660
661            // scaling for horizontal pixels
662            float P00 = P(0,0)*atc._width*0.5f;
663            float P20_00 = P(2,0)*atc._width*0.5f + P(2,3)*atc._width*0.5f;
664            osg::Vec3 scale_00(M(0,0)*P00 + M(0,2)*P20_00,
665                               M(1,0)*P00 + M(1,2)*P20_00,
666                               M(2,0)*P00 + M(2,2)*P20_00);
667
668            // scaling for vertical pixels
669            float P10 = P(1,1)*atc._height*0.5f;
670            float P20_10 = P(2,1)*atc._height*0.5f + P(2,3)*atc._height*0.5f;
671            osg::Vec3 scale_10(M(0,1)*P10 + M(0,2)*P20_10,
672                               M(1,1)*P10 + M(1,2)*P20_10,
673                               M(2,1)*P10 + M(2,2)*P20_10);
674
675            float P23 = P(2,3);
676            float P33 = P(3,3);
677
678            float pixelSizeVector_w = M(3,2)*P23 + M(3,3)*P33;
679
680            float pixelSizeVert=(_characterHeight*sqrtf(scale_10.length2()))/(pixelSizeVector_w*0.701f);
681            float pixelSizeHori=(_characterHeight/_characterAspectRatio*sqrtf(scale_00.length2()))/(pixelSizeVector_w*0.701f);
682
683            // avoid nasty math by preventing a divide by zero
684            if (pixelSizeVert == 0.0f)
685               pixelSizeVert= 1.0f;
686            if (pixelSizeHori == 0.0f)
687               pixelSizeHori= 1.0f;
688
689            if (_characterSizeMode==SCREEN_COORDS)
690            {
691                float scale_font_vert=_characterHeight/pixelSizeVert;
692                float scale_font_hori=_characterHeight/_characterAspectRatio/pixelSizeHori;
693
694                if (P10<0)
695                   scale_font_vert=-scale_font_vert;
696                matrix.postMult(osg::Matrix::scale(scale_font_hori, scale_font_vert,1.0f));
697            }
698            else if (pixelSizeVert>getFontHeight())
699            {
700                float scale_font = getFontHeight()/pixelSizeVert;
701                matrix.postMult(osg::Matrix::scale(scale_font, scale_font,1.0f));
702            }
703
704        }
705
706        if (_autoRotateToScreen)
707        {
708            matrix.postMult(rotate_matrix);
709        }
710
711        matrix.postMult(osg::Matrix::translate(_position));
712    }
713    else if (!_rotation.zeroRotation())
714    {
715        matrix.makeTranslate(-_offset);
716        matrix.postMult(osg::Matrix::rotate(_rotation));
717        matrix.postMult(osg::Matrix::translate(_position));
718    }
719    else
720    {
721        matrix.makeTranslate(_position-_offset);
722    }
723
724    // now apply matrix to the glyphs.
725    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
726        titr!=_textureGlyphQuadMap.end();
727        ++titr)
728    {
729        GlyphQuads& glyphquad = titr->second;
730        GlyphQuads::Coords2& coords2 = glyphquad._coords;
731        GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
732       
733        unsigned int numCoords = coords2.size();
734        if (numCoords!=transformedCoords.size())
735        {
736            transformedCoords.resize(numCoords);
737        }
738       
739        for(unsigned int i=0;i<numCoords;++i)
740        {
741            transformedCoords[i] = osg::Vec3(coords2[i].x(),coords2[i].y(),0.0f)*matrix;
742        }
743    }
744
745    computeBackdropPositions(contextID);
746
747    _normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix);
748    _normal.normalize();
749
750    const_cast<Text*>(this)->dirtyBound();   
751}
752
753// Presumes the atc matrix is already up-to-date
754void Text::computeBackdropPositions(unsigned int contextID) const
755{
756    if(_backdropType == NONE)
757    {
758        return;
759    }
760
761    float avg_width = 0.0f;
762    float avg_height = 0.0f;
763    unsigned int i;
764    bool is_valid_size;
765   
766    AutoTransformCache& atc = _autoTransformCache[contextID];
767    osg::Matrix& matrix = atc._matrix;
768
769    // FIXME: OPTIMIZE: This function produces the same value regardless of contextID.
770    // Since we tend to loop over contextID, we should cache this value some how
771    // instead of recomputing it each time.
772    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
773   
774    // now apply matrix to the glyphs.
775    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
776        titr!=_textureGlyphQuadMap.end();
777        ++titr)
778    {
779        GlyphQuads& glyphquad = titr->second;
780        GlyphQuads::Coords2& coords2 = glyphquad._coords;
781
782        unsigned int backdrop_index;
783        unsigned int max_backdrop_index;
784        if(_backdropType == OUTLINE)
785        {
786            // For outline, we want to draw the in every direction
787            backdrop_index = 0;
788            max_backdrop_index = 8;
789        }
790        else
791        {
792            // Yes, this may seem a little strange,
793            // but since the code is using references,
794            // I would have to duplicate the following code twice
795            // for each part of the if/else because I can't
796            // declare a reference without setting it immediately
797            // and it wouldn't survive the scope.
798            // So it happens that the _backdropType value matches
799            // the index in the array I want to store the coordinates
800            // in. So I'll just setup the for-loop so it only does
801            // the one direction I'm interested in.
802            backdrop_index = _backdropType;
803            max_backdrop_index = _backdropType+1;
804        }
805        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
806        {
807            GlyphQuads::Coords3& transformedCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
808            unsigned int numCoords = coords2.size();
809            if (numCoords!=transformedCoords.size())
810            {
811                transformedCoords.resize(numCoords);
812            }
813
814            for(i=0;i<numCoords;++i)
815            {
816                float horizontal_shift_direction;
817                float vertical_shift_direction;
818                switch(backdrop_index)
819                {
820                    case DROP_SHADOW_BOTTOM_RIGHT:
821                        {
822                            horizontal_shift_direction = 1.0f;
823                            vertical_shift_direction = -1.0f;
824                            break;
825                        }
826                    case DROP_SHADOW_CENTER_RIGHT:
827                        {
828                            horizontal_shift_direction = 1.0f;
829                            vertical_shift_direction = 0.0f;
830                            break;
831                        }
832                    case DROP_SHADOW_TOP_RIGHT:
833                        {
834                            horizontal_shift_direction = 1.0f;
835                            vertical_shift_direction = 1.0f;
836                            break;
837                        }
838                    case DROP_SHADOW_BOTTOM_CENTER:
839                        {
840                            horizontal_shift_direction = 0.0f;
841                            vertical_shift_direction = -1.0f;
842                            break;
843                        }
844                    case DROP_SHADOW_TOP_CENTER:
845                        {
846                            horizontal_shift_direction = 0.0f;
847                            vertical_shift_direction = 1.0f;
848                            break;
849                        }                               
850                    case DROP_SHADOW_BOTTOM_LEFT:
851                        {
852                            horizontal_shift_direction = -1.0f;
853                            vertical_shift_direction = -1.0f;
854                            break;
855                        }
856                    case DROP_SHADOW_CENTER_LEFT:
857                        {
858                            horizontal_shift_direction = -1.0f;
859                            vertical_shift_direction = 0.0f;
860                            break;
861                        }
862                    case DROP_SHADOW_TOP_LEFT:
863                        {
864                            horizontal_shift_direction = -1.0f;
865                            vertical_shift_direction = 1.0f;
866                            break;
867                        }
868                    default: // error
869                        {
870                            horizontal_shift_direction = 1.0f;
871                            vertical_shift_direction = -1.0f;
872                        }
873                }
874                transformedCoords[i] = osg::Vec3(horizontal_shift_direction * _backdropHorizontalOffset * avg_width+coords2[i].x(),vertical_shift_direction * _backdropVerticalOffset * avg_height+coords2[i].y(),0.0f)*matrix;
875            }
876        }
877    }
878}
879
880// This method adjusts the bounding box to account for the expanded area caused by the backdrop.
881// This assumes that the bounding box has already been computed for the text without the backdrop.
882void Text::computeBackdropBoundingBox() const
883{
884    if(_backdropType == NONE)
885    {
886        return;
887    }
888
889    float avg_width = 0.0f;
890    float avg_height = 0.0f;
891    bool is_valid_size;
892   
893    // FIXME: OPTIMIZE: It is possible that this value has already been computed before
894    // from previous calls to this function. This might be worth optimizing.
895    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
896
897    // Finally, we have one more issue to deal with.
898    // Now that the text takes more space, we need
899    // to adjust the size of the bounding box.
900    if((!_textBB.valid() || !is_valid_size))
901    {
902        return;
903    }
904   
905    // Finally, we have one more issue to deal with.
906    // Now that the text takes more space, we need
907    // to adjust the size of the bounding box.
908    switch(_backdropType)
909    {
910        case DROP_SHADOW_BOTTOM_RIGHT:
911            {
912                _textBB.set(
913                    _textBB.xMin(),
914                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
915                    _textBB.zMin(),
916                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
917                    _textBB.yMax(),
918                    _textBB.zMax()
919                );
920                break;
921            }
922        case DROP_SHADOW_CENTER_RIGHT:
923            {
924                _textBB.set(
925                    _textBB.xMin(),
926                    _textBB.yMin(),
927                    _textBB.zMin(),
928                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
929                    _textBB.yMax(),
930                    _textBB.zMax()
931                );
932                break;
933            }
934        case DROP_SHADOW_TOP_RIGHT:
935            {
936                _textBB.set(
937                    _textBB.xMin(),
938                    _textBB.yMin(),
939                    _textBB.zMin(),
940                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
941                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
942                    _textBB.zMax()
943                );
944                break;
945            }
946        case DROP_SHADOW_BOTTOM_CENTER:
947            {
948                _textBB.set(
949                    _textBB.xMin(),
950                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
951                    _textBB.zMin(),
952                    _textBB.xMax(),
953                    _textBB.yMax(),
954                    _textBB.zMax()
955                );
956                break;
957            }
958        case DROP_SHADOW_TOP_CENTER:
959            {
960                _textBB.set(
961                    _textBB.xMin(),
962                    _textBB.yMin(),
963                    _textBB.zMin(),
964                    _textBB.xMax(),
965                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
966                    _textBB.zMax()
967                );
968                break;
969            }                               
970        case DROP_SHADOW_BOTTOM_LEFT:
971            {
972                _textBB.set(
973                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
974                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
975                    _textBB.zMin(),
976                    _textBB.xMax(),
977                    _textBB.yMax(),
978                    _textBB.zMax()
979                );
980                break;
981            }
982        case DROP_SHADOW_CENTER_LEFT:
983            {
984                _textBB.set(
985                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
986                    _textBB.yMin(),
987                    _textBB.zMin(),
988                    _textBB.xMax(),
989                    _textBB.yMax(),
990                    _textBB.zMax()
991                );            break;
992            }
993        case DROP_SHADOW_TOP_LEFT:
994            {
995                _textBB.set(
996                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
997                    _textBB.yMin(),
998                    _textBB.zMin(),
999                    _textBB.xMax(),
1000                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
1001                    _textBB.zMax()
1002                );
1003                break;
1004            }
1005        case OUTLINE:
1006            {
1007                _textBB.set(
1008                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
1009                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
1010                    _textBB.zMin(),
1011                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
1012                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
1013                    _textBB.zMax()
1014                );
1015                break;
1016            }
1017        default: // error
1018            {
1019                break;
1020            }
1021    }
1022}
1023
1024void Text::computeColorGradients() const
1025{
1026    switch(_colorGradientMode)
1027    {
1028        case SOLID:
1029            return;
1030            break;
1031        case PER_CHARACTER:
1032            computeColorGradientsPerCharacter();
1033            break;
1034        case OVERALL:
1035            computeColorGradientsOverall();
1036            break;
1037        default:
1038            break;
1039    }
1040}
1041
1042void Text::computeColorGradientsOverall() const
1043{
1044
1045    float min_x = FLT_MAX;
1046    float min_y = FLT_MAX;
1047    float max_x = FLT_MIN;
1048    float max_y = FLT_MIN;
1049
1050    float rgb_q11[3];
1051    float hsv_q11[3];
1052    float rgb_q12[3];
1053    float hsv_q12[3];
1054    float rgb_q21[3];
1055    float hsv_q21[3];
1056    float rgb_q22[3];
1057    float hsv_q22[3];
1058
1059    float rgb[3];
1060    float hsv[3];
1061    unsigned int i;
1062
1063    for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
1064        const_titr!=_textureGlyphQuadMap.end();
1065        ++const_titr)
1066    {
1067        const GlyphQuads& glyphquad = const_titr->second;
1068        const GlyphQuads::Coords2& coords2 = glyphquad._coords;
1069
1070        for(i=0;i<coords2.size();++i)
1071        { 
1072            // Min and Max are needed for color gradients
1073            if(coords2[i].x() > max_x)
1074            {
1075                max_x = coords2[i].x();
1076            }
1077            if(coords2[i].x() < min_x)
1078            {
1079                min_x = coords2[i].x();
1080            }
1081            if(coords2[i].y() > max_y)
1082            {
1083                max_y = coords2[i].y();
1084            }
1085            if(coords2[i].y() < min_y)
1086            {
1087                min_y = coords2[i].y();
1088            }       
1089
1090        }
1091    }
1092
1093    rgb_q11[0] = _colorGradientBottomLeft[0];
1094    rgb_q11[1] = _colorGradientBottomLeft[1];
1095    rgb_q11[2] = _colorGradientBottomLeft[2];
1096
1097    rgb_q12[0] = _colorGradientTopLeft[0];
1098    rgb_q12[1] = _colorGradientTopLeft[1];
1099    rgb_q12[2] = _colorGradientTopLeft[2];
1100
1101    rgb_q21[0] = _colorGradientBottomRight[0];
1102    rgb_q21[1] = _colorGradientBottomRight[1];
1103    rgb_q21[2] = _colorGradientBottomRight[2];
1104
1105    rgb_q22[0] = _colorGradientTopRight[0];
1106    rgb_q22[1] = _colorGradientTopRight[1];
1107    rgb_q22[2] = _colorGradientTopRight[2];
1108
1109    // for linear interpolation to look correct
1110    // for colors and imitate what OpenGL does,
1111    // we need to convert over to Hue-Saturation-Value
1112    // and linear interpolate in that space.
1113    // HSV will interpolate through the color spectrum.
1114    // Now that I think about this, perhaps we could
1115    // extend this to use function pointers or something
1116    // so users may specify their own color interpolation
1117    // scales such as Intensity, or Heated Metal, etc.
1118    convertRgbToHsv(rgb_q11, hsv_q11);
1119    convertRgbToHsv(rgb_q12, hsv_q12);
1120    convertRgbToHsv(rgb_q21, hsv_q21);
1121    convertRgbToHsv(rgb_q22, hsv_q22);
1122
1123    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1124        titr!=_textureGlyphQuadMap.end();
1125        ++titr)
1126    {
1127        GlyphQuads& glyphquad = titr->second;
1128        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1129        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1130
1131        unsigned int numCoords = coords2.size();
1132        if (numCoords!=colorCoords.size())
1133        {
1134            colorCoords.resize(numCoords);
1135        }
1136
1137        for(i=0;i<numCoords;++i)
1138        {
1139            float hue = bilinearInterpolate(
1140                min_x,
1141                max_x,
1142                min_y,
1143                max_y,
1144                coords2[i].x(),
1145                coords2[i].y(),
1146                hsv_q11[0],
1147                hsv_q12[0],
1148                hsv_q21[0],
1149                hsv_q22[0]
1150            );
1151
1152            float saturation = bilinearInterpolate(
1153                min_x,
1154                max_x,
1155                min_y,
1156                max_y,
1157                coords2[i].x(),
1158                coords2[i].y(),
1159                hsv_q11[1],
1160                hsv_q12[1],
1161                hsv_q21[1],
1162                hsv_q22[1]
1163            );
1164
1165            float value = bilinearInterpolate(
1166                min_x,
1167                max_x,
1168                min_y,
1169                max_y,
1170                coords2[i].x(),
1171                coords2[i].y(),
1172                hsv_q11[2],
1173                hsv_q12[2],
1174                hsv_q21[2],
1175                hsv_q22[2]
1176            );
1177            // Alpha does not convert to HSV           
1178            float alpha = bilinearInterpolate(
1179                min_x,
1180                max_x,
1181                min_y,
1182                max_y,
1183                coords2[i].x(),
1184                coords2[i].y(),
1185                _colorGradientBottomLeft[3],
1186                _colorGradientTopLeft[3],
1187                _colorGradientBottomRight[3],
1188                _colorGradientTopRight[3]
1189            );                                   
1190
1191            hsv[0] = hue;
1192            hsv[1] = saturation;
1193            hsv[2] = value;
1194            // Convert back to RGB
1195            convertHsvToRgb(hsv, rgb);
1196            colorCoords[i] = osg::Vec4(rgb[0],rgb[1],rgb[2],alpha);
1197        }
1198    }
1199}
1200
1201void Text::computeColorGradientsPerCharacter() const
1202{
1203    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1204        titr!=_textureGlyphQuadMap.end();
1205        ++titr)
1206    {
1207        GlyphQuads& glyphquad = titr->second;
1208        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1209        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1210
1211        unsigned int numCoords = coords2.size();
1212        if (numCoords!=colorCoords.size())
1213        {
1214            colorCoords.resize(numCoords);
1215        }
1216
1217        for(unsigned int i=0;i<numCoords;++i)
1218        {
1219            switch(i%4)
1220            {
1221                case 0: // top-left
1222                    {
1223                        colorCoords[i] = _colorGradientTopLeft;
1224                        break;
1225                    }
1226                case 1: // bottom-left
1227                    {
1228                        colorCoords[i] = _colorGradientBottomLeft;
1229                        break;
1230                    }
1231                case 2: // bottom-right
1232                    {
1233                        colorCoords[i] = _colorGradientBottomRight;
1234                        break;
1235                    }
1236                case 3: // top-right
1237                    {
1238                        colorCoords[i] = _colorGradientTopRight;
1239                        break;
1240                    }
1241                default: // error
1242                    {
1243                        colorCoords[i] = osg::Vec4(0.0f,0.0f,0.0f,1.0f);
1244                    }
1245            }
1246        }
1247    }
1248}
1249
1250void Text::drawImplementation(osg::RenderInfo& renderInfo) const
1251{
1252    drawImplementation(*renderInfo.getState(), osg::Vec4(1.0f,1.0f,1.0f,1.0f));
1253}
1254
1255void Text::drawImplementation(osg::State& state, const osg::Vec4& colorMultiplier) const
1256{
1257    unsigned int contextID = state.getContextID();
1258
1259    state.applyMode(GL_BLEND,true);
1260#if 1   
1261    state.applyTextureMode(0,GL_TEXTURE_2D,true);
1262#else
1263    state.applyTextureMode(0,GL_TEXTURE_2D,false);
1264#endif
1265    state.applyTextureAttribute(0,getActiveFont()->getTexEnv());
1266
1267    if (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)
1268    {
1269        int frameNumber = state.getFrameStamp()?state.getFrameStamp()->getFrameNumber():0;
1270        AutoTransformCache& atc = _autoTransformCache[contextID];
1271        const osg::Matrix& modelview = state.getModelViewMatrix();
1272        const osg::Matrix& projection = state.getProjectionMatrix();
1273
1274        osg::Vec3 newTransformedPosition = _position*modelview;
1275
1276        int width = atc._width;
1277        int height = atc._height;
1278
1279        const osg::Viewport* viewport = state.getCurrentViewport();
1280        if (viewport)
1281        {
1282            width = static_cast<int>(viewport->width());
1283            height = static_cast<int>(viewport->height());
1284        }
1285
1286        bool doUpdate = atc._traversalNumber==-1;
1287        if (atc._traversalNumber>=0)
1288        {
1289            if (atc._modelview!=modelview)
1290            {
1291                doUpdate = true;
1292            }
1293            else if (width!=atc._width || height!=atc._height)
1294            {
1295                doUpdate = true;
1296            }
1297            else if (atc._projection!=projection)
1298            {
1299                doUpdate = true;
1300            }
1301        }
1302       
1303        atc._traversalNumber = frameNumber;
1304        atc._width = width;
1305        atc._height = height;
1306       
1307        if (doUpdate)
1308        {   
1309            atc._transformedPosition = newTransformedPosition;
1310            atc._projection = projection;
1311            atc._modelview = modelview;
1312
1313            computePositions(contextID);
1314        }
1315       
1316    }
1317   
1318   
1319    // Ensure that the glyph coordinates have been transformed for
1320    // this context id.
1321
1322    if ( !_textureGlyphQuadMap.empty() )
1323    {
1324        const GlyphQuads& glyphquad = (_textureGlyphQuadMap.begin())->second;
1325        if ( glyphquad._transformedCoords[contextID].empty() )
1326        {
1327            computePositions(contextID);
1328        }
1329    }
1330
1331    glNormal3fv(_normal.ptr());
1332
1333    if (_drawMode & TEXT)
1334    {
1335
1336        state.disableAllVertexArrays();
1337
1338        // Okay, since ATI's cards/drivers are not working correctly,
1339        // we need alternative solutions to glPolygonOffset.
1340        // So this is a pick your poison approach. Each alternative
1341        // backend has trade-offs associated with it, but with luck,
1342        // the user may find that works for them.
1343        if(_backdropType != NONE)
1344        {
1345            switch(_backdropImplementation)
1346            {
1347                case POLYGON_OFFSET:
1348                    renderWithPolygonOffset(state,colorMultiplier);
1349                    break;
1350                case NO_DEPTH_BUFFER:
1351                    renderWithNoDepthBuffer(state,colorMultiplier);
1352                    break;
1353                case DEPTH_RANGE:
1354                    renderWithDepthRange(state,colorMultiplier);
1355                    break;
1356                case STENCIL_BUFFER:
1357                    renderWithStencilBuffer(state,colorMultiplier);
1358                    break;
1359                default:
1360                    renderWithPolygonOffset(state,colorMultiplier);
1361            }
1362        }
1363        else
1364        {
1365            renderOnlyForegroundText(state,colorMultiplier);
1366        }
1367    }
1368
1369    if (_drawMode & BOUNDINGBOX)
1370    {
1371
1372        if (_textBB.valid())
1373        {
1374            state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1375
1376            const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1377
1378            osg::Vec3 c00(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix);
1379            osg::Vec3 c10(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix);
1380            osg::Vec3 c11(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix);
1381            osg::Vec3 c01(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix);
1382
1383       
1384            glColor4fv(colorMultiplier.ptr());
1385            glBegin(GL_LINE_LOOP);
1386                glVertex3fv(c00.ptr());
1387                glVertex3fv(c10.ptr());
1388                glVertex3fv(c11.ptr());
1389                glVertex3fv(c01.ptr());
1390            glEnd();
1391        }
1392    }   
1393
1394    if (_drawMode & ALIGNMENT)
1395    {
1396        glColor4fv(colorMultiplier.ptr());
1397
1398        float cursorsize = _characterHeight*0.5f;
1399
1400        const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1401
1402        osg::Vec3 hl(osg::Vec3(_offset.x()-cursorsize,_offset.y(),_offset.z())*matrix);
1403        osg::Vec3 hr(osg::Vec3(_offset.x()+cursorsize,_offset.y(),_offset.z())*matrix);
1404        osg::Vec3 vt(osg::Vec3(_offset.x(),_offset.y()-cursorsize,_offset.z())*matrix);
1405        osg::Vec3 vb(osg::Vec3(_offset.x(),_offset.y()+cursorsize,_offset.z())*matrix);
1406
1407        state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1408       
1409        glBegin(GL_LINES);
1410            glVertex3fv(hl.ptr());
1411            glVertex3fv(hr.ptr());
1412            glVertex3fv(vt.ptr());
1413            glVertex3fv(vb.ptr());
1414        glEnd();
1415       
1416    }   
1417}
1418
1419void Text::accept(osg::Drawable::ConstAttributeFunctor& af) const
1420{
1421    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1422        titr!=_textureGlyphQuadMap.end();
1423        ++titr)
1424    {
1425        const GlyphQuads& glyphquad = titr->second;
1426        af.apply(osg::Drawable::VERTICES,glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1427        af.apply(osg::Drawable::TEXTURE_COORDS_0,glyphquad._texcoords.size(),&(glyphquad._texcoords.front()));
1428    }
1429}
1430
1431void Text::accept(osg::PrimitiveFunctor& pf) const
1432{
1433    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1434        titr!=_textureGlyphQuadMap.end();
1435        ++titr)
1436    {
1437        const GlyphQuads& glyphquad = titr->second;
1438
1439        pf.setVertexArray(glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1440        pf.drawArrays(GL_QUADS,0,glyphquad._transformedCoords[0].size());
1441           
1442    }
1443   
1444}
1445
1446
1447void Text::setThreadSafeRefUnref(bool threadSafe)
1448{
1449    TextBase::setThreadSafeRefUnref(threadSafe);
1450
1451    getActiveFont()->setThreadSafeRefUnref(threadSafe);
1452}
1453
1454void Text::resizeGLObjectBuffers(unsigned int maxSize)
1455{
1456    TextBase::resizeGLObjectBuffers(maxSize);
1457   
1458    getActiveFont()->resizeGLObjectBuffers(maxSize);
1459}
1460
1461
1462void Text::releaseGLObjects(osg::State* state) const
1463{
1464    TextBase::releaseGLObjects(state);
1465    getActiveFont()->releaseGLObjects(state);
1466}
1467
1468
1469void Text::setBackdropType(BackdropType type)
1470{
1471    if (_backdropType==type) return;
1472
1473    _backdropType = type;
1474    computeGlyphRepresentation();
1475}
1476
1477void Text::setBackdropImplementation(BackdropImplementation implementation)
1478{
1479    if (_backdropImplementation==implementation) return;
1480
1481    _backdropImplementation = implementation;
1482    computeGlyphRepresentation();
1483}
1484
1485
1486void Text::setBackdropOffset(float offset)
1487{
1488    _backdropHorizontalOffset = offset;
1489    _backdropVerticalOffset = offset;
1490    computeGlyphRepresentation();
1491}
1492
1493void Text::setBackdropOffset(float horizontal, float vertical)
1494{
1495    _backdropHorizontalOffset = horizontal;
1496    _backdropVerticalOffset = vertical;
1497    computeGlyphRepresentation();
1498}
1499
1500void Text::setBackdropColor(const osg::Vec4& color)
1501{
1502    _backdropColor = color;
1503}
1504
1505void Text::setColorGradientMode(ColorGradientMode mode)
1506{
1507    if (_colorGradientMode==mode) return;
1508
1509    _colorGradientMode = mode;
1510    computeGlyphRepresentation();
1511}
1512
1513void Text::setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight)
1514{
1515    _colorGradientTopLeft = topLeft;
1516    _colorGradientBottomLeft = bottomLeft;
1517    _colorGradientBottomRight = bottomRight;
1518    _colorGradientTopRight = topRight;
1519    computeGlyphRepresentation();
1520}
1521
1522// Formula for f(x,y) from Wikipedia "Bilinear interpolation", 2006-06-18
1523float Text::bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const
1524{
1525    return (
1526        ((q11 / ((x2-x1)*(y2-y1))) * (x2-x)*(y2-y))
1527        + ((q21 / ((x2-x1)*(y2-y1))) * (x-x1)*(y2-y))
1528        + ((q12 / ((x2-x1)*(y2-y1))) * (x2-x)*(y-y1))
1529        + ((q22 / ((x2-x1)*(y2-y1))) * (x-x1)*(y-y1))
1530    );
1531}
1532
1533
1534/**
1535 ** routines to convert between RGB and HSV
1536 **
1537 ** Reference:  Foley, van Dam, Feiner, Hughes,
1538 **        "Computer Graphics Principles and Practices,"
1539 **        Additon-Wesley, 1990, pp592-593.
1540 **/       
1541/*
1542 *  FUNCTION
1543 *    HsvRgb( hsv, rgb )
1544 *
1545 *  DESCRIPTION
1546 *    convert a hue-saturation-value into a red-green-blue value
1547 *
1548 *    NOTE
1549 *    Array sizes are 3
1550 *    Values are between 0.0 and 1.0
1551 */
1552
1553void Text::convertHsvToRgb( float hsv[], float rgb[] ) const
1554{
1555    float h, s, v;            /* hue, sat, value        */
1556    /*    double delta;    */        /* change in color value    */
1557    float r, g, b;            /* red, green, blue        */
1558    float i, f, p, q, t;        /* interim values        */
1559
1560
1561    /* guarantee valid input:                    */
1562
1563    h = hsv[0] / 60.f;
1564    while( h >= 6.f )    h -= 6.f;
1565    while( h <  0.f )     h += 6.f;
1566
1567    s = hsv[1];
1568    if( s < 0.f )
1569        s = 0.f;
1570    if( s > 1.f )
1571        s = 1.f;
1572
1573    v = hsv[2];
1574    if( v < 0.f )
1575        v = 0.f;
1576    if( v > 1.f )
1577        v = 1.f;
1578
1579
1580    /* if sat==0, then is a gray:                    */
1581
1582    if( s == 0.0f )
1583    {
1584        rgb[0] = rgb[1] = rgb[2] = v;
1585        return;
1586    }
1587
1588
1589    /* get an rgb from the hue itself:                */
1590
1591    i = floor( h );
1592    f = h - i;
1593    p = v * ( 1.f - s );
1594    q = v * ( 1.f - s*f );
1595    t = v * ( 1.f - ( s * (1.f-f) ) );
1596
1597    switch( (int) i )
1598    {
1599        case 0:
1600            r = v;    g = t;    b = p;
1601            break;
1602
1603        case 1:
1604            r = q;    g = v;    b = p;
1605            break;
1606
1607        case 2:
1608            r = p;    g = v;    b = t;
1609            break;
1610
1611        case 3:
1612            r = p;    g = q;    b = v;
1613            break;
1614
1615        case 4:
1616            r = t;    g = p;    b = v;
1617            break;
1618
1619        case 5:
1620            r = v;    g = p;    b = q;
1621            break;
1622
1623        default:
1624            /* never happens? */
1625            r = 0;  g = 0;  b = 0;
1626            break;
1627    }
1628
1629
1630    rgb[0] = r;
1631    rgb[1] = g;
1632    rgb[2] = b;
1633
1634}
1635
1636/*
1637 *  FUNCTION
1638 *    RgbHsv
1639 *
1640 *  DESCRIPTION
1641 *    convert a red-green-blue value into hue-saturation-value
1642 *
1643 *    NOTE
1644 *    Array sizes are 3
1645 *    Values are between 0.0 and 1.0
1646 */
1647
1648void Text::convertRgbToHsv( float rgb[], float hsv[] ) const
1649{
1650    float r, g, b;            /* red, green, blue        */
1651    float min, max;            /* min and max rgb values    */
1652    float fmin, fmax, diff;        /* min, max, and range of rgb vals */
1653    float hue, sat, value;        /* h s v            */
1654    float cr, cg, cb;        /* coefficients for computing hue */
1655
1656
1657    /* determine min and max color primary values:            */
1658
1659    r = rgb[0];    g = rgb[1];    b = rgb[2];
1660    min = r;    max = r;
1661    if( g < min ) min = g;
1662    if( g > max ) max = g;
1663    if( b < min ) min = b;
1664    if( b > max ) max = b;
1665
1666    fmin = min;
1667    fmax = max;
1668    diff = fmax - fmin;
1669
1670
1671    /* get value and saturation:                    */
1672
1673    value = fmax;
1674    if( max == 0.f )
1675        sat = 0.0f;
1676    else
1677        sat = diff/fmax;
1678
1679
1680
1681    /* compute hue:                            */
1682
1683    if( sat == 0.0f )
1684        hue = 0.0f;
1685    else
1686    {
1687        float inv_diff = 1.0f / diff;
1688        cr = ( fmax-r ) * inv_diff;
1689        cg = ( fmax-g ) * inv_diff;
1690        cb = ( fmax-b ) * inv_diff;
1691
1692        if( max == r )
1693            hue =      (g-b) * inv_diff;
1694        else if( max == g )
1695            hue = 2.f + (b-r) * inv_diff;
1696        else if( max == b )
1697            hue = 4.f + (r-g) * inv_diff;
1698        else
1699            hue = 0.0f;
1700    }
1701
1702
1703    hue *= 60.0f;
1704    if( hue < 0.0f )
1705        hue += 360.0f;
1706    if( hue > 360.0f )
1707        hue -= 360.0f;
1708
1709
1710    /* store output values:                        */
1711
1712    hsv[0] = hue;
1713    hsv[1] = sat;
1714    hsv[2] = value;
1715
1716}
1717
1718void Text::drawForegroundText(osg::State& state, const GlyphQuads& glyphquad, const osg::Vec4& colorMultiplier) const
1719{
1720    unsigned int contextID = state.getContextID();
1721
1722    const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
1723    if (!transformedCoords.empty())
1724    {
1725        state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
1726        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1727
1728        if(_colorGradientMode == SOLID)
1729        {
1730            state.disableColorPointer();
1731            glColor4f(colorMultiplier.r()*_color.r(),colorMultiplier.g()*_color.g(),colorMultiplier.b()*_color.b(),colorMultiplier.a()*_color.a());
1732        }
1733        else
1734        {
1735            state.setColorPointer( 4, GL_FLOAT, 0, &(glyphquad._colorCoords.front()));
1736        }
1737
1738        glDrawArrays(GL_QUADS,0,transformedCoords.size());
1739
1740    }
1741}
1742
1743void Text::renderOnlyForegroundText(osg::State& state, const osg::Vec4& colorMultiplier) const
1744{
1745    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1746        titr!=_textureGlyphQuadMap.end();
1747        ++titr)
1748    {
1749        // need to set the texture here...
1750        state.applyTextureAttribute(0,titr->first.get());
1751
1752        const GlyphQuads& glyphquad = titr->second;
1753
1754        drawForegroundText(state, glyphquad, colorMultiplier);
1755    }
1756
1757}
1758
1759
1760void Text::renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMultiplier) const
1761{
1762    unsigned int contextID = state.getContextID();
1763
1764
1765    if (!osg::PolygonOffset::areFactorAndUnitsMultipliersSet())
1766    {
1767        osg::PolygonOffset::setFactorAndUnitsMultipliersUsingBestGuessForDriver();
1768    }
1769
1770    // Do I really need to do this for glPolygonOffset?
1771    glPushAttrib(GL_POLYGON_OFFSET_FILL);
1772    glEnable(GL_POLYGON_OFFSET_FILL);
1773
1774    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1775        titr!=_textureGlyphQuadMap.end();
1776        ++titr)
1777    {
1778        // need to set the texture here...
1779        state.applyTextureAttribute(0,titr->first.get());
1780
1781        const GlyphQuads& glyphquad = titr->second;
1782
1783        unsigned int backdrop_index;
1784        unsigned int max_backdrop_index;
1785        if(_backdropType == OUTLINE)
1786        {
1787            backdrop_index = 0;
1788            max_backdrop_index = 8;
1789        }
1790        else
1791        {
1792            backdrop_index = _backdropType;
1793            max_backdrop_index = _backdropType+1;
1794        }
1795
1796        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1797        state.disableColorPointer();
1798        glColor4fv(_backdropColor.ptr());
1799
1800        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1801        {
1802            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1803            if (!transformedBackdropCoords.empty())
1804            {
1805                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1806                glPolygonOffset(0.1f * osg::PolygonOffset::getFactorMultiplier(),
1807                                2.0f * osg::PolygonOffset::getUnitsMultiplier() * (max_backdrop_index-backdrop_index) );
1808                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1809            }
1810        }
1811
1812        // Reset the polygon offset so the foreground text is on top
1813        glPolygonOffset(0.0f,0.0f);
1814
1815        drawForegroundText(state, glyphquad, colorMultiplier);
1816    }
1817
1818    glPopAttrib();
1819}
1820   
1821
1822void Text::renderWithNoDepthBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1823{
1824    unsigned int contextID = state.getContextID();
1825
1826    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1827    glDisable(GL_DEPTH_TEST);
1828
1829    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1830        titr!=_textureGlyphQuadMap.end();
1831        ++titr)
1832    {
1833        // need to set the texture here...
1834        state.applyTextureAttribute(0,titr->first.get());
1835
1836        const GlyphQuads& glyphquad = titr->second;
1837
1838        unsigned int backdrop_index;
1839        unsigned int max_backdrop_index;
1840        if(_backdropType == OUTLINE)
1841        {
1842            backdrop_index = 0;
1843            max_backdrop_index = 8;
1844        }
1845        else
1846        {
1847            backdrop_index = _backdropType;
1848            max_backdrop_index = _backdropType+1;
1849        }
1850
1851        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1852        state.disableColorPointer();
1853        glColor4fv(_backdropColor.ptr());
1854
1855        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1856        {
1857            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1858            if (!transformedBackdropCoords.empty())
1859            {
1860                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1861                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1862            }
1863        }
1864
1865        drawForegroundText(state, glyphquad, colorMultiplier);
1866    }
1867
1868    glPopAttrib();
1869}
1870
1871// This idea comes from Paul Martz's OpenGL FAQ: 13.050
1872void Text::renderWithDepthRange(osg::State& state, const osg::Vec4& colorMultiplier) const
1873{
1874    unsigned int contextID = state.getContextID();
1875
1876    // Hmmm, the man page says GL_VIEWPORT_BIT for Depth range (near and far)
1877    // but experimentally, GL_DEPTH_BUFFER_BIT for glDepthRange.
1878//    glPushAttrib(GL_VIEWPORT_BIT);
1879    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1880
1881    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1882        titr!=_textureGlyphQuadMap.end();
1883        ++titr)
1884    {
1885        // need to set the texture here...
1886        state.applyTextureAttribute(0,titr->first.get());
1887
1888        const GlyphQuads& glyphquad = titr->second;
1889
1890        unsigned int backdrop_index;
1891        unsigned int max_backdrop_index;
1892        if(_backdropType == OUTLINE)
1893        {
1894            backdrop_index = 0;
1895            max_backdrop_index = 8;
1896        }
1897        else
1898        {
1899            backdrop_index = _backdropType;
1900            max_backdrop_index = _backdropType+1;
1901        }
1902
1903        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1904        state.disableColorPointer();
1905        glColor4fv(_backdropColor.ptr());
1906
1907        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1908        {
1909            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1910            if (!transformedBackdropCoords.empty())
1911            {
1912                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1913                double offset = double(max_backdrop_index-backdrop_index)*0.003;
1914                glDepthRange( offset, 1.0+offset);
1915
1916                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1917            }
1918        }
1919
1920        glDepthRange(0.0, 1.0);
1921
1922        drawForegroundText(state, glyphquad, colorMultiplier);
1923    }
1924
1925    glPopAttrib();
1926}
1927
1928void Text::renderWithStencilBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1929{
1930    /* Here are the steps:
1931     * 1) Disable drawing color
1932     * 2) Enable the stencil buffer
1933     * 3) Draw all the text to the stencil buffer
1934     * 4) Disable the stencil buffer
1935     * 5) Enable color
1936     * 6) Disable the depth buffer
1937     * 7) Draw all the text again.
1938     * 7b) Make sure the foreground text is drawn last if priority levels
1939     * are the same OR
1940     * 7c) If priority levels are different, then make sure the foreground
1941     * text has the higher priority.
1942     */
1943    unsigned int contextID = state.getContextID();
1944    TextureGlyphQuadMap::iterator titr; // Moved up here for VC6
1945   
1946    glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_TEST);
1947
1948    // It seems I can get away without calling this here
1949    //glClear(GL_STENCIL_BUFFER_BIT);
1950
1951    // enable stencil buffer
1952    glEnable(GL_STENCIL_TEST);
1953
1954    // write a one to the stencil buffer everywhere we are about to draw
1955    glStencilFunc(GL_ALWAYS, 1, 1);
1956
1957    // write only to the stencil buffer if we pass the depth test
1958    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1959
1960    // Disable writing to the color buffer so we only write to the stencil
1961    // buffer and the depth buffer
1962    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1963
1964    // make sure the depth buffer is enabled
1965//    glEnable(GL_DEPTH_TEST);
1966//    glDepthMask(GL_TRUE);
1967//    glDepthFunc(GL_LESS);
1968
1969    // Arrrgh! Why does the code only seem to work correctly if I call this?
1970    glDepthMask(GL_FALSE);
1971   
1972
1973    // Draw all the text to the stencil buffer to mark out the region
1974    // that we can write too.
1975   
1976    for(titr=_textureGlyphQuadMap.begin();
1977        titr!=_textureGlyphQuadMap.end();
1978        ++titr)
1979    {
1980        // need to set the texture here...
1981        state.applyTextureAttribute(0,titr->first.get());
1982
1983        const GlyphQuads& glyphquad = titr->second;
1984
1985        unsigned int backdrop_index;
1986        unsigned int max_backdrop_index;
1987        if(_backdropType == OUTLINE)
1988        {
1989            backdrop_index = 0;
1990            max_backdrop_index = 8;
1991        }
1992        else
1993        {
1994            backdrop_index = _backdropType;
1995            max_backdrop_index = _backdropType+1;
1996        }
1997
1998        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1999        state.disableColorPointer();
2000
2001        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
2002        {
2003            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
2004            if (!transformedBackdropCoords.empty())
2005            {
2006                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
2007                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
2008            }
2009        }
2010
2011        // Draw the foreground text
2012        const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
2013        if (!transformedCoords.empty())
2014        {
2015            state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
2016            state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
2017            glDrawArrays(GL_QUADS,0,transformedCoords.size());
2018        }
2019    }
2020
2021
2022    // disable the depth buffer
2023//    glDisable(GL_DEPTH_TEST);
2024//    glDepthMask(GL_FALSE);
2025//    glDepthMask(GL_TRUE);
2026//    glDepthFunc(GL_ALWAYS);
2027
2028    // Set the stencil function to pass when the stencil is 1
2029    // Bug: This call seems to have no effect. Try changing to NOTEQUAL
2030    // and see the exact same results.
2031    glStencilFunc(GL_EQUAL, 1, 1);
2032
2033    // disable writing to the stencil buffer
2034    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2035    glStencilMask(GL_FALSE);
2036
2037    // Re-enable writing to the color buffer so we can see the results
2038    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2039
2040
2041    // Draw all the text again
2042
2043    for(titr=_textureGlyphQuadMap.begin();
2044        titr!=_textureGlyphQuadMap.end();
2045        ++titr)
2046    {
2047        // need to set the texture here...
2048        state.applyTextureAttribute(0,titr->first.get());
2049
2050        const GlyphQuads& glyphquad = titr->second;
2051
2052        unsigned int backdrop_index;
2053        unsigned int max_backdrop_index;
2054        if(_backdropType == OUTLINE)
2055        {
2056            backdrop_index = 0;
2057            max_backdrop_index = 8;
2058        }
2059        else
2060        {
2061            backdrop_index = _backdropType;
2062            max_backdrop_index = _backdropType+1;
2063        }
2064
2065        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
2066        state.disableColorPointer();
2067        glColor4fv(_backdropColor.ptr());
2068
2069        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
2070        {
2071            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
2072            if (!transformedBackdropCoords.empty())
2073            {
2074                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
2075                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
2076            }
2077        }
2078
2079        drawForegroundText(state, glyphquad, colorMultiplier);
2080    }
2081   
2082    glPopAttrib();
2083}
2084
2085
Note: See TracBrowser for help on using the browser.