From 314c32865b4367fe9549f4c2a71e2b0a6e3ae45a Mon Sep 17 00:00:00 2001
From: Benau <Benau@users.noreply.github.com>
Date: Sat, 7 Jan 2017 16:36:25 +0800
Subject: [PATCH] Allow using single color channel for font texture

---
 src/font/font_with_face.cpp       | 98 +++++++++++++++++--------------
 src/graphics/central_settings.cpp | 14 ++++-
 src/graphics/central_settings.hpp |  2 +
 src/graphics/stk_texture.cpp      | 22 +++++--
 4 files changed, 84 insertions(+), 52 deletions(-)

diff --git a/src/font/font_with_face.cpp b/src/font/font_with_face.cpp
index 7dd4691f5..37c5af420 100644
--- a/src/font/font_with_face.cpp
+++ b/src/font/font_with_face.cpp
@@ -22,6 +22,7 @@
 #include "font/font_manager.hpp"
 #include "font/font_settings.hpp"
 #include "graphics/2dutils.hpp"
+#include "graphics/central_settings.hpp"
 #include "graphics/irr_driver.hpp"
 #include "graphics/stk_texture.hpp"
 #include "graphics/stk_tex_manager.hpp"
@@ -131,13 +132,24 @@ void FontWithFace::loadGlyphInfo(wchar_t c)
  */
 void FontWithFace::createNewGlyphPage()
 {
-    uint8_t* data = new uint8_t[getGlyphPageSize() * getGlyphPageSize() * 4]();
+#ifndef SERVER_ONLY
+    uint8_t* data = new uint8_t[getGlyphPageSize() * getGlyphPageSize() *
+    (CVS->isARBTextureSwizzleUsable() ? 1 : 4)];
+#else
+    uint8_t* data = NULL;
+#endif
     m_current_height = 0;
     m_used_width = 0;
     m_used_height = 0;
     STKTexture* stkt = new STKTexture(data, typeid(*this).name() +
         StringUtils::toString(m_spritebank->getTextureCount()),
-        getGlyphPageSize());
+        getGlyphPageSize(),
+#ifndef SERVER_ONLY
+        CVS->isARBTextureSwizzleUsable()
+#else
+        false
+#endif
+        );
     m_spritebank->addTexture(stkt);
     STKTexManager::getInstance()->addTexture(stkt);
 }   // createNewGlyphPage
@@ -180,36 +192,6 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
         createNewGlyphPage();
     }
 
-    std::vector<uint32_t> image_data;
-    image_data.resize(texture_size.Width * texture_size.Height,
-        video::SColor(0, 255, 255, 255).color);
-    switch (bits.pixel_mode)
-    {
-        case FT_PIXEL_MODE_GRAY:
-        {
-            // Load the grayscale data in.
-            const float gray_count = static_cast<float>(bits.num_grays);
-            const unsigned int image_pitch =
-                4 * texture_size.Width / sizeof(unsigned int);
-            uint8_t* glyph_data = bits.buffer;
-            for (unsigned int y = 0; y < (unsigned int)bits.rows; y++)
-            {
-                uint8_t* row = glyph_data;
-                for (unsigned int x = 0; x < (unsigned int)bits.width; x++)
-                {
-                    image_data.data()[y * image_pitch + x] |=
-                        static_cast<uint32_t>(255.0f *
-                        (static_cast<float>(*row++) / gray_count)) << 24;
-                }
-                glyph_data += bits.pitch;
-            }
-            break;
-        }
-        default:
-            assert(false);
-    }
-
-    // Done creating a single glyph, now copy to the glyph page...
     // Determine the linebreak location
     if (m_used_width + texture_size.Width > getGlyphPageSize())
     {
@@ -222,19 +204,45 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi)
 #ifndef SERVER_ONLY
     video::ITexture* tex = m_spritebank->getTexture(cur_tex);
     glBindTexture(GL_TEXTURE_2D, tex->getOpenGLTextureName());
-    unsigned int format = GL_BGRA;
-#if defined(USE_GLES2)
-    if (!CVS->isEXTTextureFormatBGRA8888Usable())
-        format = GL_RGBA;
-#endif
-    glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height,
-        texture_size.Width, texture_size.Height, format, GL_UNSIGNED_BYTE,
-        image_data.data());
-    //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-    //static GLint swizzle_mask[] = { GL_ONE, GL_ONE, GL_ONE, GL_RED };
-    //glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask);
-    //glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height, bits.width, bits.rows, GL_RED, GL_UNSIGNED_BYTE, bits.buffer);
-    //glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    if (CVS->isARBTextureSwizzleUsable())
+    {
+        glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height,
+            bits.width, bits.rows, GL_RED, GL_UNSIGNED_BYTE, bits.buffer);
+    }
+    else
+    {
+        std::vector<uint32_t> image_data;
+        image_data.resize(texture_size.Width * texture_size.Height,
+            video::SColor(0, 255, 255, 255).color);
+        switch (bits.pixel_mode)
+        {
+            case FT_PIXEL_MODE_GRAY:
+            {
+                // Load the grayscale data in.
+                const float gray_count = static_cast<float>(bits.num_grays);
+                const unsigned int image_pitch =
+                    4 * texture_size.Width / sizeof(unsigned int);
+                uint8_t* glyph_data = bits.buffer;
+                for (unsigned int y = 0; y < (unsigned int)bits.rows; y++)
+                {
+                    uint8_t* row = glyph_data;
+                    for (unsigned int x = 0; x < (unsigned int)bits.width; x++)
+                    {
+                        image_data.data()[y * image_pitch + x] |=
+                            static_cast<uint32_t>(255.0f *
+                            (static_cast<float>(*row++) / gray_count)) << 24;
+                    }
+                    glyph_data += bits.pitch;
+                }
+                break;
+            }
+            default:
+                assert(false);
+        }
+        glTexSubImage2D(GL_TEXTURE_2D, 0, m_used_width, m_used_height,
+            texture_size.Width, texture_size.Height, GL_BGRA, GL_UNSIGNED_BYTE,
+            image_data.data());
+    }
     if (tex->hasMipMaps())
         glGenerateMipmap(GL_TEXTURE_2D);
     glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp
