From 9af911808bde2b6aa26384cd4b1dcf697b68a2ca Mon Sep 17 00:00:00 2001
From: cybermind <iddqd_mail@mail.ru>
Date: Sun, 31 Mar 2013 14:59:38 +0600
Subject: [PATCH] [*] Grayscale works in OpenGL too

---
 src/include/icons.h   |  1 +
 src/include/video.h   |  4 ++--
 src/ui/icons.cpp      | 25 +++++------------------
 src/video/graphic.cpp | 46 ++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 51 insertions(+), 25 deletions(-)

diff --git a/src/include/icons.h b/src/include/icons.h
index 94a410799..f97a237dc 100644
--- a/src/include/icons.h
+++ b/src/include/icons.h
@@ -126,6 +126,7 @@ public:
 
 public:
 	CGraphic *G;              /// Graphic data
+	CGraphic *GrayScale;      /// Icon when drawn grayscaled
 	int Frame;                /// Frame number in graphic
 private:
 	std::string Ident;        /// Icon identifier
diff --git a/src/include/video.h b/src/include/video.h
index a86ae566b..020b4f3f7 100644
--- a/src/include/video.h
+++ b/src/include/video.h
@@ -107,11 +107,11 @@ public:
 	static CGraphic *New(const std::string &file, int w = 0, int h = 0);
 	static CGraphic *ForceNew(const std::string &file, int w = 0, int h = 0);
 
-	CGraphic *Clone() const;
+	CGraphic *Clone(bool grayscale = false) const;
 
 	static void Free(CGraphic *g);
 
-	void Load();
+	void Load(bool grayscale = false);
 	void Flip();
 	void UseDisplayFormat();
 	void Resize(int w, int h);
diff --git a/src/ui/icons.cpp b/src/ui/icons.cpp
index 448d34238..8307f089f 100644
--- a/src/ui/icons.cpp
+++ b/src/ui/icons.cpp
@@ -60,7 +60,7 @@ static IconMap Icons;   /// Map of ident to icon.
 /**
 **  CIcon constructor
 */
-CIcon::CIcon(const std::string &ident) : G(NULL), Frame(0), Ident(ident)
+CIcon::CIcon(const std::string &ident) : G(NULL), GrayScale(NULL), Frame(0), Ident(ident)
 {
 }
 
@@ -70,6 +70,7 @@ CIcon::CIcon(const std::string &ident) : G(NULL), Frame(0), Ident(ident)
 CIcon::~CIcon()
 {
 	CGraphic::Free(this->G);
+	CGraphic::Free(this->GrayScale);
 }
 
 /**
@@ -111,6 +112,7 @@ void CIcon::Load()
 {
 	Assert(G);
 	G->Load();
+	GrayScale = G->Clone(true);
 	if (Frame >= G->NumFrames) {
 		DebugPrint("Invalid icon frame: %s - %d\n" _C_ Ident.c_str() _C_ Frame);
 		Frame = 0;
@@ -134,30 +136,13 @@ void CIcon::DrawIcon(const CPlayer &player, const PixelPos &pos) const
 }
 
 /**
-**  Draw icon at pos.
+**  Draw grayscale icon at pos.
 **
 **  @param pos     display pixel position
 */
 void CIcon::DrawGrayscaleIcon(const PixelPos &pos) const
 {
-	SDL_LockSurface(this->G->Surface);
-	SDL_Color colors[256], backup[256];
-	SDL_Palette &pal = *this->G->Surface->format->palette;
-	memcpy(backup, pal.colors, sizeof(SDL_Color) * 256);
-	for (int i = 0; i < 256; ++i) {
-		int gray = 0.21 * pal.colors[i].r + 0.72 * pal.colors[i].g + 0.07 * pal.colors[i].b;
-		colors[i].r = colors[i].g = colors[i].b = gray;
-	}
-	SDL_SetColors(this->G->Surface, &colors[0], 0, 256);
-	if (this->G->SurfaceFlip) {
-		SDL_SetColors(this->G->SurfaceFlip, &colors[0], 0, 256);
-	}
-	SDL_UnlockSurface(this->G->Surface);
-	this->G->DrawFrameClip(this->Frame, pos.x, pos.y);
-	SDL_LockSurface(this->G->Surface);
-	SDL_SetColors(this->G->Surface, &backup[0], 0, 256);
-	SDL_UnlockSurface(this->G->Surface);
-
+	this->GrayScale->DrawFrameClip(this->Frame, pos.x, pos.y);
 }
 
 /**
diff --git a/src/video/graphic.cpp b/src/video/graphic.cpp
index 385826a2a..c609b0ba5 100644
--- a/src/video/graphic.cpp
+++ b/src/video/graphic.cpp
@@ -587,13 +587,15 @@ CGraphic *CGraphic::ForceNew(const std::string &file, int w, int h)
 
 /**
 **  Clone a graphic
+**
+**  @param grayscale  Make grayscale texture
 */
-CGraphic *CGraphic::Clone() const
+CGraphic *CGraphic::Clone(bool grayscale) const
 {
 	CGraphic *g = CGraphic::ForceNew(this->File, this->Width, this->Height);
 
 	if (this->IsLoaded()) {
-		g->Load();
+		g->Load(grayscale);
 	}
 
 	return g;
@@ -664,8 +666,10 @@ void CGraphic::GenFramesMap()
 
 /**
 **  Load a graphic
+**
+**  @param grayscale  Make a grayscale surface
 */
-void CGraphic::Load()
+void CGraphic::Load(bool grayscale)
 {
 	if (Surface) {
 		return;
@@ -700,6 +704,42 @@ void CGraphic::Load()
 
 	NumFrames = GraphicWidth / Width * GraphicHeight / Height;
 
+	if (grayscale) {
+		SDL_LockSurface(Surface);
+		const SDL_PixelFormat *f = Surface->format;
+		const int bpp = Surface->format->BytesPerPixel;
+		const double redGray = 0.21;
+		const double greenGray = 0.72;
+		const double blueGray = 0.07;
+		switch (bpp) {
+			case 1: {
+				SDL_Color colors[256];
+				SDL_Palette &pal = *Surface->format->palette;
+				for (int i = 0; i < 256; ++i) {
+					const int gray = redGray * pal.colors[i].r + greenGray * pal.colors[i].g + blueGray * pal.colors[i].b;
+					colors[i].r = colors[i].g = colors[i].b = gray;
+				}
+				SDL_SetColors(Surface, &colors[0], 0, 256);
+				break;
+			}
+			case 4: {
+				Uint32* p;
+				for (int i = 0; i < Height; ++i) {
+					for (int j = 0; j < Width; ++j) {
+						p = (Uint32 *)(Surface->pixels) + i * Width + j * bpp;
+						const Uint32 gray = ((Uint8)((*p) * redGray) >> f->Rshift) +
+							((Uint8)(*(p + 1) * greenGray) >> f->Gshift) +
+							((Uint8)(*(p + 2) * blueGray) >> f->Bshift) +
+							((Uint8)(*(p + 3)) >> f->Ashift);
+						*p = gray;
+					}
+				}
+				break;
+			}
+		}
+		SDL_UnlockSurface(Surface);
+	}
+
 #if defined(USE_OPENGL) || defined(USE_GLES)
 	if (UseOpenGL) {
 		MakeTexture(this);