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

Revision 8868, 66.4 kB (checked in by robert, 6 years ago)

From Mathias Froehlich, "This is a generic optimization that does not depend on any cpu or instruction
set.

The optimization is based on the observation that matrix matrix multiplication
with a dense matrix 4x4 is 43 Operations whereas multiplication with a
transform, or scale matrix is only 4
2 operations. Which is a gain of a
*FACTOR*4* for these special cases.
The change implements these special cases, provides a unit test for these
implementation and converts uses of the expensiver dense matrix matrix
routine with the specialized versions.

Depending on the transform nodes in the scenegraph this change gives a
noticable improovement.
For example the osgforest code using the MatrixTransform? is about 20% slower
than the same codepath using the PositionAttitudeTransform? instead of the
MatrixTransform? with this patch applied.

If I remember right, the sse type optimizations did *not* provide a factor 4
improovement. Also these changes are totally independent of any cpu or
instruction set architecture. So I would prefer to have this current kind of
change instead of some hand coded and cpu dependent assembly stuff. If we
need that hand tuned stuff, these can go on top of this changes which must
provide than hand optimized additional variants for the specialized versions
to give a even better result in the end.

An other change included here is a change to rotation matrix from quaterion
code. There is a sqrt call which couold be optimized away. Since we divide in
effect by sqrt(length)*sqrt(length) which is just length ...
"

  • 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        matrix.postMultRotate(_rotation);