index dbe7ad034..ea245cfaa 100644
--- a/src/graphics/central_settings.cpp
+++ b/src/graphics/central_settings.cpp
@@ -51,6 +51,7 @@ void CentralVideoSettings::init()
     hasExplicitAttribLocation = false;
     hasGS = false;
     hasTextureFilterAnisotropic = false;
+    hasTextureSwizzle = false;
 
 #if defined(USE_GLES2)
     hasBGRA = false;
@@ -182,7 +183,11 @@ void CentralVideoSettings::init()
             hasGS = true;
             Log::info("GLDriver", "Geometry Shaders Present");
         }
-
+        if (hasGLExtension("GL_ARB_texture_swizzle"))
+        {
+            hasTextureSwizzle = true;
+            Log::info("GLDriver", "ARB Texture Swizzle Present");
+        }
         // Only unset the high def textures if they are set as default. If the
         // user has enabled them (bit 1 set), then leave them enabled.
         if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_HIGHDEFINITION_TEXTURES) &&
@@ -228,6 +233,7 @@ void CentralVideoSettings::init()
         if (m_glsl == true)
         {
             hasTextureStorage = true;
+            hasTextureSwizzle = true;
         }
 
         if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_FORMAT_BGRA8888) &&
@@ -447,4 +453,10 @@ bool CentralVideoSettings::supportsHardwareSkinning() const
 {
     return isARBUniformBufferObjectUsable();
 }
+
+bool CentralVideoSettings::isARBTextureSwizzleUsable() const
+{
+    return m_glsl && hasTextureSwizzle;
+}
+
 #endif   // !SERVER_ONLY
diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp
index 71a50fdf1..146160e0b 100644
--- a/src/graphics/central_settings.hpp
+++ b/src/graphics/central_settings.hpp
@@ -43,6 +43,7 @@ private:
     bool hasImageLoadStore;
     bool hasMultiDrawIndirect;
     bool hasTextureFilterAnisotropic;
+    bool hasTextureSwizzle;
 
 #if defined(USE_GLES2)
     bool hasBGRA;
@@ -82,6 +83,7 @@ public:
     bool isARBMultiDrawIndirectUsable() const;
     bool isARBExplicitAttribLocationUsable() const;
     bool isEXTTextureFilterAnisotropicUsable() const;
+    bool isARBTextureSwizzleUsable() const;
 
 #if defined(USE_GLES2)
     bool isEXTTextureFormatBGRA8888Usable() const;
diff --git a/src/graphics/stk_texture.cpp b/src/graphics/stk_texture.cpp
index 1afb76433..e2a3ec6b7 100644
--- a/src/graphics/stk_texture.cpp
+++ b/src/graphics/stk_texture.cpp
@@ -82,6 +82,8 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
     if (ProfileWorld::isNoGraphics())
     {
         m_texture_name = 1;
+        if (preload_data)
+            delete[] preload_data;
         return;
     }
 #ifndef SERVER_ONLY
@@ -156,8 +158,8 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
 
     const unsigned int w = m_size.Width;
     const unsigned int h = m_size.Height;
-    unsigned int format = GL_BGRA;
-    unsigned int internal_format = GL_RGBA;
+    unsigned int format = single_channel ? GL_RED : GL_BGRA;
+    unsigned int internal_format = single_channel ? GL_RED : GL_RGBA;
 
 #if !defined(USE_GLES2)
     if (m_mesh_texture && CVS->isTextureCompressionEnabled())
@@ -166,14 +168,14 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
             GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
             GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
     }
-    else
+    else if (!single_channel)
     {
         internal_format = m_srgb ? GL_SRGB_ALPHA : GL_RGBA;
     }
 #endif
 
 #if defined(USE_GLES2)
-    if (!CVS->isEXTTextureFormatBGRA8888Usable())
+    if (!CVS->isEXTTextureFormatBGRA8888Usable() && !single_channel)
     {
         format = GL_RGBA;
         for (unsigned int i = 0; i < w * h; i++)
@@ -206,6 +208,14 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
         glBindTexture(GL_TEXTURE_2D, m_texture_name);
         if (!reload)
         {
+            if (single_channel)
+            {
+                glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
+            }
             glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
                 GL_UNSIGNED_BYTE, data);
         }
@@ -220,7 +230,7 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
             glGenerateMipmap(GL_TEXTURE_2D);
     }
 
-    m_texture_size = w * h * 4 /*BRGA*/;
+    m_texture_size = w * h * (single_channel ? 1 : 4);
     if (no_upload)
         m_texture_image = orig_img;
     else if (orig_img)
@@ -454,10 +464,10 @@ bool STKTexture::hasMipMaps() const
 //-----------------------------------------------------------------------------
 void* STKTexture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
 {
-#ifndef SERVER_ONLY
     if (m_texture_image)
         return m_texture_image->lock();
 
+#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
     uint8_t* pixels = new uint8_t[m_size.Width * m_size.Height * 4]();
     GLint tmp_texture;
     glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmp_texture);