forked from 0ad/0ad
Grayscale effects for GUI sprites
This was SVN commit r1536.
This commit is contained in:
parent
76c67f029d
commit
849f50a500
@ -161,4 +161,5 @@
|
||||
<!ATTLIST effect
|
||||
add-color CDATA #IMPLIED
|
||||
multiply-color CDATA #IMPLIED
|
||||
grayscale CDATA #IMPLIED
|
||||
>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user