644
645        if (_characterSizeMode!=OBJECT_COORDS)
646        {
647
648            osg::Matrix M(rotate_matrix);
649            M.postMultTranslate(_position);
650            M.postMult(atc._modelview);
651            osg::Matrix& P = atc._projection;
652           
653            // compute the pixel size vector.
654                       
655            // pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix.
656            // here we do it in short hand with the knowledge of how the window matrix is formed
657            // note P23,P33 are multiplied by an implicit 1 which would come from the window matrix.
658            // Robert Osfield, June 2002.
659
660            // scaling for horizontal pixels
661            float P00 = P(0,0)*atc._width*0.5f;
662            float P20_00 = P(2,0)*atc._width*0.5f + P(2,3)*atc._width*0.5f;
663            osg::Vec3 scale_00(M(0,0)*P00 + M(0,2)*P20_00,
664                               M(1,0)*P00 + M(1,2)*P20_00,
665                               M(2,0)*P00 + M(2,2)*P20_00);
666
667            // scaling for vertical pixels
668            float P10 = P(1,1)*atc._height*0.5f;
669            float P20_10 = P(2,1)*atc._height*0.5f + P(2,3)*atc._height*0.5f;
670            osg::Vec3 scale_10(M(0,1)*P10 + M(0,2)*P20_10,
671                               M(1,1)*P10 + M(1,2)*P20_10,
672                               M(2,1)*P10 + M(2,2)*P20_10);
673
674            float P23 = P(2,3);
675            float P33 = P(3,3);
676
677            float pixelSizeVector_w = M(3,2)*P23 + M(3,3)*P33;
678
679            float pixelSizeVert=(_characterHeight*sqrtf(scale_10.length2()))/(pixelSizeVector_w*0.701f);
680            float pixelSizeHori=(_characterHeight/_characterAspectRatio*sqrtf(scale_00.length2()))/(pixelSizeVector_w*0.701f);
681
682            // avoid nasty math by preventing a divide by zero
683            if (pixelSizeVert == 0.0f)
684               pixelSizeVert= 1.0f;
685            if (pixelSizeHori == 0.0f)
686               pixelSizeHori= 1.0f;
687
688            if (_characterSizeMode==SCREEN_COORDS)
689            {
690                float scale_font_vert=_characterHeight/pixelSizeVert;
691                float scale_font_hori=_characterHeight/_characterAspectRatio/pixelSizeHori;
692
693                if (P10<0)
694                   scale_font_vert=-scale_font_vert;
695                matrix.postMultScale(osg::Vec3f(scale_font_hori, scale_font_vert,1.0f));
696            }
697            else if (pixelSizeVert>getFontHeight())
698            {
699                float scale_font = getFontHeight()/pixelSizeVert;
700                matrix.postMultScale(osg::Vec3f(scale_font, scale_font,1.0f));
701            }
702
703        }
704
705        if (_autoRotateToScreen)
706        {
707            matrix.postMult(rotate_matrix);
708        }
709
710        matrix.postMultTranslate(_position);
711    }
712    else if (!_rotation.zeroRotation())
713    {
714        matrix.makeRotate(_rotation);
715        matrix.preMultTranslate(-_offset);
716        matrix.postMultTranslate(_position);
717    }
718    else
719    {
720        matrix.makeTranslate(_position-_offset);
721    }
722
723    // now apply matrix to the glyphs.
724    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
725        titr!=_textureGlyphQuadMap.end();
726        ++titr)
727    {
728        GlyphQuads& glyphquad = titr->second;
729        GlyphQuads::Coords2& coords2 = glyphquad._coords;
730        GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
731       
732        unsigned int numCoords = coords2.size();
733        if (numCoords!=transformedCoords.size())
734        {
735            transformedCoords.resize(numCoords);
736        }
737       
738        for(unsigned int i=0;i<numCoords;++i)
739        {
740            transformedCoords[i] = osg::Vec3(coords2[i].x(),coords2[i].y(),0.0f)*matrix;
741        }
742    }
743
744    computeBackdropPositions(contextID);
745
746    _normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix);
747    _normal.normalize();
748
749    const_cast<Text*>(this)->dirtyBound();   
750}
751
752// Presumes the atc matrix is already up-to-date
753void Text::computeBackdropPositions(unsigned int contextID) const
754{
755    if(_backdropType == NONE)
756    {
757        return;
758    }
759
760    float avg_width = 0.0f;
761    float avg_height = 0.0f;
762    unsigned int i;
763    bool is_valid_size;
764   
765    AutoTransformCache& atc = _autoTransformCache[contextID];
766    osg::Matrix& matrix = atc._matrix;
767
768    // FIXME: OPTIMIZE: This function produces the same value regardless of contextID.
769    // Since we tend to loop over contextID, we should cache this value some how
770    // instead of recomputing it each time.
771    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
772   
773    // now apply matrix to the glyphs.
774    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
775        titr!=_textureGlyphQuadMap.end();
776        ++titr)
777    {
778        GlyphQuads& glyphquad = titr->second;
779        GlyphQuads::Coords2& coords2 = glyphquad._coords;
780
781        unsigned int backdrop_index;
782        unsigned int max_backdrop_index;
783        if(_backdropType == OUTLINE)
784        {
785            // For outline, we want to draw the in every direction
786            backdrop_index = 0;
787            max_backdrop_index = 8;
788        }
789        else
790        {
791            // Yes, this may seem a little strange,
792            // but since the code is using references,
793            // I would have to duplicate the following code twice
794            // for each part of the if/else because I can't
795            // declare a reference without setting it immediately
796            // and it wouldn't survive the scope.
797            // So it happens that the _backdropType value matches
798            // the index in the array I want to store the coordinates
799            // in. So I'll just setup the for-loop so it only does
800            // the one direction I'm interested in.
801            backdrop_index = _backdropType;
802            max_backdrop_index = _backdropType+1;
803        }
804        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
805        {
806            GlyphQuads::Coords3& transformedCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
807            unsigned int numCoords = coords2.size();
808            if (numCoords!=transformedCoords.size())
809            {
810                transformedCoords.resize(numCoords);
811            }
812
813            for(i=0;i<numCoords;++i)
814            {
815                float horizontal_shift_direction;
816                float vertical_shift_direction;
817                switch(backdrop_index)
818                {
819                    case DROP_SHADOW_BOTTOM_RIGHT:
820                        {
821                            horizontal_shift_direction = 1.0f;
822                            vertical_shift_direction = -1.0f;
823                            break;
824                        }
825                    case DROP_SHADOW_CENTER_RIGHT:
826                        {
827                            horizontal_shift_direction = 1.0f;
828                            vertical_shift_direction = 0.0f;
829                            break;
830                        }
831                    case DROP_SHADOW_TOP_RIGHT:
832                        {
833                            horizontal_shift_direction = 1.0f;
834                            vertical_shift_direction = 1.0f;
835                            break;
836                        }
837                    case DROP_SHADOW_BOTTOM_CENTER:
838                        {
839                            horizontal_shift_direction = 0.0f;
840                            vertical_shift_direction = -1.0f;
841                            break;
842                        }
843                    case DROP_SHADOW_TOP_CENTER:
844                        {
845                            horizontal_shift_direction = 0.0f;
846                            vertical_shift_direction = 1.0f;
847                            break;
848                        }                               
849                    case DROP_SHADOW_BOTTOM_LEFT:
850                        {
851                            horizontal_shift_direction = -1.0f;
852                            vertical_shift_direction = -1.0f;
853                            break;
854                        }
855                    case DROP_SHADOW_CENTER_LEFT:
856                        {
857                            horizontal_shift_direction = -1.0f;
858                            vertical_shift_direction = 0.0f;
859                            break;
860                        }
861                    case DROP_SHADOW_TOP_LEFT:
862                        {
863                            horizontal_shift_direction = -1.0f;
864                            vertical_shift_direction = 1.0f;
865                            break;
866                        }
867                    default: // error
868                        {
869                            horizontal_shift_direction = 1.0f;
870                            vertical_shift_direction = -1.0f;
871                        }
872                }
873                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;
874            }
875        }
876    }
877}
878
879// This method adjusts the bounding box to account for the expanded area caused by the backdrop.
880// This assumes that the bounding box has already been computed for the text without the backdrop.
881void Text::computeBackdropBoundingBox() const
882{
883    if(_backdropType == NONE)
884    {
885        return;
886    }
887
888    float avg_width = 0.0f;
889    float avg_height = 0.0f;
890    bool is_valid_size;
891   
892    // FIXME: OPTIMIZE: It is possible that this value has already been computed before
893    // from previous calls to this function. This might be worth optimizing.
894    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
895
896    // Finally, we have one more issue to deal with.
897    // Now that the text takes more space, we need
898    // to adjust the size of the bounding box.
899    if((!_textBB.valid() || !is_valid_size))
900    {
901        return;
902    }
903   
904    // Finally, we have one more issue to deal with.
905    // Now that the text takes more space, we need
906    // to adjust the size of the bounding box.
907    switch(_backdropType)
908    {
909        case DROP_SHADOW_BOTTOM_RIGHT:
910            {
911                _textBB.set(
912                    _textBB.xMin(),
913                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
914                    _textBB.zMin(),
915                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
916                    _textBB.yMax(),
917                    _textBB.zMax()
918                );
919                break;
920            }
921        case DROP_SHADOW_CENTER_RIGHT:
922            {
923                _textBB.set(
924                    _textBB.xMin(),
925                    _textBB.yMin(),
926                    _textBB.zMin(),
927                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
928                    _textBB.yMax(),
929                    _textBB.zMax()
930                );
931                break;
932            }
933        case DROP_SHADOW_TOP_RIGHT:
934            {
935                _textBB.set(
936                    _textBB.xMin(),
937                    _textBB.yMin(),
938                    _textBB.zMin(),
939                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
940                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
941                    _textBB.zMax()
942                );
943                break;
944            }
945        case DROP_SHADOW_BOTTOM_CENTER:
946            {
947                _textBB.set(
948                    _textBB.xMin(),
949                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
950                    _textBB.zMin(),
951                    _textBB.xMax(),
952                    _textBB.yMax(),
953                    _textBB.zMax()
954                );
955                break;
956            }
957        case DROP_SHADOW_TOP_CENTER:
958            {
959                _textBB.set(
960                    _textBB.xMin(),
961                    _textBB.yMin(),
962                    _textBB.zMin(),
963                    _textBB.xMax(),
964                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
965                    _textBB.zMax()
966                );
967                break;
968            }                               
969        case DROP_SHADOW_BOTTOM_LEFT:
970            {
971                _textBB.set(
972                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
973                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
974                    _textBB.zMin(),
975                    _textBB.xMax(),
976                    _textBB.yMax(),
977                    _textBB.zMax()
978                );
979                break;
980            }
981        case DROP_SHADOW_CENTER_LEFT:
982            {
983                _textBB.set(
984                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
985                    _textBB.yMin(),
986                    _textBB.zMin(),
987                    _textBB.xMax(),
988                    _textBB.yMax(),
989                    _textBB.zMax()
990                );            break;
991            }
992        case DROP_SHADOW_TOP_LEFT:
993            {
994                _textBB.set(
995                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
996                    _textBB.yMin(),
997                    _textBB.zMin(),
998                    _textBB.xMax(),
999                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
1000                    _textBB.zMax()
1001                );
1002                break;
1003            }
1004        case OUTLINE:
1005            {
1006                _textBB.set(
1007                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
1008                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
1009                    _textBB.zMin(),
1010                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
1011                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
1012                    _textBB.zMax()
1013                );
1014                break;
1015            }
1016        default: // error
1017            {
1018                break;
1019            }
1020    }
1021}
1022
1023void Text::computeColorGradients() const
1024{
1025    switch(_colorGradientMode)
1026    {
1027        case SOLID:
1028            return;
1029            break;
1030        case PER_CHARACTER:
1031            computeColorGradientsPerCharacter();
1032            break;
1033        case OVERALL:
1034            computeColorGradientsOverall();
1035            break;
1036        default:
1037            break;
1038    }
1039}
1040
1041void Text::computeColorGradientsOverall() const
1042{
1043
1044    float min_x = FLT_MAX;
1045    float min_y = FLT_MAX;
1046    float max_x = FLT_MIN;
1047    float max_y = FLT_MIN;
1048
1049    float rgb_q11[3];
1050    float hsv_q11[3];
1051    float rgb_q12[3];
1052    float hsv_q12[3];
1053    float rgb_q21[3];
1054    float hsv_q21[3];
1055    float rgb_q22[3];
1056    float hsv_q22[3];
1057
1058    float rgb[3];
1059    float hsv[3];
1060    unsigned int i;
1061
1062    for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
1063        const_titr!=_textureGlyphQuadMap.end();
1064        ++const_titr)
1065    {
1066        const GlyphQuads& glyphquad = const_titr->second;
1067        const GlyphQuads::Coords2& coords2 = glyphquad._coords;
1068
1069        for(i=0;i<coords2.size();++i)
1070        { 
1071            // Min and Max are needed for color gradients
1072            if(coords2[i].x() > max_x)
1073            {
1074                max_x = coords2[i].x();
1075            }
1076            if(coords2[i].x() < min_x)
1077            {
1078                min_x = coords2[i].x();
1079            }
1080            if(coords2[i].y() > max_y)
1081            {
1082                max_y = coords2[i].y();
1083            }
1084            if(coords2[i].y() < min_y)
1085            {
1086                min_y = coords2[i].y();
1087            }       
1088
1089        }
1090    }
1091
1092    rgb_q11[0] = _colorGradientBottomLeft[0];
1093    rgb_q11[1] = _colorGradientBottomLeft[1];
1094    rgb_q11[2] = _colorGradientBottomLeft[2];
1095
1096    rgb_q12[0] = _colorGradientTopLeft[0];
1097    rgb_q12[1] = _colorGradientTopLeft[1];
1098    rgb_q12[2] = _colorGradientTopLeft[2];
1099
1100    rgb_q21[0] = _colorGradientBottomRight[0];
1101    rgb_q21[1] = _colorGradientBottomRight[1];
1102    rgb_q21[2] = _colorGradientBottomRight[2];
1103
1104    rgb_q22[0] = _colorGradientTopRight[0];
1105    rgb_q22[1] = _colorGradientTopRight[1];
1106    rgb_q22[2] = _colorGradientTopRight[2];
1107
1108    // for linear interpolation to look correct
1109    // for colors and imitate what OpenGL does,
1110    // we need to convert over to Hue-Saturation-Value
1111    // and linear interpolate in that space.
1112    // HSV will interpolate through the color spectrum.
1113    // Now that I think about this, perhaps we could
1114    // extend this to use function pointers or something
1115    // so users may specify their own color interpolation
1116    // scales such as Intensity, or Heated Metal, etc.
1117    convertRgbToHsv(rgb_q11, hsv_q11);
1118    convertRgbToHsv(rgb_q12, hsv_q12);
1119    convertRgbToHsv(rgb_q21, hsv_q21);
1120    convertRgbToHsv(rgb_q22, hsv_q22);
1121
1122    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1123        titr!=_textureGlyphQuadMap.end();
1124        ++titr)
1125    {
1126        GlyphQuads& glyphquad = titr->second;
1127        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1128        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1129
1130        unsigned int numCoords = coords2.size();
1131        if (numCoords!=colorCoords.size())
1132        {
1133            colorCoords.resize(numCoords);
1134        }
1135
1136        for(i=0;i<numCoords;++i)
1137        {
1138            float hue = bilinearInterpolate(
1139                min_x,
1140                max_x,
1141                min_y,
1142                max_y,
1143                coords2[i].x(),
1144                coords2[i].y(),
1145                hsv_q11[0],
1146                hsv_q12[0],
1147                hsv_q21[0],
1148                hsv_q22[0]
1149            );
1150
1151            float saturation = bilinearInterpolate(
1152                min_x,
1153                max_x,
1154                min_y,
1155                max_y,
1156                coords2[i].x(),
1157                coords2[i].y(),
1158                hsv_q11[1],
1159                hsv_q12[1],
1160                hsv_q21[1],
1161                hsv_q22[1]
1162            );
1163
1164            float value = bilinearInterpolate(
1165                min_x,
1166                max_x,
1167                min_y,
1168                max_y,
1169                coords2[i].x(),
1170                coords2[i].y(),
1171                hsv_q11[2],
1172                hsv_q12[2],
1173                hsv_q21[2],
1174                hsv_q22[2]
1175            );
1176            // Alpha does not convert to HSV           
1177            float alpha = bilinearInterpolate(
1178                min_x,
1179                max_x,
1180                min_y,
1181                max_y,
1182                coords2[i].x(),
1183                coords2[i].y(),
1184                _colorGradientBottomLeft[3],
1185                _colorGradientTopLeft[3],
1186                _colorGradientBottomRight[3],
1187                _colorGradientTopRight[3]
1188            );                                   
1189
1190            hsv[0] = hue;
1191            hsv[1] = saturation;
1192            hsv[2] = value;
1193            // Convert back to RGB
1194            convertHsvToRgb(hsv, rgb);
1195            colorCoords[i] = osg::Vec4(rgb[0],rgb[1],rgb[2],alpha);
1196        }
1197    }
1198}
1199
1200void Text::computeColorGradientsPerCharacter() const
1201{
1202    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1203        titr!=_textureGlyphQuadMap.end();
1204        ++titr)
1205    {
1206        GlyphQuads& glyphquad = titr->second;
1207        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1208        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1209
1210        unsigned int numCoords = coords2.size();
1211        if (numCoords!=colorCoords.size())
1212        {
1213            colorCoords.resize(numCoords);
1214        }
1215
1216        for(unsigned int i=0;i<numCoords;++i)
1217        {
1218            switch(i%4)
1219            {
1220                case 0: // top-left
1221                    {
1222                        colorCoords[i] = _colorGradientTopLeft;
1223                        break;
1224                    }
1225                case 1: // bottom-left
1226                    {
1227                        colorCoords[i] = _colorGradientBottomLeft;
1228                        break;
1229                    }
1230                case 2: // bottom-right
1231                    {
1232                        colorCoords[i] = _colorGradientBottomRight;
1233                        break;
1234                    }
1235                case 3: // top-right
1236                    {
1237                        colorCoords[i] = _colorGradientTopRight;
1238                        break;
1239                    }
1240                default: // error
1241                    {
1242                        colorCoords[i] = osg::Vec4(0.0f,0.0f,0.0f,1.0f);
1243                    }
1244            }
1245        }
1246    }
1247}
1248
1249void Text::drawImplementation(osg::RenderInfo& renderInfo) const
1250{
1251    drawImplementation(*renderInfo.getState(), osg::Vec4(1.0f,1.0f,1.0f,1.0f));
1252}
1253
1254void Text::drawImplementation(osg::State& state, const osg::Vec4& colorMultiplier) const
1255{
1256    unsigned int contextID = state.getContextID();
1257
1258    state.applyMode(GL_BLEND,true);
1259#if 1   
1260    state.applyTextureMode(0,GL_TEXTURE_2D,true);
1261#else
1262    state.applyTextureMode(0,GL_TEXTURE_2D,false);
1263#endif
1264    state.applyTextureAttribute(0,getActiveFont()->getTexEnv());
1265
1266    if (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)
1267    {
1268        int frameNumber = state.getFrameStamp()?state.getFrameStamp()->getFrameNumber():0;
1269        AutoTransformCache& atc = _autoTransformCache[contextID];
1270        const osg::Matrix& modelview = state.getModelViewMatrix();
1271        const osg::Matrix& projection = state.getProjectionMatrix();
1272
1273        osg::Vec3 newTransformedPosition = _position*modelview;
1274
1275        int width = atc._width;
1276        int height = atc._height;
1277
1278        const osg::Viewport* viewport = state.getCurrentViewport();
1279        if (viewport)
1280        {
1281            width = static_cast<int>(viewport->width());
1282            height = static_cast<int>(viewport->height());
1283        }
1284
1285        bool doUpdate = atc._traversalNumber==-1;
1286        if (atc._traversalNumber>=0)
1287        {
1288            if (atc._modelview!=modelview)
1289            {
1290                doUpdate = true;
1291            }
1292            else if (width!=atc._width || height!=atc._height)
1293            {
1294                doUpdate = true;
1295            }
1296            else if (atc._projection!=projection)
1297            {
1298                doUpdate = true;
1299            }
1300        }
1301       
1302        atc._traversalNumber = frameNumber;
1303        atc._width = width;
1304        atc._height = height;
1305       
1306        if (doUpdate)
1307        {   
1308            atc._transformedPosition = newTransformedPosition;
1309            atc._projection = projection;
1310            atc._modelview = modelview;
1311
1312            computePositions(contextID);
1313        }
1314       
1315    }
1316   
1317   
1318    // Ensure that the glyph coordinates have been transformed for
1319    // this context id.
1320
1321    if ( !_textureGlyphQuadMap.empty() )
1322    {
1323        const GlyphQuads& glyphquad = (_textureGlyphQuadMap.begin())->second;
1324        if ( glyphquad._transformedCoords[contextID].empty() )
1325        {
1326            computePositions(contextID);
1327        }
1328    }
1329
1330    glNormal3fv(_normal.ptr());
1331
1332    if (_drawMode & TEXT)
1333    {
1334
1335        state.disableAllVertexArrays();
1336
1337        // Okay, since ATI's cards/drivers are not working correctly,
1338        // we need alternative solutions to glPolygonOffset.
1339        // So this is a pick your poison approach. Each alternative
1340        // backend has trade-offs associated with it, but with luck,
1341        // the user may find that works for them.
1342        if(_backdropType != NONE)
1343        {
1344            switch(_backdropImplementation)
1345            {
1346                case POLYGON_OFFSET:
1347                    renderWithPolygonOffset(state,colorMultiplier);
1348                    break;
1349                case NO_DEPTH_BUFFER:
1350                    renderWithNoDepthBuffer(state,colorMultiplier);
1351                    break;
1352                case DEPTH_RANGE:
1353                    renderWithDepthRange(state,colorMultiplier);
1354                    break;
1355                case STENCIL_BUFFER:
1356                    renderWithStencilBuffer(state,colorMultiplier);
1357                    break;
1358                default:
1359                    renderWithPolygonOffset(state,colorMultiplier);
1360            }
1361        }
1362        else
1363        {
1364            renderOnlyForegroundText(state,colorMultiplier);
1365        }
1366    }
1367
1368    if (_drawMode & BOUNDINGBOX)
1369    {
1370
1371        if (_textBB.valid())
1372        {
1373            state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1374
1375            const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1376
1377            osg::Vec3 c00(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix);
1378            osg::Vec3 c10(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix);
1379            osg::Vec3 c11(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix);
1380            osg::Vec3 c01(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix);
1381
1382       
1383            glColor4fv(colorMultiplier.ptr());
1384            glBegin(GL_LINE_LOOP);
1385                glVertex3fv(c00.ptr());
1386                glVertex3fv(c10.ptr());
1387                glVertex3fv(c11.ptr());
1388                glVertex3fv(c01.ptr());
1389            glEnd();
1390        }
1391    }   
1392
1393    if (_drawMode & ALIGNMENT)
1394    {
1395        glColor4fv(colorMultiplier.ptr());
1396
1397        float cursorsize = _characterHeight*0.5f;
1398
1399        const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1400
1401        osg::Vec3 hl(osg::Vec3(_offset.x()-cursorsize,_offset.y(),_offset.z())*matrix);
1402        osg::Vec3 hr(osg::Vec3(_offset.x()+cursorsize,_offset.y(),_offset.z())*matrix);
1403        osg::Vec3 vt(osg::Vec3(_offset.x(),_offset.y()-cursorsize,_offset.z())*matrix);
1404        osg::Vec3 vb(osg::Vec3(_offset.x(),_offset.y()+cursorsize,_offset.z())*matrix);
1405
1406        state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1407       
1408        glBegin(GL_LINES);
1409            glVertex3fv(hl.ptr());
1410            glVertex3fv(hr.ptr());
1411            glVertex3fv(vt.ptr());
1412            glVertex3fv(vb.ptr());
1413        glEnd();
1414       
1415    }   
1416}
1417
1418void Text::accept(osg::Drawable::ConstAttributeFunctor& af) const
1419{
1420    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1421        titr!=_textureGlyphQuadMap.end();
1422        ++titr)
1423    {
1424        const GlyphQuads& glyphquad = titr->second;
1425        af.apply(osg::Drawable::VERTICES,glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1426        af.apply(osg::Drawable::TEXTURE_COORDS_0,glyphquad._texcoords.size(),&(glyphquad._texcoords.front()));
1427    }
1428}
1429
1430void Text::accept(osg::PrimitiveFunctor& pf) const
1431{
1432    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1433        titr!=_textureGlyphQuadMap.end();
1434        ++titr)
1435    {
1436        const GlyphQuads& glyphquad = titr->second;
1437
1438        pf.setVertexArray(glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1439        pf.drawArrays(GL_QUADS,0,glyphquad._transformedCoords[0].size());
1440           
1441    }
1442   
1443}
1444
1445
1446void Text::setThreadSafeRefUnref(bool threadSafe)
1447{
1448    TextBase::setThreadSafeRefUnref(threadSafe);
1449
1450    getActiveFont()->setThreadSafeRefUnref(threadSafe);
1451}
1452
1453void Text::resizeGLObjectBuffers(unsigned int maxSize)
1454{
1455    TextBase::resizeGLObjectBuffers(maxSize);
1456   
1457    getActiveFont()->resizeGLObjectBuffers(maxSize);
1458}
1459
1460
1461void Text::releaseGLObjects(osg::State* state) const
1462{
1463    TextBase::releaseGLObjects(state);
1464    getActiveFont()->releaseGLObjects(state);
1465}
1466
1467
1468void Text::setBackdropType(BackdropType type)
1469{
1470    if (_backdropType==type) return;
1471
1472    _backdropType = type;
1473    computeGlyphRepresentation();
1474}
1475
1476void Text::setBackdropImplementation(BackdropImplementation implementation)
1477{
1478    if (_backdropImplementation==implementation) return;
1479
1480    _backdropImplementation = implementation;
1481    computeGlyphRepresentation();
1482}
1483
1484
1485void Text::setBackdropOffset(float offset)
1486{
1487    _backdropHorizontalOffset = offset;
1488    _backdropVerticalOffset = offset;
1489    computeGlyphRepresentation();
1490}
1491
1492void Text::setBackdropOffset(float horizontal, float vertical)
1493{
1494    _backdropHorizontalOffset = horizontal;
1495    _backdropVerticalOffset = vertical;
1496    computeGlyphRepresentation();
1497}
1498
1499void Text::setBackdropColor(const osg::Vec4& color)
1500{
1501    _backdropColor = color;
1502}
1503
1504void Text::setColorGradientMode(ColorGradientMode mode)
1505{
1506    if (_colorGradientMode==mode) return;
1507
1508    _colorGradientMode = mode;
1509    computeGlyphRepresentation();
1510}
1511
1512void Text::setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight)
1513{
1514    _colorGradientTopLeft = topLeft;
1515    _colorGradientBottomLeft = bottomLeft;
1516    _colorGradientBottomRight = bottomRight;
1517    _colorGradientTopRight = topRight;
1518    computeGlyphRepresentation();
1519}
1520
1521// Formula for f(x,y) from Wikipedia "Bilinear interpolation", 2006-06-18
1522float Text::bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const
1523{
1524    return (
1525        ((q11 / ((x2-x1)*(y2-y1))) * (x2-x)*(y2-y))
1526        + ((q21 / ((x2-x1)*(y2-y1))) * (x-x1)*(y2-y))
1527        + ((q12 / ((x2-x1)*(y2-y1))) * (x2-x)*(y-y1))
1528        + ((q22 / ((x2-x1)*(y2-y1))) * (x-x1)*(y-y1))
1529    );
1530}
1531
1532
1533/**
1534 ** routines to convert between RGB and HSV
1535 **
1536 ** Reference:  Foley, van Dam, Feiner, Hughes,
1537 **        "Computer Graphics Principles and Practices,"
1538 **        Additon-Wesley, 1990, pp592-593.
1539 **/       
1540/*
1541 *  FUNCTION
1542 *    HsvRgb( hsv, rgb )
1543 *
1544 *  DESCRIPTION
1545 *    convert a hue-saturation-value into a red-green-blue value
1546 *
1547 *    NOTE
1548 *    Array sizes are 3
1549 *    Values are between 0.0 and 1.0
1550 */
1551
1552void Text::convertHsvToRgb( float hsv[], float rgb[] ) const
1553{
1554    float h, s, v;            /* hue, sat, value        */
1555    /*    double delta;    */        /* change in color value    */
1556    float r, g, b;            /* red, green, blue        */
1557    float i, f, p, q, t;        /* interim values        */
1558
1559
1560    /* guarantee valid input:                    */
1561
1562    h = hsv[0] / 60.f;
1563    while( h >= 6.f )    h -= 6.f;
1564    while( h <  0.f )     h += 6.f;
1565
1566    s = hsv[1];
1567    if( s < 0.f )
1568        s = 0.f;
1569    if( s > 1.f )
1570        s = 1.f;
1571
1572    v = hsv[2];
1573    if( v < 0.f )
1574        v = 0.f;
1575    if( v > 1.f )
1576        v = 1.f;
1577
1578
1579    /* if sat==0, then is a gray:                    */
1580
1581    if( s == 0.0f )
1582    {
1583        rgb[0] = rgb[1] = rgb[2] = v;
1584        return;
1585    }
1586
1587
1588    /* get an rgb from the hue itself:                */
1589
1590    i = floor( h );
1591    f = h - i;
1592    p = v * ( 1.f - s );
1593    q = v * ( 1.f - s*f );
1594    t = v * ( 1.f - ( s * (1.f-f) ) );
1595
1596    switch( (int) i )
1597    {
1598        case 0:
1599            r = v;    g = t;    b = p;
1600            break;
1601
1602        case 1:
1603            r = q;    g = v;    b = p;
1604            break;
1605
1606        case 2:
1607            r = p;    g = v;    b = t;
1608            break;
1609
1610        case 3:
1611            r = p;    g = q;    b = v;
1612            break;
1613
1614        case 4:
1615            r = t;    g = p;    b = v;
1616            break;
1617
1618        case 5:
1619            r = v;    g = p;    b = q;
1620            break;
1621
1622        default:
1623            /* never happens? */
1624            r = 0;  g = 0;  b = 0;
1625            break;
1626    }
1627
1628
1629    rgb[0] = r;
1630    rgb[1] = g;
1631    rgb[2] = b;
1632
1633}
1634
1635/*
1636 *  FUNCTION
1637 *    RgbHsv
1638 *
1639 *  DESCRIPTION
1640 *    convert a red-green-blue value into hue-saturation-value
1641 *
1642 *    NOTE
1643 *    Array sizes are 3
1644 *    Values are between 0.0 and 1.0
1645 */
1646
1647void Text::convertRgbToHsv( float rgb[], float hsv[] ) const
1648{
1649    float r, g, b;            /* red, green, blue        */
1650    float min, max;            /* min and max rgb values    */
1651    float fmin, fmax, diff;        /* min, max, and range of rgb vals */
1652    float hue, sat, value;        /* h s v            */
1653    float cr, cg, cb;        /* coefficients for computing hue */
1654
1655
1656    /* determine min and max color primary values:            */
1657
1658    r = rgb[0];    g = rgb[1];    b = rgb[2];
1659    min = r;    max = r;
1660    if( g < min ) min = g;
1661    if( g > max ) max = g;
1662    if( b < min ) min = b;
1663    if( b > max ) max = b;
1664
1665    fmin = min;
1666    fmax = max;
1667    diff = fmax - fmin;
1668
1669
1670    /* get value and saturation:                    */
1671
1672    value = fmax;
1673    if( max == 0.f )
1674        sat = 0.0f;
1675    else
1676        sat = diff/fmax;
1677
1678
1679
1680    /* compute hue:                            */
1681
1682    if( sat == 0.0f )
1683        hue = 0.0f;
1684    else
1685    {
1686        float inv_diff = 1.0f / diff;
1687        cr = ( fmax-r ) * inv_diff;
1688        cg = ( fmax-g ) * inv_diff;
1689        cb = ( fmax-b ) * inv_diff;
1690
1691        if( max == r )
1692            hue =      (g-b) * inv_diff;
1693        else if( max == g )
1694            hue = 2.f + (b-r) * inv_diff;
1695        else if( max == b )
1696            hue = 4.f + (r-g) * inv_diff;
1697        else
1698            hue = 0.0f;
1699    }
1700
1701
1702    hue *= 60.0f;
1703    if( hue < 0.0f )
1704        hue += 360.0f;
1705    if( hue > 360.0f )
1706        hue -= 360.0f;
1707
1708
1709    /* store output values:                        */
1710
1711    hsv[0] = hue;
1712    hsv[1] = sat;
1713    hsv[2] = value;
1714
1715}
1716
1717void Text::drawForegroundText(osg::State& state, const GlyphQuads& glyphquad, const osg::Vec4& colorMultiplier) const
1718{
1719    unsigned int contextID = state.getContextID();
1720
1721    const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
1722    if (!transformedCoords.empty())
1723    {
1724        state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
1725        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1726
1727        if(_colorGradientMode == SOLID)
1728        {
1729            state.disableColorPointer();
1730            glColor4f(colorMultiplier.r()*_color.r(),colorMultiplier.g()*_color.g(),colorMultiplier.b()*_color.b(),colorMultiplier.a()*_color.a());
1731        }
1732        else
1733        {
1734            state.setColorPointer( 4, GL_FLOAT, 0, &(glyphquad._colorCoords.front()));
1735        }
1736
1737        glDrawArrays(GL_QUADS,0,transformedCoords.size());
1738
1739    }
1740}
1741
1742void Text::renderOnlyForegroundText(osg::State& state, const osg::Vec4& colorMultiplier) const
1743{
1744    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1745        titr!=_textureGlyphQuadMap.end();
1746        ++titr)
1747    {
1748        // need to set the texture here...
1749        state.applyTextureAttribute(0,titr->first.get());
1750
1751        const GlyphQuads& glyphquad = titr->second;
1752
1753        drawForegroundText(state, glyphquad, colorMultiplier);
1754    }
1755
1756}
1757
1758
1759void Text::renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMultiplier) const
1760{
1761    unsigned int contextID = state.getContextID();
1762
1763
1764    if (!osg::PolygonOffset::areFactorAndUnitsMultipliersSet())
1765    {
1766        osg::PolygonOffset::setFactorAndUnitsMultipliersUsingBestGuessForDriver();
1767    }
1768
1769    // Do I really need to do this for glPolygonOffset?
1770    glPushAttrib(GL_POLYGON_OFFSET_FILL);
1771    glEnable(GL_POLYGON_OFFSET_FILL);
1772
1773    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1774        titr!=_textureGlyphQuadMap.end();
1775        ++titr)
1776    {
1777        // need to set the texture here...
1778        state.applyTextureAttribute(0,titr->first.get());
1779
1780        const GlyphQuads& glyphquad = titr->second;
1781
1782        unsigned int backdrop_index;
1783        unsigned int max_backdrop_index;
1784        if(_backdropType == OUTLINE)
1785        {
1786            backdrop_index = 0;
1787            max_backdrop_index = 8;
1788        }
1789        else
1790        {
1791            backdrop_index = _backdropType;
1792            max_backdrop_index = _backdropType+1;
1793        }
1794
1795        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1796        state.disableColorPointer();
1797        glColor4fv(_backdropColor.ptr());
1798
1799        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1800        {
1801            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1802            if (!transformedBackdropCoords.empty())
1803            {
1804                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1805                glPolygonOffset(0.1f * osg::PolygonOffset::getFactorMultiplier(),
1806                                2.0f * osg::PolygonOffset::getUnitsMultiplier() * (max_backdrop_index-backdrop_index) );
1807                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1808            }
1809        }
1810
1811        // Reset the polygon offset so the foreground text is on top
1812        glPolygonOffset(0.0f,0.0f);
1813
1814        drawForegroundText(state, glyphquad, colorMultiplier);
1815    }
1816
1817    glPopAttrib();
1818}
1819   
1820
1821void Text::renderWithNoDepthBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1822{
1823    unsigned int contextID = state.getContextID();
1824
1825    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1826    glDisable(GL_DEPTH_TEST);
1827
1828    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1829        titr!=_textureGlyphQuadMap.end();
1830        ++titr)
1831    {
1832        // need to set the texture here...
1833        state.applyTextureAttribute(0,titr->first.get());
1834
1835        const GlyphQuads& glyphquad = titr->second;
1836
1837        unsigned int backdrop_index;
1838        unsigned int max_backdrop_index;
1839        if(_backdropType == OUTLINE)
1840        {
1841            backdrop_index = 0;
1842            max_backdrop_index = 8;
1843        }
1844        else
1845        {
1846            backdrop_index = _backdropType;
1847            max_backdrop_index = _backdropType+1;
1848        }
1849
1850        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1851        state.disableColorPointer();
1852        glColor4fv(_backdropColor.ptr());
1853
1854        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1855        {
1856            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1857            if (!transformedBackdropCoords.empty())
1858            {
1859                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1860                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1861            }
1862        }
1863
1864        drawForegroundText(state, glyphquad, colorMultiplier);
1865    }
1866
1867    glPopAttrib();
1868}
1869
1870// This idea comes from Paul Martz's OpenGL FAQ: 13.050
1871void Text::renderWithDepthRange(osg::State& state, const osg::Vec4& colorMultiplier) const
1872{
1873    unsigned int contextID = state.getContextID();
1874
1875    // Hmmm, the man page says GL_VIEWPORT_BIT for Depth range (near and far)
1876    // but experimentally, GL_DEPTH_BUFFER_BIT for glDepthRange.
1877//    glPushAttrib(GL_VIEWPORT_BIT);
1878    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1879
1880    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1881        titr!=_textureGlyphQuadMap.end();
1882        ++titr)
1883    {
1884        // need to set the texture here...
1885        state.applyTextureAttribute(0,titr->first.get());
1886
1887        const GlyphQuads& glyphquad = titr->second;
1888
1889        unsigned int backdrop_index;
1890        unsigned int max_backdrop_index;
1891        if(_backdropType == OUTLINE)
1892        {
1893            backdrop_index = 0;
1894            max_backdrop_index = 8;
1895        }
1896        else
1897        {
1898            backdrop_index = _backdropType;
1899            max_backdrop_index = _backdropType+1;
1900        }
1901
1902        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1903        state.disableColorPointer();
1904        glColor4fv(_backdropColor.ptr());
1905
1906        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1907        {
1908            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1909            if (!transformedBackdropCoords.empty())
1910            {
1911                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1912                double offset = double(max_backdrop_index-backdrop_index)*0.003;
1913                glDepthRange( offset, 1.0+offset);
1914
1915                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
1916            }
1917        }
1918
1919        glDepthRange(0.0, 1.0);
1920
1921        drawForegroundText(state, glyphquad, colorMultiplier);
1922    }
1923
1924    glPopAttrib();
1925}
1926
1927void Text::renderWithStencilBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1928{
1929    /* Here are the steps:
1930     * 1) Disable drawing color
1931     * 2) Enable the stencil buffer
1932     * 3) Draw all the text to the stencil buffer
1933     * 4) Disable the stencil buffer
1934     * 5) Enable color
1935     * 6) Disable the depth buffer
1936     * 7) Draw all the text again.
1937     * 7b) Make sure the foreground text is drawn last if priority levels
1938     * are the same OR
1939     * 7c) If priority levels are different, then make sure the foreground
1940     * text has the higher priority.
1941     */
1942    unsigned int contextID = state.getContextID();
1943    TextureGlyphQuadMap::iterator titr; // Moved up here for VC6
1944   
1945    glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_TEST);
1946
1947    // It seems I can get away without calling this here
1948    //glClear(GL_STENCIL_BUFFER_BIT);
1949
1950    // enable stencil buffer
1951    glEnable(GL_STENCIL_TEST);
1952
1953    // write a one to the stencil buffer everywhere we are about to draw
1954    glStencilFunc(GL_ALWAYS, 1, 1);
1955
1956    // write only to the stencil buffer if we pass the depth test
1957    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1958
1959    // Disable writing to the color buffer so we only write to the stencil
1960    // buffer and the depth buffer
1961    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1962
1963    // make sure the depth buffer is enabled
1964//    glEnable(GL_DEPTH_TEST);
1965//    glDepthMask(GL_TRUE);
1966//    glDepthFunc(GL_LESS);
1967
1968    // Arrrgh! Why does the code only seem to work correctly if I call this?
1969    glDepthMask(GL_FALSE);
1970   
1971
1972    // Draw all the text to the stencil buffer to mark out the region
1973    // that we can write too.
1974   
1975    for(titr=_textureGlyphQuadMap.begin();
1976        titr!=_textureGlyphQuadMap.end();
1977        ++titr)
1978    {
1979        // need to set the texture here...
1980        state.applyTextureAttribute(0,titr->first.get());
1981
1982        const GlyphQuads& glyphquad = titr->second;
1983
1984        unsigned int backdrop_index;
1985        unsigned int max_backdrop_index;
1986        if(_backdropType == OUTLINE)
1987        {
1988            backdrop_index = 0;
1989            max_backdrop_index = 8;
1990        }
1991        else
1992        {
1993            backdrop_index = _backdropType;
1994            max_backdrop_index = _backdropType+1;
1995        }
1996
1997        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1998        state.disableColorPointer();
1999
2000        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
2001        {
2002            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
2003            if (!transformedBackdropCoords.empty())
2004            {
2005                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
2006                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
2007            }
2008        }
2009
2010        // Draw the foreground text
2011        const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
2012        if (!transformedCoords.empty())
2013        {
2014            state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
2015            state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
2016            glDrawArrays(GL_QUADS,0,transformedCoords.size());
2017        }
2018    }
2019
2020
2021    // disable the depth buffer
2022//    glDisable(GL_DEPTH_TEST);
2023//    glDepthMask(GL_FALSE);
2024//    glDepthMask(GL_TRUE);
2025//    glDepthFunc(GL_ALWAYS);
2026
2027    // Set the stencil function to pass when the stencil is 1
2028    // Bug: This call seems to have no effect. Try changing to NOTEQUAL
2029    // and see the exact same results.
2030    glStencilFunc(GL_EQUAL, 1, 1);
2031
2032    // disable writing to the stencil buffer
2033    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2034    glStencilMask(GL_FALSE);
2035
2036    // Re-enable writing to the color buffer so we can see the results
2037    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
2038
2039
2040    // Draw all the text again
2041
2042    for(titr=_textureGlyphQuadMap.begin();
2043        titr!=_textureGlyphQuadMap.end();
2044        ++titr)
2045    {
2046        // need to set the texture here...
2047        state.applyTextureAttribute(0,titr->first.get());
2048
2049        const GlyphQuads& glyphquad = titr->second;
2050
2051        unsigned int backdrop_index;
2052        unsigned int max_backdrop_index;
2053        if(_backdropType == OUTLINE)
2054        {
2055            backdrop_index = 0;
2056            max_backdrop_index = 8;
2057        }
2058        else
2059        {
2060            backdrop_index = _backdropType;
2061            max_backdrop_index = _backdropType+1;
2062        }
2063
2064        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
2065        state.disableColorPointer();
2066        glColor4fv(_backdropColor.ptr());
2067
2068        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
2069        {
2070            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
2071            if (!transformedBackdropCoords.empty())
2072            {
2073                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
2074                glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
2075            }
2076        }
2077
2078        drawForegroundText(state, glyphquad, colorMultiplier);
2079    }
2080   
2081    glPopAttrib();
2082}
2083
2084
Note: See TracBrowser for help on using the browser.