From Eric Wing, added support for outline/shadow and colour gradient effects.
This commit is contained in:
parent
c71fc9fcb6
commit
710adfd698
@ -211,6 +211,117 @@ public:
|
|||||||
|
|
||||||
unsigned int getDrawMode() const { return _drawMode; }
|
unsigned int getDrawMode() const { return _drawMode; }
|
||||||
|
|
||||||
|
|
||||||
|
enum BackdropType
|
||||||
|
{
|
||||||
|
DROP_SHADOW_BOTTOM_RIGHT = 0, // usually the type of shadow you see
|
||||||
|
DROP_SHADOW_CENTER_RIGHT,
|
||||||
|
DROP_SHADOW_TOP_RIGHT,
|
||||||
|
DROP_SHADOW_BOTTOM_CENTER,
|
||||||
|
DROP_SHADOW_TOP_CENTER,
|
||||||
|
DROP_SHADOW_BOTTOM_LEFT,
|
||||||
|
DROP_SHADOW_CENTER_LEFT,
|
||||||
|
DROP_SHADOW_TOP_LEFT,
|
||||||
|
OUTLINE,
|
||||||
|
NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BackdropType gives you a background shadow text behind your regular
|
||||||
|
* text. This helps give text extra contrast which can be useful when
|
||||||
|
* placing text against noisy backgrounds.
|
||||||
|
* The color of the background shadow text is specified by setBackdropColor().
|
||||||
|
* DROP_SHADOW_BOTTOM_RIGHT will draw backdrop text to the right and down of
|
||||||
|
* the normal text. Other DROW_SHADOW_* modes do the same for their repective directions.
|
||||||
|
* OUTLINE will draw backdrop text so that it appears the text has an outline
|
||||||
|
* or border around the normal text. This mode is particularly useful against
|
||||||
|
* really noisy backgrounds that may put text on top of things that have
|
||||||
|
* all types of colors which you don't have control over.
|
||||||
|
* Some real world examples of this general technique in use that I know of
|
||||||
|
* are Google Earth, Sid Meier's Pirates (2004 Remake), and Star Control 2 (PC 1993).
|
||||||
|
* The default is NONE.
|
||||||
|
*/
|
||||||
|
void setBackdropType(BackdropType type);
|
||||||
|
|
||||||
|
BackdropType getBackdropType() const { return _backdropType; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the amount text is offset to create the backdrop/shadow effect.
|
||||||
|
* Set the value too high and for example, in OUTLINE mode you will get a "Brady Bunch"
|
||||||
|
* effect where you see duplicates of the text in a 3x3 grid.
|
||||||
|
* Set the value too small and you won't see anything.
|
||||||
|
* The values represent percentages. 1.0 means 100% so a value of 1.0
|
||||||
|
* in DROW_SHADOW_LEFT_CENTER mode would cause each glyph to be echoed
|
||||||
|
* next to it self. So the letter 'e' might look like 'ee'.
|
||||||
|
* Good values tend to be in the 0.03 to 0.10 range (but will be subject
|
||||||
|
* to your specific font and display characteristics).
|
||||||
|
* Note that the text bounding boxes are updated to include backdrop offsets.
|
||||||
|
* However, other metric information such as getCharacterHeight() are unaffected
|
||||||
|
* by this. This means that individual glyph spacing (kerning?) are unchanged
|
||||||
|
* even when this mode is used.
|
||||||
|
* The default is 0.07 (7% offset).
|
||||||
|
*/
|
||||||
|
void setBackdropOffset(float offset = 0.07f);
|
||||||
|
/**
|
||||||
|
* This overloaded version lets you specify the offset for the horizontal
|
||||||
|
* and vertical components separately.
|
||||||
|
*/
|
||||||
|
void setBackdropOffset(float horizontal, float vertical);
|
||||||
|
|
||||||
|
float getBackdropHorizontalOffet() const { return _backdropHorizontalOffset; }
|
||||||
|
|
||||||
|
float getBackdropVerticalOffset() const { return _backdropVerticalOffset; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This specifies the color of the backdrop text.
|
||||||
|
* The default is black.
|
||||||
|
*/
|
||||||
|
void setBackdropColor(const osg::Vec4& color);
|
||||||
|
|
||||||
|
const osg::Vec4& getBackdropColor() const { return _backdropColor; }
|
||||||
|
|
||||||
|
|
||||||
|
enum ColorGradientMode
|
||||||
|
{
|
||||||
|
SOLID = 0, // a.k.a. ColorGradients off
|
||||||
|
PER_CHARACTER,
|
||||||
|
OVERALL
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets different types of text coloring modes.
|
||||||
|
* When the coloring mode is not set to SOLID, the
|
||||||
|
* colors specified in setColorGradientCorners() determine
|
||||||
|
* the colors for the text.
|
||||||
|
* When the gradient mode is OVERALL, the coloring scheme
|
||||||
|
* attempts to approximate the effect as if the entire text box/region
|
||||||
|
* were a single polygon and you had applied colors to each of the four
|
||||||
|
* corners with GL_SMOOTH enabled. In this mode, OpenGL interpolates
|
||||||
|
* the colors across the polygon, and this is what OVERALL tries to
|
||||||
|
* emulate. This can be used to give nice embellishments on things
|
||||||
|
* like logos and names.
|
||||||
|
* PER_CHARACTER is similar to OVERALL except that it applies the
|
||||||
|
* color interpolation to the four corners of each character instead
|
||||||
|
* of across the overall text box.
|
||||||
|
* The default is SOLID (a.k.a. off).
|
||||||
|
*/
|
||||||
|
void setColorGradientMode(ColorGradientMode mode);
|
||||||
|
|
||||||
|
ColorGradientMode getColorGradientMode() const { return _colorGradientMode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only for gradient mode, let's you specify the colors of the 4 corners.
|
||||||
|
* If ColorGradients are off, these values are ignored (and the value from setColor()
|
||||||
|
* is the only one that is relevant.
|
||||||
|
*/
|
||||||
|
void setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight);
|
||||||
|
|
||||||
|
const osg::Vec4& getColorGradientTopLeft() const { return _colorGradientTopLeft; }
|
||||||
|
const osg::Vec4& getColorGradientBottomLeft() const { return _colorGradientBottomLeft; }
|
||||||
|
const osg::Vec4& getColorGradientBottomRight() const { return _colorGradientBottomRight; }
|
||||||
|
const osg::Vec4& getColorGradientTopRight() const { return _colorGradientTopRight; }
|
||||||
|
|
||||||
|
|
||||||
void setKerningType(KerningType kerningType) { _kerningType = kerningType; }
|
void setKerningType(KerningType kerningType) { _kerningType = kerningType; }
|
||||||
|
|
||||||
KerningType getKerningType() const { return _kerningType; }
|
KerningType getKerningType() const { return _kerningType; }
|
||||||
@ -254,6 +365,7 @@ public:
|
|||||||
typedef std::vector<osg::Vec2> Coords2;
|
typedef std::vector<osg::Vec2> Coords2;
|
||||||
typedef std::vector<osg::Vec3> Coords3;
|
typedef std::vector<osg::Vec3> Coords3;
|
||||||
typedef std::vector<osg::Vec2> TexCoords;
|
typedef std::vector<osg::Vec2> TexCoords;
|
||||||
|
typedef std::vector<osg::Vec4> ColorCoords;
|
||||||
|
|
||||||
Glyphs _glyphs;
|
Glyphs _glyphs;
|
||||||
Coords2 _coords;
|
Coords2 _coords;
|
||||||
@ -261,6 +373,9 @@ public:
|
|||||||
TexCoords _texcoords;
|
TexCoords _texcoords;
|
||||||
LineNumbers _lineNumbers;
|
LineNumbers _lineNumbers;
|
||||||
|
|
||||||
|
osg::buffered_object<Coords3> _transformedBackdropCoords[8];
|
||||||
|
ColorCoords _colorCoords;
|
||||||
|
|
||||||
Glyphs getGlyphs() { return _glyphs; }
|
Glyphs getGlyphs() { return _glyphs; }
|
||||||
const Glyphs getGlyphs() const { return _glyphs; }
|
const Glyphs getGlyphs() const { return _glyphs; }
|
||||||
|
|
||||||
@ -356,6 +471,26 @@ protected:
|
|||||||
void computePositions();
|
void computePositions();
|
||||||
void computePositions(unsigned int contextID) const;
|
void computePositions(unsigned int contextID) const;
|
||||||
|
|
||||||
|
void computeBackdropPositions(unsigned int contextID) const;
|
||||||
|
void computeColorGradients() const;
|
||||||
|
void computeColorGradientsOverall() const;
|
||||||
|
void computeColorGradientsPerCharacter() const;
|
||||||
|
|
||||||
|
BackdropType _backdropType;
|
||||||
|
float _backdropHorizontalOffset;
|
||||||
|
float _backdropVerticalOffset;
|
||||||
|
osg::Vec4 _backdropColor;
|
||||||
|
|
||||||
|
ColorGradientMode _colorGradientMode;
|
||||||
|
osg::Vec4 _colorGradientTopLeft;
|
||||||
|
osg::Vec4 _colorGradientBottomLeft;
|
||||||
|
osg::Vec4 _colorGradientBottomRight;
|
||||||
|
osg::Vec4 _colorGradientTopRight;
|
||||||
|
|
||||||
|
// Helper functions for color interpolation
|
||||||
|
float bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const;
|
||||||
|
void convertHsvToRgb( float hsv[], float rgb[] ) const;
|
||||||
|
void convertRgbToHsv( float rgb[], float hsv[] ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,16 @@ Text::Text():
|
|||||||
_color(1.0f,1.0f,1.0f,1.0f),
|
_color(1.0f,1.0f,1.0f,1.0f),
|
||||||
_drawMode(TEXT),
|
_drawMode(TEXT),
|
||||||
_kerningType(KERNING_DEFAULT),
|
_kerningType(KERNING_DEFAULT),
|
||||||
_lineCount(0)
|
_lineCount(0),
|
||||||
|
_backdropType(NONE),
|
||||||
|
_backdropHorizontalOffset(0.07f),
|
||||||
|
_backdropVerticalOffset(0.07f),
|
||||||
|
_backdropColor(0.0f, 0.0f, 0.0f, 1.0f),
|
||||||
|
_colorGradientMode(SOLID),
|
||||||
|
_colorGradientTopLeft(1.0f, 0.0f, 0.0f, 1.0f),
|
||||||
|
_colorGradientBottomLeft(0.0f, 1.0f, 0.0f, 1.0f),
|
||||||
|
_colorGradientBottomRight(0.0f, 0.0f, 1.0f, 1.0f),
|
||||||
|
_colorGradientTopRight(1.0f, 1.0f, 1.0f, 1.0f)
|
||||||
{
|
{
|
||||||
setUseDisplayList(false);
|
setUseDisplayList(false);
|
||||||
setSupportsDisplayList(false);
|
setSupportsDisplayList(false);
|
||||||
@ -693,6 +702,7 @@ void Text::computeGlyphRepresentation()
|
|||||||
setStateSet(const_cast<osg::StateSet*>((*_textureGlyphQuadMap.begin()).first.get()));
|
setStateSet(const_cast<osg::StateSet*>((*_textureGlyphQuadMap.begin()).first.get()));
|
||||||
|
|
||||||
computePositions();
|
computePositions();
|
||||||
|
computeColorGradients();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::computePositions()
|
void Text::computePositions()
|
||||||
@ -706,7 +716,6 @@ void Text::computePositions()
|
|||||||
|
|
||||||
void Text::computePositions(unsigned int contextID) const
|
void Text::computePositions(unsigned int contextID) const
|
||||||
{
|
{
|
||||||
|
|
||||||
switch(_alignment)
|
switch(_alignment)
|
||||||
{
|
{
|
||||||
case LEFT_TOP: _offset.set(_textBB.xMin(),_textBB.yMax(),_textBB.zMin()); break;
|
case LEFT_TOP: _offset.set(_textBB.xMin(),_textBB.yMax(),_textBB.zMin()); break;
|
||||||
@ -730,8 +739,6 @@ void Text::computePositions(unsigned int contextID) const
|
|||||||
case RIGHT_BOTTOM_BASE_LINE: _offset.set(_textBB.xMax(),-_characterHeight*(_lineCount-1),0.0f); break;
|
case RIGHT_BOTTOM_BASE_LINE: _offset.set(_textBB.xMax(),-_characterHeight*(_lineCount-1),0.0f); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AutoTransformCache& atc = _autoTransformCache[contextID];
|
AutoTransformCache& atc = _autoTransformCache[contextID];
|
||||||
osg::Matrix& matrix = atc._matrix;
|
osg::Matrix& matrix = atc._matrix;
|
||||||
|
|
||||||
@ -809,11 +816,9 @@ void Text::computePositions(unsigned int contextID) const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (_autoRotateToScreen)
|
if (_autoRotateToScreen)
|
||||||
{
|
{
|
||||||
matrix.postMult(rotate_matrix);
|
matrix.postMult(rotate_matrix);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_rotation.zeroRotation() )
|
if (!_rotation.zeroRotation() )
|
||||||
@ -834,8 +839,6 @@ void Text::computePositions(unsigned int contextID) const
|
|||||||
matrix.makeTranslate(_position-_offset);
|
matrix.makeTranslate(_position-_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// now apply matrix to the glyphs.
|
// now apply matrix to the glyphs.
|
||||||
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
||||||
titr!=_textureGlyphQuadMap.end();
|
titr!=_textureGlyphQuadMap.end();
|
||||||
@ -857,12 +860,508 @@ void Text::computePositions(unsigned int contextID) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computeBackdropPositions(contextID);
|
||||||
|
|
||||||
_normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix);
|
_normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix);
|
||||||
_normal.normalize();
|
_normal.normalize();
|
||||||
|
|
||||||
const_cast<Text*>(this)->dirtyBound();
|
const_cast<Text*>(this)->dirtyBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Presumes the atc matrix is already up-to-date
|
||||||
|
void Text::computeBackdropPositions(unsigned int contextID) const
|
||||||
|
{
|
||||||
|
if(_backdropType == NONE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float width = 0.0f;
|
||||||
|
float height = 0.0f;
|
||||||
|
float running_width = 0.0f;
|
||||||
|
float running_height = 0.0f;
|
||||||
|
float avg_width = 0.0f;
|
||||||
|
float avg_height = 0.0f;
|
||||||
|
int counter = 0;
|
||||||
|
unsigned int i;
|
||||||
|
AutoTransformCache& atc = _autoTransformCache[contextID];
|
||||||
|
osg::Matrix& matrix = atc._matrix;
|
||||||
|
|
||||||
|
// This section is going to try to compute the average width and height
|
||||||
|
// for a character among the text. The reason I shift by an
|
||||||
|
// average amount per-character instead of shifting each character
|
||||||
|
// by its per-instance amount is because it may look strange to see
|
||||||
|
// the individual backdrop text letters not space themselves the same
|
||||||
|
// way the foreground text does. Using one value gives uniformity.
|
||||||
|
for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
|
||||||
|
const_titr!=_textureGlyphQuadMap.end();
|
||||||
|
++const_titr)
|
||||||
|
{
|
||||||
|
const GlyphQuads& glyphquad = const_titr->second;
|
||||||
|
const GlyphQuads::Coords2& coords2 = glyphquad._coords;
|
||||||
|
for(i = 0; i < coords2.size(); i+=4)
|
||||||
|
{
|
||||||
|
width = coords2[i+2].x() - coords2[i].x();
|
||||||
|
height = coords2[i].y() - coords2[i+1].y();
|
||||||
|
|
||||||
|
running_width += width;
|
||||||
|
running_height += height;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avg_width = running_width/counter;
|
||||||
|
avg_height = running_height/counter;
|
||||||
|
|
||||||
|
// now apply matrix to the glyphs.
|
||||||
|
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
||||||
|
titr!=_textureGlyphQuadMap.end();
|
||||||
|
++titr)
|
||||||
|
{
|
||||||
|
GlyphQuads& glyphquad = titr->second;
|
||||||
|
GlyphQuads::Coords2& coords2 = glyphquad._coords;
|
||||||
|
|
||||||
|
unsigned int backdrop_index;
|
||||||
|
unsigned int max_backdrop_index;
|
||||||
|
if(_backdropType == OUTLINE)
|
||||||
|
{
|
||||||
|
// For outline, we want to draw the in every direction
|
||||||
|
backdrop_index = 0;
|
||||||
|
max_backdrop_index = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Yes, this may seem a little strange,
|
||||||
|
// but since the code is using references,
|
||||||
|
// I would have to duplicate the following code twice
|
||||||
|
// for each part of the if/else because I can't
|
||||||
|
// declare a reference without setting it immediately
|
||||||
|
// and it wouldn't survive the scope.
|
||||||
|
// So it happens that the _backdropType value matches
|
||||||
|
// the index in the array I want to store the coordinates
|
||||||
|
// in. So I'll just setup the for-loop so it only does
|
||||||
|
// the one direction I'm interested in.
|
||||||
|
backdrop_index = _backdropType;
|
||||||
|
max_backdrop_index = _backdropType+1;
|
||||||
|
}
|
||||||
|
for( ; backdrop_index < max_backdrop_index; backdrop_index++)
|
||||||
|
{
|
||||||
|
GlyphQuads::Coords3& transformedCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
|
||||||
|
unsigned int numCoords = coords2.size();
|
||||||
|
if (numCoords!=transformedCoords.size())
|
||||||
|
{
|
||||||
|
transformedCoords.resize(numCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<numCoords;++i)
|
||||||
|
{
|
||||||
|
float horizontal_shift_direction;
|
||||||
|
float vertical_shift_direction;
|
||||||
|
switch(backdrop_index)
|
||||||
|
{
|
||||||
|
case DROP_SHADOW_BOTTOM_RIGHT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 1.0f;
|
||||||
|
vertical_shift_direction = -1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_CENTER_RIGHT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 1.0f;
|
||||||
|
vertical_shift_direction = 0.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_RIGHT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 1.0f;
|
||||||
|
vertical_shift_direction = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_BOTTOM_CENTER:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 0.0f;
|
||||||
|
vertical_shift_direction = -1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_CENTER:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 0.0f;
|
||||||
|
vertical_shift_direction = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_BOTTOM_LEFT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = -1.0f;
|
||||||
|
vertical_shift_direction = -1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_CENTER_LEFT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = -1.0f;
|
||||||
|
vertical_shift_direction = 0.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_LEFT:
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = -1.0f;
|
||||||
|
vertical_shift_direction = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // error
|
||||||
|
{
|
||||||
|
horizontal_shift_direction = 1.0f;
|
||||||
|
vertical_shift_direction = -1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we have one more issue to deal with.
|
||||||
|
// Now that the text takes more space, we need
|
||||||
|
// to adjust the size of the bounding box.
|
||||||
|
switch(_backdropType)
|
||||||
|
{
|
||||||
|
case DROP_SHADOW_BOTTOM_RIGHT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin(),
|
||||||
|
_textBB.yMin() - avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax() + avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMax(),
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_CENTER_RIGHT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin(),
|
||||||
|
_textBB.yMin(),
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax() + avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMax(),
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_RIGHT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin(),
|
||||||
|
_textBB.yMin(),
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax() + avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMax() + avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_BOTTOM_CENTER:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin(),
|
||||||
|
_textBB.yMin() - avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax(),
|
||||||
|
_textBB.yMax(),
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_CENTER:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin(),
|
||||||
|
_textBB.yMin(),
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax(),
|
||||||
|
_textBB.yMax() + avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_BOTTOM_LEFT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin() - avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMin() - avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax(),
|
||||||
|
_textBB.yMax(),
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_CENTER_LEFT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin() - avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMin(),
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax(),
|
||||||
|
_textBB.yMax(),
|
||||||
|
_textBB.zMax()
|
||||||
|
); break;
|
||||||
|
}
|
||||||
|
case DROP_SHADOW_TOP_LEFT:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin() - avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMin(),
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax(),
|
||||||
|
_textBB.yMax() + avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OUTLINE:
|
||||||
|
{
|
||||||
|
_textBB.set(
|
||||||
|
_textBB.xMin() - avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMin() - avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMin(),
|
||||||
|
_textBB.xMax() + avg_width * _backdropHorizontalOffset,
|
||||||
|
_textBB.yMax() + avg_height * _backdropVerticalOffset,
|
||||||
|
_textBB.zMax()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // error
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::computeColorGradients() const
|
||||||
|
{
|
||||||
|
switch(_colorGradientMode)
|
||||||
|
{
|
||||||
|
case SOLID:
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case PER_CHARACTER:
|
||||||
|
computeColorGradientsPerCharacter();
|
||||||
|
break;
|
||||||
|
case OVERALL:
|
||||||
|
computeColorGradientsOverall();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::computeColorGradientsOverall() const
|
||||||
|
{
|
||||||
|
|
||||||
|
float min_x = FLT_MAX;
|
||||||
|
float min_y = FLT_MAX;
|
||||||
|
float max_x = FLT_MIN;
|
||||||
|
float max_y = FLT_MIN;
|
||||||
|
|
||||||
|
float rgb_q11[3];
|
||||||
|
float hsv_q11[3];
|
||||||
|
float rgb_q12[3];
|
||||||
|
float hsv_q12[3];
|
||||||
|
float rgb_q21[3];
|
||||||
|
float hsv_q21[3];
|
||||||
|
float rgb_q22[3];
|
||||||
|
float hsv_q22[3];
|
||||||
|
|
||||||
|
float rgb[3];
|
||||||
|
float hsv[3];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
|
||||||
|
const_titr!=_textureGlyphQuadMap.end();
|
||||||
|
++const_titr)
|
||||||
|
{
|
||||||
|
const GlyphQuads& glyphquad = const_titr->second;
|
||||||
|
const GlyphQuads::Coords2& coords2 = glyphquad._coords;
|
||||||
|
|
||||||
|
for(i=0;i<coords2.size();++i)
|
||||||
|
{
|
||||||
|
// Min and Max are needed for color gradients
|
||||||
|
if(coords2[i].x() > max_x)
|
||||||
|
{
|
||||||
|
max_x = coords2[i].x();
|
||||||
|
}
|
||||||
|
if(coords2[i].x() < min_x)
|
||||||
|
{
|
||||||
|
min_x = coords2[i].x();
|
||||||
|
}
|
||||||
|
if(coords2[i].y() > max_y)
|
||||||
|
{
|
||||||
|
max_y = coords2[i].y();
|
||||||
|
}
|
||||||
|
if(coords2[i].y() < min_y)
|
||||||
|
{
|
||||||
|
min_y = coords2[i].y();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb_q11[0] = _colorGradientBottomLeft[0];
|
||||||
|
rgb_q11[1] = _colorGradientBottomLeft[1];
|
||||||
|
rgb_q11[2] = _colorGradientBottomLeft[2];
|
||||||
|
|
||||||
|
rgb_q12[0] = _colorGradientTopLeft[0];
|
||||||
|
rgb_q12[1] = _colorGradientTopLeft[1];
|
||||||
|
rgb_q12[2] = _colorGradientTopLeft[2];
|
||||||
|
|
||||||
|
rgb_q21[0] = _colorGradientBottomRight[0];
|
||||||
|
rgb_q21[1] = _colorGradientBottomRight[1];
|
||||||
|
rgb_q21[2] = _colorGradientBottomRight[2];
|
||||||
|
|
||||||
|
rgb_q22[0] = _colorGradientTopRight[0];
|
||||||
|
rgb_q22[1] = _colorGradientTopRight[1];
|
||||||
|
rgb_q22[2] = _colorGradientTopRight[2];
|
||||||
|
|
||||||
|
// for linear interpolation to look correct
|
||||||
|
// for colors and imitate what OpenGL does,
|
||||||
|
// we need to convert over to Hue-Saturation-Value
|
||||||
|
// and linear interpolate in that space.
|
||||||
|
// HSV will interpolate through the color spectrum.
|
||||||
|
// Now that I think about this, perhaps we could
|
||||||
|
// extend this to use function pointers or something
|
||||||
|
// so users may specify their own color interpolation
|
||||||
|
// scales such as Intensity, or Heated Metal, etc.
|
||||||
|
convertRgbToHsv(rgb_q11, hsv_q11);
|
||||||
|
convertRgbToHsv(rgb_q12, hsv_q12);
|
||||||
|
convertRgbToHsv(rgb_q21, hsv_q21);
|
||||||
|
convertRgbToHsv(rgb_q22, hsv_q22);
|
||||||
|
|
||||||
|
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
||||||
|
titr!=_textureGlyphQuadMap.end();
|
||||||
|
++titr)
|
||||||
|
{
|
||||||
|
GlyphQuads& glyphquad = titr->second;
|
||||||
|
GlyphQuads::Coords2& coords2 = glyphquad._coords;
|
||||||
|
GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
|
||||||
|
|
||||||
|
unsigned int numCoords = coords2.size();
|
||||||
|
if (numCoords!=colorCoords.size())
|
||||||
|
{
|
||||||
|
colorCoords.resize(numCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<numCoords;++i)
|
||||||
|
{
|
||||||
|
float hue = bilinearInterpolate(
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
min_y,
|
||||||
|
max_y,
|
||||||
|
coords2[i].x(),
|
||||||
|
coords2[i].y(),
|
||||||
|
hsv_q11[0],
|
||||||
|
hsv_q12[0],
|
||||||
|
hsv_q21[0],
|
||||||
|
hsv_q22[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
float saturation = bilinearInterpolate(
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
min_y,
|
||||||
|
max_y,
|
||||||
|
coords2[i].x(),
|
||||||
|
coords2[i].y(),
|
||||||
|
hsv_q11[1],
|
||||||
|
hsv_q12[1],
|
||||||
|
hsv_q21[1],
|
||||||
|
hsv_q22[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
float value = bilinearInterpolate(
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
min_y,
|
||||||
|
max_y,
|
||||||
|
coords2[i].x(),
|
||||||
|
coords2[i].y(),
|
||||||
|
hsv_q11[2],
|
||||||
|
hsv_q12[2],
|
||||||
|
hsv_q21[2],
|
||||||
|
hsv_q22[2]
|
||||||
|
);
|
||||||
|
// Alpha does not convert to HSV
|
||||||
|
float alpha = bilinearInterpolate(
|
||||||
|
min_x,
|
||||||
|
max_x,
|
||||||
|
min_y,
|
||||||
|
max_y,
|
||||||
|
coords2[i].x(),
|
||||||
|
coords2[i].y(),
|
||||||
|
_colorGradientBottomLeft[3],
|
||||||
|
_colorGradientTopLeft[3],
|
||||||
|
_colorGradientBottomRight[3],
|
||||||
|
_colorGradientTopRight[3]
|
||||||
|
);
|
||||||
|
|
||||||
|
hsv[0] = hue;
|
||||||
|
hsv[1] = saturation;
|
||||||
|
hsv[2] = value;
|
||||||
|
// Convert back to RGB
|
||||||
|
convertHsvToRgb(hsv, rgb);
|
||||||
|
colorCoords[i] = osg::Vec4(rgb[0],rgb[1],rgb[2],alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::computeColorGradientsPerCharacter() const
|
||||||
|
{
|
||||||
|
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
||||||
|
titr!=_textureGlyphQuadMap.end();
|
||||||
|
++titr)
|
||||||
|
{
|
||||||
|
GlyphQuads& glyphquad = titr->second;
|
||||||
|
GlyphQuads::Coords2& coords2 = glyphquad._coords;
|
||||||
|
GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
|
||||||
|
|
||||||
|
unsigned int numCoords = coords2.size();
|
||||||
|
if (numCoords!=colorCoords.size())
|
||||||
|
{
|
||||||
|
colorCoords.resize(numCoords);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned int i=0;i<numCoords;++i)
|
||||||
|
{
|
||||||
|
switch(i%4)
|
||||||
|
{
|
||||||
|
case 0: // top-left
|
||||||
|
{
|
||||||
|
colorCoords[i] = _colorGradientTopLeft;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: // bottom-left
|
||||||
|
{
|
||||||
|
colorCoords[i] = _colorGradientBottomLeft;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: // bottom-right
|
||||||
|
{
|
||||||
|
colorCoords[i] = _colorGradientBottomRight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: // top-right
|
||||||
|
{
|
||||||
|
colorCoords[i] = _colorGradientTopRight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: // error
|
||||||
|
{
|
||||||
|
colorCoords[i] = osg::Vec4(0.0f,0.0f,0.0f,1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Text::drawImplementation(osg::State& state) const
|
void Text::drawImplementation(osg::State& state) const
|
||||||
{
|
{
|
||||||
unsigned int contextID = state.getContextID();
|
unsigned int contextID = state.getContextID();
|
||||||
@ -932,14 +1431,19 @@ void Text::drawImplementation(osg::State& state) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
glNormal3fv(_normal.ptr());
|
glNormal3fv(_normal.ptr());
|
||||||
glColor4fv(_color.ptr());
|
|
||||||
|
|
||||||
if (_drawMode & TEXT)
|
if (_drawMode & TEXT)
|
||||||
{
|
{
|
||||||
|
|
||||||
state.disableAllVertexArrays();
|
state.disableAllVertexArrays();
|
||||||
|
|
||||||
for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
|
if(_backdropType != NONE)
|
||||||
|
{
|
||||||
|
// Do I really need to do this for glPolygonOffset?
|
||||||
|
glPushAttrib(GL_POLYGON_OFFSET_FILL);
|
||||||
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
}
|
||||||
|
for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
|
||||||
titr!=_textureGlyphQuadMap.end();
|
titr!=_textureGlyphQuadMap.end();
|
||||||
++titr)
|
++titr)
|
||||||
{
|
{
|
||||||
@ -948,14 +1452,67 @@ void Text::drawImplementation(osg::State& state) const
|
|||||||
|
|
||||||
const GlyphQuads& glyphquad = titr->second;
|
const GlyphQuads& glyphquad = titr->second;
|
||||||
|
|
||||||
|
// For backdrop text
|
||||||
|
if(_backdropType != NONE)
|
||||||
|
{
|
||||||
|
unsigned int backdrop_index;
|
||||||
|
unsigned int max_backdrop_index;
|
||||||
|
if(_backdropType == OUTLINE)
|
||||||
|
{
|
||||||
|
backdrop_index = 0;
|
||||||
|
max_backdrop_index = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
backdrop_index = _backdropType;
|
||||||
|
max_backdrop_index = _backdropType+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
|
||||||
|
state.disableColorPointer();
|
||||||
|
glColor4fv(_backdropColor.ptr());
|
||||||
|
|
||||||
|
for( ; backdrop_index < max_backdrop_index; backdrop_index++)
|
||||||
|
{
|
||||||
|
const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
|
||||||
|
if (!transformedBackdropCoords.empty())
|
||||||
|
{
|
||||||
|
state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
|
||||||
|
glPolygonOffset(-1.0f, -1.0f * (backdrop_index+1) );
|
||||||
|
glDrawArrays(GL_QUADS,0,transformedBackdropCoords.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end of backdrop text
|
||||||
|
|
||||||
const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
|
const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
|
||||||
if (!transformedCoords.empty())
|
if (!transformedCoords.empty())
|
||||||
{
|
{
|
||||||
state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
|
state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
|
||||||
state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
|
state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
|
||||||
|
|
||||||
glDrawArrays(GL_QUADS,0,transformedCoords.size());
|
if(_colorGradientMode == SOLID)
|
||||||
|
{
|
||||||
|
state.disableColorPointer();
|
||||||
|
glColor4fv(_color.ptr());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.setColorPointer( 4, GL_FLOAT, 0, &(glyphquad._colorCoords.front()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_backdropType != NONE)
|
||||||
|
{
|
||||||
|
// Make sure that the main (foreground) text is on top
|
||||||
|
glPolygonOffset(-10, -10);
|
||||||
|
}
|
||||||
|
glDrawArrays(GL_QUADS,0,transformedCoords.size());
|
||||||
|
glPolygonOffset(0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_backdropType != NONE)
|
||||||
|
{
|
||||||
|
glPopAttrib();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1007,9 +1564,6 @@ void Text::drawImplementation(osg::State& state) const
|
|||||||
glEnd();
|
glEnd();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// glPopMatrix();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::accept(osg::Drawable::ConstAttributeFunctor& af) const
|
void Text::accept(osg::Drawable::ConstAttributeFunctor& af) const
|
||||||
@ -1047,3 +1601,251 @@ void Text::releaseGLObjects(osg::State* state) const
|
|||||||
Drawable::releaseGLObjects(state);
|
Drawable::releaseGLObjects(state);
|
||||||
if (_font.valid()) _font->releaseGLObjects(state);
|
if (_font.valid()) _font->releaseGLObjects(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Text::setBackdropType(BackdropType type)
|
||||||
|
{
|
||||||
|
if (_backdropType==type) return;
|
||||||
|
|
||||||
|
_backdropType = type;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setBackdropOffset(float offset)
|
||||||
|
{
|
||||||
|
_backdropHorizontalOffset = offset;
|
||||||
|
_backdropVerticalOffset = offset;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setBackdropOffset(float horizontal, float vertical)
|
||||||
|
{
|
||||||
|
_backdropHorizontalOffset = horizontal;
|
||||||
|
_backdropVerticalOffset = vertical;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setBackdropColor(const osg::Vec4& color)
|
||||||
|
{
|
||||||
|
_backdropColor = color;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setColorGradientMode(ColorGradientMode mode)
|
||||||
|
{
|
||||||
|
if (_colorGradientMode==mode) return;
|
||||||
|
|
||||||
|
_colorGradientMode = mode;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight)
|
||||||
|
{
|
||||||
|
_colorGradientTopLeft = topLeft;
|
||||||
|
_colorGradientBottomLeft = bottomLeft;
|
||||||
|
_colorGradientBottomRight = bottomRight;
|
||||||
|
_colorGradientTopRight = topRight;
|
||||||
|
computeGlyphRepresentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formula for f(x,y) from Wikipedia "Bilinear interpolation", 2006-06-18
|
||||||
|
float Text::bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
((q11 / ((x2-x1)*(y2-y1))) * (x2-x)*(y2-y))
|
||||||
|
+ ((q21 / ((x2-x1)*(y2-y1))) * (x-x1)*(y2-y))
|
||||||
|
+ ((q12 / ((x2-x1)*(y2-y1))) * (x2-x)*(y-y1))
|
||||||
|
+ ((q22 / ((x2-x1)*(y2-y1))) * (x-x1)*(y-y1))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
** routines to convert between RGB and HSV
|
||||||
|
**
|
||||||
|
** Reference: Foley, van Dam, Feiner, Hughes,
|
||||||
|
** "Computer Graphics Principles and Practices,"
|
||||||
|
** Additon-Wesley, 1990, pp592-593.
|
||||||
|
**/
|
||||||
|
/*
|
||||||
|
* FUNCTION
|
||||||
|
* HsvRgb( hsv, rgb )
|
||||||
|
*
|
||||||
|
* DESCRIPTION
|
||||||
|
* convert a hue-saturation-value into a red-green-blue value
|
||||||
|
*
|
||||||
|
* NOTE
|
||||||
|
* Array sizes are 3
|
||||||
|
* Values are between 0.0 and 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Text::convertHsvToRgb( float hsv[], float rgb[] ) const
|
||||||
|
{
|
||||||
|
float h, s, v; /* hue, sat, value */
|
||||||
|
/* double delta; */ /* change in color value */
|
||||||
|
float r, g, b; /* red, green, blue */
|
||||||
|
float i, f, p, q, t; /* interim values */
|
||||||
|
|
||||||
|
|
||||||
|
/* guarantee valid input: */
|
||||||
|
|
||||||
|
h = hsv[0] / 60.f;
|
||||||
|
while( h >= 6.f ) h -= 6.f;
|
||||||
|
while( h < 0.f ) h += 6.f;
|
||||||
|
|
||||||
|
s = hsv[1];
|
||||||
|
if( s < 0.f )
|
||||||
|
s = 0.f;
|
||||||
|
if( s > 1.f )
|
||||||
|
s = 1.f;
|
||||||
|
|
||||||
|
v = hsv[2];
|
||||||
|
if( v < 0.f )
|
||||||
|
v = 0.f;
|
||||||
|
if( v > 1.f )
|
||||||
|
v = 1.f;
|
||||||
|
|
||||||
|
|
||||||
|
/* if sat==0, then is a gray: */
|
||||||
|
|
||||||
|
if( s == 0.0f )
|
||||||
|
{
|
||||||
|
rgb[0] = rgb[1] = rgb[2] = v;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* get an rgb from the hue itself: */
|
||||||
|
|
||||||
|
i = floor( h );
|
||||||
|
f = h - i;
|
||||||
|
p = v * ( 1.f - s );
|
||||||
|
q = v * ( 1.f - s*f );
|
||||||
|
t = v * ( 1.f - ( s * (1.f-f) ) );
|
||||||
|
|
||||||
|
switch( (int) i )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = v; g = t; b = p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
r = q; g = v; b = p;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
r = p; g = v; b = t;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
r = p; g = q; b = v;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
r = t; g = p; b = v;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
r = v; g = p; b = q;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* never happens? */
|
||||||
|
r = 0; g = 0; b = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rgb[0] = r;
|
||||||
|
rgb[1] = g;
|
||||||
|
rgb[2] = b;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FUNCTION
|
||||||
|
* RgbHsv
|
||||||
|
*
|
||||||
|
* DESCRIPTION
|
||||||
|
* convert a red-green-blue value into hue-saturation-value
|
||||||
|
*
|
||||||
|
* NOTE
|
||||||
|
* Array sizes are 3
|
||||||
|
* Values are between 0.0 and 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Text::convertRgbToHsv( float rgb[], float hsv[] ) const
|
||||||
|
{
|
||||||
|
float r, g, b; /* red, green, blue */
|
||||||
|
float min, max; /* min and max rgb values */
|
||||||
|
float fmin, fmax, diff; /* min, max, and range of rgb vals */
|
||||||
|
float hue, sat, value; /* h s v */
|
||||||
|
float cr, cg, cb; /* coefficients for computing hue */
|
||||||
|
|
||||||
|
|
||||||
|
/* determine min and max color primary values: */
|
||||||
|
|
||||||
|
r = rgb[0]; g = rgb[1]; b = rgb[2];
|
||||||
|
min = r; max = r;
|
||||||
|
if( g < min ) min = g;
|
||||||
|
if( g > max ) max = g;
|
||||||
|
if( b < min ) min = b;
|
||||||
|
if( b > max ) max = b;
|
||||||
|
|
||||||
|
fmin = min;
|
||||||
|
fmax = max;
|
||||||
|
diff = fmax - fmin;
|
||||||
|
|
||||||
|
|
||||||
|
/* get value and saturation: */
|
||||||
|
|
||||||
|
value = fmax;
|
||||||
|
if( max == 0.f )
|
||||||
|
sat = 0.0f;
|
||||||
|
else
|
||||||
|
sat = diff/fmax;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* compute hue: */
|
||||||
|
|
||||||
|
if( sat == 0.0f )
|
||||||
|
hue = 0.0f;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float inv_diff = 1.0f / diff;
|
||||||
|
cr = ( fmax-r ) * inv_diff;
|
||||||
|
cg = ( fmax-g ) * inv_diff;
|
||||||
|
cb = ( fmax-b ) * inv_diff;
|
||||||
|
|
||||||
|
if( max == r )
|
||||||
|
hue = (g-b) * inv_diff;
|
||||||
|
else if( max == g )
|
||||||
|
hue = 2.f + (b-r) * inv_diff;
|
||||||
|
else if( max == b )
|
||||||
|
hue = 4.f + (r-g) * inv_diff;
|
||||||
|
else
|
||||||
|
hue = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hue *= 60.0f;
|
||||||
|
if( hue < 0.0f )
|
||||||
|
hue += 360.0f;
|
||||||
|
if( hue > 360.0f )
|
||||||
|
hue -= 360.0f;
|
||||||
|
|
||||||
|
|
||||||
|
/* store output values: */
|
||||||
|
|
||||||
|
hsv[0] = hue;
|
||||||
|
hsv[1] = sat;
|
||||||
|
hsv[2] = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user