1
1
forked from 0ad/0ad

Grayscale effects for GUI sprites

This was SVN commit r1536.
This commit is contained in:
Ykkrosh 2004-12-19 12:20:04 +00:00
parent 76c67f029d
commit 849f50a500
7 changed files with 279 additions and 78 deletions

View File

@ -161,4 +161,5 @@
<!ATTLIST effect
add-color CDATA #IMPLIED
multiply-color CDATA #IMPLIED
grayscale CDATA #IMPLIED
>

View File

@ -1569,8 +1569,16 @@ void CGUI::Xeromyces_ReadEffects(XMBElement Element, CXeromyces* pFile, SGUIImag
} \
else
#define BOOL(xml, mem) \
if (attr_name == xml) \
{ \
effects.m_##mem = true; \
} \
else
COLOR("add-color", AddColor)
COLOR("multiply-color", MultiplyColor)
BOOL("grayscale", Greyscale)
{
debug_warn("Oops"); // DTD shouldn't allow this

View File

@ -1,22 +1,6 @@
#include "precompiled.h"
#include "CGUISprite.h"
CGUISpriteInstance::CGUISpriteInstance()
{
}
CGUISpriteInstance::CGUISpriteInstance(CStr SpriteName)
: m_SpriteName(SpriteName)
{
}
CGUISpriteInstance &CGUISpriteInstance::operator=(CStr SpriteName)
{
m_SpriteName = SpriteName;
Invalidate();
return *this;
}
void CGUISpriteInstance::Draw(CRect Size, int CellID, std::map<CStr, CGUISprite> &Sprites)
{
if (m_CachedSize != Size || m_CachedCellID != CellID)
@ -37,3 +21,30 @@ bool CGUISpriteInstance::IsEmpty() const
{
return m_SpriteName=="";
}
// Plus a load of constructors / assignment operators, which don't copy the
// DrawCall cache (to avoid losing track of who has allocated various bits
// of data):
CGUISpriteInstance::CGUISpriteInstance()
{
}
CGUISpriteInstance::CGUISpriteInstance(CStr SpriteName)
: m_SpriteName(SpriteName)
{
}
CGUISpriteInstance::CGUISpriteInstance(const CGUISpriteInstance &Sprite)
: m_SpriteName(Sprite.m_SpriteName)
{
}
CGUISpriteInstance &CGUISpriteInstance::operator=(CStr SpriteName)
{
m_SpriteName = SpriteName;
m_DrawCallCache.clear();
Invalidate();
return *this;
}

View File

@ -49,8 +49,10 @@ gee@pyro.nu
struct SGUIImageEffects
{
SGUIImageEffects() : m_Greyscale(false) {}
CColor m_AddColor;
CColor m_MultiplyColor;
bool m_Greyscale;
};
@ -141,6 +143,7 @@ class CGUISpriteInstance
public:
CGUISpriteInstance();
CGUISpriteInstance(CStr SpriteName);
CGUISpriteInstance(const CGUISpriteInstance &Sprite);
CGUISpriteInstance &operator=(CStr SpriteName);
void Draw(CRect Size, int CellID, std::map<CStr, CGUISprite> &Sprites);
void Invalidate();

View File

@ -10,36 +10,178 @@
using namespace GUIRenderer;
// Copyable texture Handle, for use in STL containers where the Handle should
// be freed when it's finished with.
Handle_rfcnt_tex::Handle_rfcnt_tex()
: h(0)
void DrawCalls::clear()
{
for (iterator it = begin(); it != end(); ++it)
{
delete it->m_Effects;
tex_free(it->m_TexHandle);
}
std::vector<SDrawCall>::clear();
}
DrawCalls::DrawCalls()
{
}
Handle_rfcnt_tex::Handle_rfcnt_tex(Handle h_)
: h(h_)
// Never copy anything (to avoid losing track of who owns various pointers):
DrawCalls::DrawCalls(const DrawCalls&)
{
}
Handle_rfcnt_tex::Handle_rfcnt_tex(const Handle_rfcnt_tex& that)
const DrawCalls& DrawCalls::operator=(const DrawCalls&)
{
h = that.h;
if (h) h_add_ref(h);
}
Handle_rfcnt_tex::~Handle_rfcnt_tex()
{
if (h) tex_free(h);
}
Handle_rfcnt_tex& Handle_rfcnt_tex::operator=(Handle h_)
{
h = h_;
return *this;
}
DrawCalls::~DrawCalls()
{
clear();
std::vector<SDrawCall>::~vector();
}
// Implementations of graphical effects
class Effect_AddColor : public IGLState
{
public:
Effect_AddColor(CColor c) : m_Color(c) {}
~Effect_AddColor() {}
void Set(Handle tex)
{
glColor4fv(m_Color.FloatArray());
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
tex_bind(tex);
}
void Unset()
{
}
private:
CColor m_Color;
};
class Effect_MultiplyColor : public IGLState
{
public:
Effect_MultiplyColor(CColor c) : m_Color(c) {}
~Effect_MultiplyColor() {}
void Set(Handle tex)
{
glColor4fv(m_Color.FloatArray());
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
tex_bind(tex);
}
void Unset()
{
}
private:
CColor m_Color;
};
#define X(n) (n##f/2.0f + 0.5f)
const float GreyscaleDotColor[4] = { X(0.3), X(0.59), X(0.11), 1.0f };
#undef X
const float GreyscaleInterpColor0[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
const float GreyscaleInterpColor1[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
class Effect_Greyscale : public IGLState
{
public:
~Effect_Greyscale() {}
void Set(Handle tex)
{
/*
For the main conversion, use GL_DOT3_RGB, which is defined as
L = 4 * ((Arg0r - 0.5) * (Arg1r - 0.5)+
(Arg0g - 0.5) * (Arg1g - 0.5)+
(Arg0b - 0.5) * (Arg1b - 0.5))
where each of the RGB components is given the value 'L'.
Use the magical luminance formula
L = 0.3R + 0.59G + 0.11B
to calculate the greyscale value.
But to work around the annoying "Arg0-0.5", we need to calculate
Arg0+0.5. But we also need to scale it into the range 0.5-1.0, else
Arg0>0.5 will be clamped to 1.0. So use GL_INTERPOLATE, which outputs:
A0 * A2 + A1 * (1 - A2)
and set A2 = 0.5, A1 = 1.0, and A0 = texture (i.e. interpolating halfway
between the texture and {1,1,1}) giving
A0/2 + 0.5
and use that as Arg0.
So L = 4*(A0/2 * (Arg1-.5))
= 2 (Rx+Gy+Bz) (where Arg1 = {x+0.5, y+0.5, z+0.5})
= 2x R + 2y G + 2z B
= 0.3R + 0.59G + 0.11B
so e.g. 2y = 0.59 = 2(Arg1g-0.5) => Arg1g = 0.59/2+0.5
which fortunately doesn't get clamped.
So, just implement that:
*/
// TODO: Render all greyscale objects at the same time, to reduce
// the number of times the following code is called - it looks like
// a rather worrying amount of work for rendering a single button...
// Texture unit 0:
glEnable(GL_TEXTURE_2D);
tex_bind(tex);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleInterpColor0);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
glColor4fv(GreyscaleInterpColor1);
// Texture unit 1:
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
tex_bind(tex);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, GreyscaleDotColor);
}
void Unset()
{
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
}
};
// Functions to perform drawing-related actions:
void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect &Size, int CellID, std::map<CStr, CGUISprite> &Sprites)
@ -47,6 +189,7 @@ void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect
// This is called only when something has changed (like the size of the
// sprite), so it doesn't need to be particularly efficient.
// Clean up the old data
Calls.clear();
std::map<CStr, CGUISprite>::iterator it (Sprites.find(SpriteName));
@ -66,7 +209,8 @@ void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect
Calls.reserve(it->second.m_Images.size());
// Iterate through all the sprite's images
// Iterate through all the sprite's images, loading the texture and
// calculating the texture coordinates
std::vector<SGUIImage>::const_iterator cit;
for (cit = it->second.m_Images.begin(); cit != it->second.m_Images.end(); ++cit)
{
@ -100,13 +244,16 @@ void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect
// TODO: Detect the presence of an alpha channel in a nicer way
Call.m_EnableBlending = (TexFormat == GL_RGBA || TexFormat == GL_BGRA);
// Textures are positioned by defining a rectangular block of the
// texture (usually the whole texture), and a rectangular block on
// the screen. The texture is positioned to make those blocks line up.
// Get the screen's position/size for the block
CRect BlockScreen = cit->m_TextureSize.GetClientArea(ObjectSize);
// Get the texture's position/size for the block:
CRect BlockTex;
@ -161,13 +308,49 @@ void GUIRenderer::UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect
else
{
Call.m_TexHandle = 0;
// Enable blending if it's transparent (allowing a little error in the calculations)
Call.m_EnableBlending = !(fabs(cit->m_BackColor.a - 1.0f) < 0.0000001f);
}
Call.m_BackColor = cit->m_BackColor;
Call.m_BorderColor = cit->m_Border ? cit->m_BorderColor : CColor();
Call.m_DeltaZ = cit->m_DeltaZ;
Call.m_Effects = cit->m_Effects;
if (cit->m_Effects)
{
if (cit->m_Effects->m_AddColor != CColor())
Call.m_Effects = new Effect_AddColor(cit->m_Effects->m_AddColor);
else if (cit->m_Effects->m_MultiplyColor != CColor())
Call.m_Effects = new Effect_MultiplyColor(cit->m_Effects->m_MultiplyColor);
else if (cit->m_Effects->m_Greyscale)
Call.m_Effects = new Effect_Greyscale;
else
/* Slight confusion - why no effects? */
Call.m_Effects = NULL;
}
else
{
Call.m_Effects = NULL;
/* TODO: Delete this code
_CrtMemState s;
_CrtMemCheckpoint(&s);
struct ::_CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
size_t nDataSize;
int nBlockUse;
long lRequest;
};
debug_out("%d %s\n", s.pBlockHeader->lRequest, SpriteName.c_str());
*/
}
Calls.push_back(Call);
}
@ -187,34 +370,19 @@ void GUIRenderer::Draw(DrawCalls &Calls)
glEnable(GL_BLEND);
}
if (cit->m_TexHandle.h)
if (cit->m_TexHandle)
{
// TODO: Handle the GL state in a nicer way
glEnable(GL_TEXTURE_2D);
bool done = false;
if (cit->m_Effects)
cit->m_Effects->Set(cit->m_TexHandle);
else
{
if (cit->m_Effects->m_MultiplyColor != CColor())
{
glColor4fv(cit->m_Effects->m_MultiplyColor.FloatArray());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
done = true;
}
else if (cit->m_Effects && cit->m_Effects->m_AddColor != CColor())
{
glColor4fv(cit->m_Effects->m_AddColor.FloatArray());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
done = true;
}
}
if (! done)
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
tex_bind(cit->m_TexHandle.h);
tex_bind(cit->m_TexHandle);
}
glBegin(GL_QUADS);
glTexCoord2f(cit->m_TexCoords.right,cit->m_TexCoords.bottom);
@ -231,6 +399,8 @@ void GUIRenderer::Draw(DrawCalls &Calls)
glEnd();
if (cit->m_Effects)
cit->m_Effects->Unset();
}
else
{

View File

@ -10,23 +10,23 @@ struct SGUIImageEffects;
namespace GUIRenderer
{
struct Handle_rfcnt_tex
class IGLState
{
Handle h;
Handle_rfcnt_tex();
Handle_rfcnt_tex(Handle h_);
Handle_rfcnt_tex(const Handle_rfcnt_tex& that);
~Handle_rfcnt_tex();
Handle_rfcnt_tex& operator=(Handle h_);
public:
virtual ~IGLState() {};
virtual void Set(Handle tex)=0;
virtual void Unset()=0;
};
struct SDrawCall
{
Handle_rfcnt_tex m_TexHandle;
SDrawCall() : m_TexHandle(0), m_Effects(NULL) {}
Handle m_TexHandle;
bool m_EnableBlending;
SGUIImageEffects* m_Effects;
IGLState* m_Effects;
CRect m_Vertices;
CRect m_TexCoords;
@ -36,7 +36,15 @@ namespace GUIRenderer
CColor m_BackColor;
};
typedef std::vector<SDrawCall> DrawCalls;
class DrawCalls : public std::vector<SDrawCall>
{
public:
void clear();
DrawCalls();
DrawCalls(const DrawCalls&);
const DrawCalls& DrawCalls::operator=(const DrawCalls&);
~DrawCalls();
};
}
#include "gui/CGUISprite.h"

View File

@ -466,21 +466,21 @@ PS_RESULT GUI<T>::SetSetting(IGUIObject *pObject, const CStr& Setting, const T &
//
// If setting was "size", we need to re-cache itself and all children
if (Setting == CStr("size"))
if (Setting == "size")
{
RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize);
}
else
if (Setting == CStr("hidden"))
{
// Hiding an object requires us to reset it and all children
QueryResetting(pObject);
//RecurseObject(0, pObject, IGUIObject::ResetStates);
}
if (Setting == "hidden")
{
// Hiding an object requires us to reset it and all children
QueryResetting(pObject);
//RecurseObject(0, pObject, IGUIObject::ResetStates);
}
HandleMessage(pObject, SGUIMessage(GUIM_SETTINGS_UPDATED, Setting));
HandleMessage(pObject, SGUIMessage(GUIM_SETTINGS_UPDATED, Setting));
return PS_OK;
return PS_OK;
}
// Instantiate templated functions: