From 2483903b74bcada52f585eb8a141a9ff761917d2 Mon Sep 17 00:00:00 2001 From: Sergey Lipskiy Date: Sun, 12 Feb 2017 14:27:26 +0700 Subject: [PATCH] Refactor TextDrawer: remove direct use of OpenGL Remove DummyTextDrawer. --- projects/msvc12/GLideN64.vcxproj | 4 +- projects/msvc12/GLideN64.vcxproj.filters | 12 +- src/CMakeLists.txt | 1 + src/Graphics/Context.cpp | 15 +- src/Graphics/Context.h | 6 +- src/Graphics/ContextImpl.h | 3 +- .../GLSL/glsl_SpecialShadersFactory.cpp | 49 ++- .../GLSL/glsl_SpecialShadersFactory.h | 2 + .../OpenGLContext/opengl_ContextImpl.cpp | 16 +- .../OpenGLContext/opengl_ContextImpl.h | 8 +- .../OpenGLContext/opengl_DummyTextDrawer.h | 20 - .../OpenGLContext/opengl_TextDrawer.h | 16 - .../OpenGLContext/opengl_TextDrawerImpl.cpp | 408 ------------------ src/GraphicsDrawer.cpp | 9 +- src/GraphicsDrawer.h | 2 - src/TextDrawer.cpp | 374 ++++++++++++++++ src/TextDrawer.h | 28 ++ src/mupen64plus-video-gliden64.mk | 1 + 18 files changed, 483 insertions(+), 491 deletions(-) delete mode 100644 src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h delete mode 100644 src/Graphics/OpenGLContext/opengl_TextDrawer.h delete mode 100644 src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp create mode 100644 src/TextDrawer.cpp create mode 100644 src/TextDrawer.h diff --git a/projects/msvc12/GLideN64.vcxproj b/projects/msvc12/GLideN64.vcxproj index 23b79863..8c850bbb 100644 --- a/projects/msvc12/GLideN64.vcxproj +++ b/projects/msvc12/GLideN64.vcxproj @@ -350,6 +350,7 @@ + @@ -442,9 +443,7 @@ - - @@ -472,6 +471,7 @@ + diff --git a/projects/msvc12/GLideN64.vcxproj.filters b/projects/msvc12/GLideN64.vcxproj.filters index 5bc4376b..148f9fb2 100644 --- a/projects/msvc12/GLideN64.vcxproj.filters +++ b/projects/msvc12/GLideN64.vcxproj.filters @@ -329,6 +329,9 @@ Source Files\Graphics\OpenGL + + Source Files + @@ -595,12 +598,6 @@ Header Files - - Header Files\Graphics\OpenGL - - - Header Files\Graphics\OpenGL - Header Files\Graphics\OpenGL @@ -622,5 +619,8 @@ Header Files\Graphics\OpenGL + + Header Files + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 254cee5c..0250b906 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ set(GLideN64_SOURCES S2DEX2.cpp S2DEX.cpp SoftwareRender.cpp + TextDrawer.cpp TextureFilterHandler.cpp Textures.cpp Turbo3D.cpp diff --git a/src/Graphics/Context.cpp b/src/Graphics/Context.cpp index f6ed7db4..d3a9daa5 100644 --- a/src/Graphics/Context.cpp +++ b/src/Graphics/Context.cpp @@ -249,6 +249,11 @@ ShaderProgram * Context::createOrientationCorrectionShader() return m_impl->createOrientationCorrectionShader(); } +ShaderProgram * Context::createTextDrawerShader() +{ + return m_impl->createTextDrawerShader(); +} + void Context::resetShaderProgram() { m_impl->resetShaderProgram(); @@ -269,16 +274,6 @@ void Context::drawLine(f32 _width, SPVertex * _vertices) m_impl->drawLine(_width, _vertices); } -void Context::drawText(const char *_pText, float _x, float _y) -{ - m_impl->drawText(_pText, _x, _y); -} - -void Context::getTextSize(const char *_pText, float & _w, float & _h) -{ - m_impl->getTextSize(_pText, _w, _h); -} - f32 Context::getMaxLineWidth() { return m_impl->getMaxLineWidth(); diff --git a/src/Graphics/Context.h b/src/Graphics/Context.h index 5ba6c93c..e1914e52 100644 --- a/src/Graphics/Context.h +++ b/src/Graphics/Context.h @@ -220,6 +220,8 @@ namespace graphics { ShaderProgram * createOrientationCorrectionShader(); + ShaderProgram * createTextDrawerShader(); + void resetShaderProgram(); /*---------------Draw-------------*/ @@ -253,10 +255,6 @@ namespace graphics { f32 getMaxLineWidth(); - void drawText(const char *_pText, float _x, float _y); - - void getTextSize(const char *_pText, float & _w, float & _h); - /*---------------Misc-------------*/ bool isSupported(SpecialFeatures _feature) const; diff --git a/src/Graphics/ContextImpl.h b/src/Graphics/ContextImpl.h index c056c9f5..4d0eed63 100644 --- a/src/Graphics/ContextImpl.h +++ b/src/Graphics/ContextImpl.h @@ -55,13 +55,12 @@ namespace graphics { virtual ShaderProgram * createTexrectCopyShader() = 0; virtual ShaderProgram * createGammaCorrectionShader() = 0; virtual ShaderProgram * createOrientationCorrectionShader() = 0; + virtual ShaderProgram * createTextDrawerShader() = 0; virtual void resetShaderProgram() = 0; virtual void drawTriangles(const Context::DrawTriangleParameters & _params) = 0; virtual void drawRects(const Context::DrawRectParameters & _params) = 0; virtual void drawLine(f32 _width, SPVertex * _vertices) = 0; virtual f32 getMaxLineWidth() = 0; - virtual void drawText(const char *_pText, float _x, float _y) = 0; - virtual void getTextSize(const char *_pText, float & _w, float & _h) = 0; virtual bool isSupported(SpecialFeatures _feature) const = 0; virtual bool isError() const = 0; virtual bool isFramebufferError() const = 0; diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp index a7983061..7d9a7d89 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp @@ -387,6 +387,25 @@ namespace glsl { } }; + /*---------------TextDrawerShaderPart-------------*/ + + class TextDraw : public ShaderPart + { + public: + TextDraw(const opengl::GLInfo & _glinfo) + { + m_part = + "IN mediump vec2 vTexCoord0; \n" + "uniform sampler2D uTex0; \n" + "uniform lowp vec4 uColor; \n" + "OUT lowp vec4 fragColor; \n" + " \n" + "void main() \n" + "{ \n" + " fragColor = texture2D(uTex0, vTexCoord0).r * uColor; \n" + ; + } + }; /*---------------SpecialShader-------------*/ @@ -594,7 +613,7 @@ namespace glsl { } }; - /*---------------PostProcessorShaderPart-------------*/ + /*---------------PostProcessorShader-------------*/ typedef SpecialShader GammaCorrectionShaderBase; @@ -638,6 +657,29 @@ namespace glsl { } }; + /*---------------TexrectDrawerShader-------------*/ + + typedef SpecialShader TextDrawerShaderBase; + + class TextDrawerShader : public TextDrawerShaderBase + { + public: + TextDrawerShader(const opengl::GLInfo & _glinfo, + opengl::CachedUseProgram * _useProgram, + const ShaderPart * _vertexHeader, + const ShaderPart * _fragmentHeader, + const ShaderPart * _fragmentEnd) + : TextDrawerShaderBase(_glinfo, _useProgram, _vertexHeader, _fragmentHeader, _fragmentEnd) + { + m_useProgram->useProgram(m_program); + const int texLoc = glGetUniformLocation(GLuint(m_program), "uTex0"); + glUniform1i(texLoc, 0); + const int colorLoc = glGetUniformLocation(GLuint(m_program), "uColor"); + glUniform4fv(colorLoc, 1, config.font.colorf); + m_useProgram->useProgram(graphics::ObjectHandle()); + } + }; + /*---------------SpecialShadersFactory-------------*/ SpecialShadersFactory::SpecialShadersFactory(const opengl::GLInfo & _glinfo, @@ -691,4 +733,9 @@ namespace glsl { return new OrientationCorrectionShader(m_glinfo, m_useProgram, m_vertexHeader, m_fragmentHeader, m_fragmentEnd); } + graphics::ShaderProgram * SpecialShadersFactory::createTextDrawerShader() const + { + return new TextDrawerShader(m_glinfo, m_useProgram, m_vertexHeader, m_fragmentHeader, m_fragmentEnd); + } + } diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.h b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.h index 2878f9cf..f0401321 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.h +++ b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.h @@ -36,6 +36,8 @@ namespace glsl { graphics::ShaderProgram * createOrientationCorrectionShader() const; + graphics::ShaderProgram * createTextDrawerShader() const; + private: const opengl::GLInfo & m_glinfo; const ShaderPart * m_vertexHeader; diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp index aebe4e0b..e2292e8e 100644 --- a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp +++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp @@ -4,7 +4,6 @@ #include "opengl_ContextImpl.h" #include "opengl_BufferedDrawer.h" #include "opengl_UnbufferedDrawer.h" -#include "opengl_DummyTextDrawer.h" #include "opengl_ColorBufferReaderWithPixelBuffer.h" #include "opengl_ColorBufferReaderWithBufferStorage.h" //#include "opengl_ColorBufferReaderWithEGLImage.h" @@ -63,7 +62,6 @@ void ContextImpl::init() m_graphicsDrawer.reset(new BufferedDrawer(m_glInfo, m_cachedFunctions->getCachedVertexAttribArray(), m_cachedFunctions->getCachedBindBuffer())); else m_graphicsDrawer.reset(new UnbufferedDrawer(m_glInfo, m_cachedFunctions->getCachedVertexAttribArray())); - m_textDrawer.reset(new DummyTextDrawer); } m_combinerProgramBuilder.reset(new glsl::CombinerProgramBuilder(m_glInfo, m_cachedFunctions->getCachedUseProgram())); @@ -363,6 +361,11 @@ graphics::ShaderProgram * ContextImpl::createOrientationCorrectionShader() return m_specialShadersFactory->createOrientationCorrectionShader(); } +graphics::ShaderProgram * ContextImpl::createTextDrawerShader() +{ + return m_specialShadersFactory->createTextDrawerShader(); +} + void ContextImpl::resetShaderProgram() { m_cachedFunctions->getCachedUseProgram()->useProgram(graphics::ObjectHandle()); @@ -391,15 +394,6 @@ f32 ContextImpl::getMaxLineWidth() return lineWidthRange[1]; } -void ContextImpl::drawText(const char *_pText, float _x, float _y) -{ - m_textDrawer->drawText(_pText, _x, _y); -} - -void ContextImpl::getTextSize(const char *_pText, float & _w, float & _h) -{ - m_textDrawer->getTextSize(_pText, _w, _h); -} bool ContextImpl::isSupported(graphics::SpecialFeatures _feature) const { switch (_feature) { diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.h b/src/Graphics/OpenGLContext/opengl_ContextImpl.h index feb89af0..5e05eb98 100644 --- a/src/Graphics/OpenGLContext/opengl_ContextImpl.h +++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.h @@ -6,7 +6,6 @@ #include "opengl_GLInfo.h" #include "opengl_CachedFunctions.h" #include "opengl_GraphicsDrawer.h" -#include "opengl_TextDrawer.h" namespace glsl { class CombinerProgramBuilder; @@ -119,6 +118,8 @@ namespace opengl { graphics::ShaderProgram * createOrientationCorrectionShader() override; + graphics::ShaderProgram * createTextDrawerShader() override; + void resetShaderProgram() override; void drawTriangles(const graphics::Context::DrawTriangleParameters & _params) override; @@ -129,10 +130,6 @@ namespace opengl { f32 getMaxLineWidth() override; - void drawText(const char *_pText, float _x, float _y) override; - - void getTextSize(const char *_pText, float & _w, float & _h) override; - bool isSupported(graphics::SpecialFeatures _feature) const override; bool isError() const override; @@ -156,7 +153,6 @@ namespace opengl { std::unique_ptr m_fbTexFormats; std::unique_ptr m_graphicsDrawer; - std::unique_ptr m_textDrawer; std::unique_ptr m_combinerProgramBuilder; std::unique_ptr m_specialShadersFactory; diff --git a/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h b/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h deleted file mode 100644 index 4d84e843..00000000 --- a/src/Graphics/OpenGLContext/opengl_DummyTextDrawer.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef OPENGL_DUMMY_TEXTDRAWER_H -#define OPENGL_DUMMY_TEXTDRAWER_H - -#include "opengl_TextDrawer.h" - -namespace opengl { - - class DummyTextDrawer : public TextDrawer - { - public: - DummyTextDrawer() {} - ~DummyTextDrawer() {} - - void drawText(const char *_pText, float x, float y) const override {} - void getTextSize(const char *_pText, float & _w, float & _h) const override {} - }; - -} - -#endif // OPENGL_DUMMY_TEXTDRAWER_H diff --git a/src/Graphics/OpenGLContext/opengl_TextDrawer.h b/src/Graphics/OpenGLContext/opengl_TextDrawer.h deleted file mode 100644 index 2be42427..00000000 --- a/src/Graphics/OpenGLContext/opengl_TextDrawer.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef TEXTDRAWERIMPL_H -#define TEXTDRAWERIMPL_H - -namespace opengl { - - class TextDrawer - { - public: - virtual ~TextDrawer() {} - virtual void drawText(const char *_pText, float x, float y) const = 0; - virtual void getTextSize(const char *_pText, float & _w, float & _h) const = 0; - }; - -} - -#endif // TEXTDRAWERIMPL_H diff --git a/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp b/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp deleted file mode 100644 index 1d01a7d9..00000000 --- a/src/Graphics/OpenGLContext/opengl_TextDrawerImpl.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/* Draw text on screen. - * Requires freetype library. - * Code is taken from "OpenGL source examples from the OpenGL Programming wikibook: - * http://en.wikibooks.org/wiki/OpenGL_Programming" - */ - -#define NOMINMAX - -#include -#include -#include -#include -#include - -#include -#include FT_FREETYPE_H - -#include "opengl_TextDrawerImpl.h" -#include -#include -#include -#include - -#include -#include - -//#include "ShaderUtils.h" - -struct point { - GLfloat x; - GLfloat y; - GLfloat s; - GLfloat t; - point() : x(0), y(0), s(0), t(0) {} - point(GLfloat _x, GLfloat _y, GLfloat _s, GLfloat _t) : x(_x), y(_y), s(_s), t(_t) {} -}; - -// Maximum texture width -#define MAXWIDTH 1024 - -#if defined(GLES3_1) -#define SHADER_VERSION "#version 310 es \n" -#elif defined(GLES3) -#define SHADER_VERSION "#version 300 es \n" -#elif defined(GLES2) -#define SHADER_VERSION "#version 100 \n" -#else -#define SHADER_VERSION "#version 330 core \n" -#endif - -#ifdef GLES2 -const GLenum monohromeformat = GL_LUMINANCE; -const GLenum monohromeInternalformat = GL_LUMINANCE; -#else -const GLenum monohromeformat = GL_RED; -const GLenum monohromeInternalformat = GL_R8; -#endif // GLES2 - -static -const char * strDrawTextVertexShader = -SHADER_VERSION -"#if (__VERSION__ > 120) \n" -"# define IN in \n" -"# define OUT out \n" -"#else \n" -"# define IN attribute \n" -"# define OUT varying \n" -"#endif // __VERSION \n" -"IN highp vec4 aRectPosition; \n" -"OUT mediump vec2 texpos; \n" -"void main(void) { \n" -" gl_Position = vec4(aRectPosition.xy, 0, 1); \n" -" texpos = aRectPosition.zw; \n" -"} \n" -; - -static -const char * strDrawTextFragmentShader = -SHADER_VERSION -"#if (__VERSION__ > 120) \n" -"# define IN in \n" -"# define OUT out \n" -"# define texture2D texture \n" -"#else \n" -"# define IN varying \n" -"# define OUT \n" -"#endif // __VERSION __ \n" -"IN mediump vec2 texpos; \n" -"uniform sampler2D uTex; \n" -"uniform lowp vec4 uColor; \n" -"OUT lowp vec4 fragColor; \n" -"void main(void) { \n" -" fragColor = texture2D(uTex, texpos).r * uColor; \n" -#ifdef GLES2 -" gl_FragColor = fragColor; \n" -#endif -"} \n" -; - -/** - * The atlas struct holds a texture that contains the visible US-ASCII characters - * of a certain font rendered with a certain character height. - * It also contains an array that contains all the information necessary to - * generate the appropriate vertex and texture coordinates for each character. - * - * After the constructor is run, you don't need to use any FreeType functions anymore. - */ -struct Atlas { - GLuint tex; // texture object - - int w; // width of texture in pixels - int h; // height of texture in pixels - - struct { - float ax; // advance.x - float ay; // advance.y - - float bw; // bitmap.width; - float bh; // bitmap.height; - - float bl; // bitmap_left; - float bt; // bitmap_top; - - float tx; // x offset of glyph in texture coordinates - float ty; // y offset of glyph in texture coordinates - } c[128]; // character information - - Atlas(FT_Face face, int height) { - FT_Set_Pixel_Sizes(face, 0, height); - FT_GlyphSlot g = face->glyph; - - int roww = 0; - int rowh = 0; - w = 0; - h = 0; - - memset(c, 0, sizeof c); - - /* Find minimum size for a texture holding all visible ASCII characters */ - for (int i = 32; i < 128; i++) { - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { - fprintf(stderr, "Loading character %c failed!\n", i); - continue; - } - if (roww + g->bitmap.width + 1 >= MAXWIDTH) { - w = std::max(w, roww); - h += rowh; - roww = 0; - rowh = 0; - } - roww += g->bitmap.width + 1; - rowh = std::max(rowh, (int)g->bitmap.rows); - } - - w = std::max(w, roww); - h += rowh; - - /* Create a texture that will be used to hold all ASCII glyphs */ - - graphics::ObjectHandle texHandle = gfxContext.createTexture(graphics::target::TEXTURE_2D); - tex = GLuint(texHandle); - - graphics::Context::InitTextureParams initParams; - initParams.handle = texHandle; - initParams.width = w; - initParams.height = h; - initParams.internalFormat = monohromeInternalformat; - initParams.format = monohromeformat; - initParams.dataType = graphics::datatype::UNSIGNED_BYTE; - gfxContext.init2DTexture(initParams); - - /* We require 1 byte alignment when uploading texture data */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - graphics::Context::TexParameters setParams; - setParams.handle = texHandle; - setParams.target = graphics::target::TEXTURE_2D; - setParams.minFilter = graphics::textureParameters::FILTER_LINEAR; - setParams.magFilter = graphics::textureParameters::FILTER_LINEAR; - setParams.wrapS = graphics::textureParameters::WRAP_CLAMP_TO_EDGE; - setParams.wrapT = graphics::textureParameters::WRAP_CLAMP_TO_EDGE; - gfxContext.setTextureParameters(setParams); - - /* Paste all glyph bitmaps into the texture, remembering the offset */ - int ox = 0; - int oy = 0; - - rowh = 0; - - for (int i = 32; i < 128; i++) { - if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { - fprintf(stderr, "Loading character %c failed!\n", i); - continue; - } - - if (ox + g->bitmap.width + 1 >= MAXWIDTH) { - oy += rowh; - rowh = 0; - ox = 0; - } - - glTexSubImage2D(GL_TEXTURE_2D, 0, ox, oy, g->bitmap.width, g->bitmap.rows, monohromeformat, GL_UNSIGNED_BYTE, g->bitmap.buffer); - c[i].ax = _FIXED2FLOAT(g->advance.x, 6); - c[i].ay = _FIXED2FLOAT(g->advance.y, 6); - - c[i].bw = (float)g->bitmap.width; - c[i].bh = (float)g->bitmap.rows; - - c[i].bl = (float)g->bitmap_left; - c[i].bt = (float)g->bitmap_top; - - c[i].tx = ox / (float)w; - c[i].ty = oy / (float)h; - - rowh = std::max(rowh, (int)g->bitmap.rows); - ox += g->bitmap.width + 1; - } - - fprintf(stderr, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024); - } - - ~Atlas() { - gfxContext.deleteTexture(graphics::ObjectHandle(tex)); - } -}; - -TextDrawer::TextDrawer() : - m_pAtlas(nullptr), m_program(0), m_uTex(0), m_uColor(0), m_vbo(0) -{} - -TextDrawer & TextDrawer::get() { - static TextDrawer drawer; - return drawer; -} - -static -bool getFontFileName(char * _strName) -{ -#ifdef OS_WINDOWS - char * pSysPath = getenv("WINDIR"); - if (pSysPath == nullptr) - return false; - sprintf(_strName, "%s/Fonts/%s", pSysPath, config.font.name.c_str()); -#elif defined (OS_ANDROID) - sprintf(_strName, "/system/fonts/%s", config.font.name.c_str()); -#elif defined (PANDORA) - sprintf(_strName, "/usr/share/fonts/truetype/%s", config.font.name.c_str()); -#else - sprintf(_strName, "/usr/share/fonts/truetype/freefont/%s", config.font.name.c_str()); -#endif - return true; -} - -FT_Library ft; -FT_Face face; - -void TextDrawer::init() -{ - if (m_pAtlas != nullptr) - return; - - char strBuffer[PLUGIN_PATH_SIZE]; - const char *fontfilename; - if (getFontFileName(strBuffer)) - fontfilename = strBuffer; - else - return; - - /* Initialize the FreeType2 library */ - if (FT_Init_FreeType(&ft)) { - fprintf(stderr, "Could not init freetype library\n"); - return; - } - - /* Load a font */ - if (FT_New_Face(ft, fontfilename, 0, &face)) { - fprintf(stderr, "Could not open font %s\n", fontfilename); - return; - } - - m_program = createRectShaderProgram(strDrawTextVertexShader, strDrawTextFragmentShader); - if(m_program == 0) - return; - - m_uTex = glGetUniformLocation(m_program, "uTex"); - m_uColor = glGetUniformLocation(m_program, "uColor"); - - if(m_uTex == -1 || m_uColor == -1) - return; - - // Create the vertex buffer object - glGenBuffers(1, &m_vbo); - - /* Create texture atlas for selected font size */ - m_pAtlas = new Atlas(face, config.font.size); -} - -void TextDrawer::destroy() -{ - if (m_pAtlas == nullptr) - return; - delete m_pAtlas; - m_pAtlas = nullptr; - glDeleteBuffers(1, &m_vbo); - m_vbo = 0; - glDeleteProgram(m_program); - m_program = 0; - FT_Done_Face(face); - FT_Done_FreeType(ft); -} - -/** - * Render text using the currently loaded font and currently set font size. - * Rendering starts at coordinates (x, y), z is always 0. - * The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy). - */ -void TextDrawer::renderText(const char *_pText, float _x, float _y) const -{ - if (m_pAtlas == nullptr) - return; - OGLVideo & ogl = video(); - const float sx = 2.0f / ogl.getWidth(); - const float sy = 2.0f / ogl.getHeight(); - - const u8 *p; - - glUseProgram(m_program); - - /* Enable blending, necessary for our alpha texture */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - - /* Set color */ - glUniform4fv(m_uColor, 1, config.font.colorf); - - /* Use the texture containing the atlas */ - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_pAtlas->tex); - glUniform1i(m_uTex, 0); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -#ifndef GLES2 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); -#endif - - /* Set up the VBO for our vertex data */ - glBindBuffer(GL_ARRAY_BUFFER, m_vbo); - glVertexAttribPointer(SC_RECT_POSITION, 4, GL_FLOAT, GL_FALSE, 0, 0); - - std::vector coords(6 * strlen(_pText)); - int c = 0; - - /* Loop through all characters */ - for (p = (const u8 *)_pText; *p; ++p) { - /* Calculate the vertex and texture coordinates */ - float x2 = _x + m_pAtlas->c[*p].bl * sx; - float y2 = -_y - m_pAtlas->c[*p].bt * sy; - float w = m_pAtlas->c[*p].bw * sx; - float h = m_pAtlas->c[*p].bh * sy; - - /* Advance the cursor to the start of the next character */ - _x += m_pAtlas->c[*p].ax * sx; - _y += m_pAtlas->c[*p].ay * sy; - - /* Skip glyphs that have no pixels */ - if (!w || !h) - continue; - - coords[c++] = point(x2, -y2, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty); - coords[c++] = point(x2 + w, -y2, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty); - coords[c++] = point(x2, -y2 - h, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); - coords[c++] = point(x2 + w, -y2, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty); - coords[c++] = point(x2, -y2 - h, m_pAtlas->c[*p].tx, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); - coords[c++] = point(x2 + w, -y2 - h, m_pAtlas->c[*p].tx + m_pAtlas->c[*p].bw / m_pAtlas->w, m_pAtlas->c[*p].ty + m_pAtlas->c[*p].bh / m_pAtlas->h); - } - - /* Draw all the character on the screen in one go */ - glBufferData(GL_ARRAY_BUFFER, coords.size()*sizeof(point), coords.data(), GL_DYNAMIC_DRAW); - glDrawArrays(GL_TRIANGLES, 0, c); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -void TextDrawer::getTextSize(const char *_pText, float & _w, float & _h) const -{ - _w = _h = 0; - if (m_pAtlas == nullptr) - return; - OGLVideo & ogl = video(); - const float sx = 2.0f / ogl.getWidth(); - const float sy = 2.0f / ogl.getHeight(); - float bw, bh; - - for (const u8 *p = (const u8 *)_pText; *p; ++p) { - bw = m_pAtlas->c[*p].bw * sx; - bh = m_pAtlas->c[*p].bh * sy; - - _w += m_pAtlas->c[*p].ax * sx; - _h += m_pAtlas->c[*p].ay * sy; - } - _w += bw; - _h += bh; -} diff --git a/src/GraphicsDrawer.cpp b/src/GraphicsDrawer.cpp index dd959900..ac29ba19 100644 --- a/src/GraphicsDrawer.cpp +++ b/src/GraphicsDrawer.cpp @@ -13,6 +13,7 @@ #include "NoiseTexture.h" #include "ZlutTexture.h" #include "PaletteTexture.h" +#include "TextDrawer.h" #include "FrameBuffer.h" #include "DepthBuffer.h" #include "FrameBufferInfo.h" @@ -1297,13 +1298,13 @@ void GraphicsDrawer::correctTexturedRectParams(TexturedRectParams & _params) void GraphicsDrawer::drawText(const char *_pText, float x, float y) { m_drawingState = DrawingState::Non; - gfxContext.drawText(_pText, x, y); + g_textDrawer.drawText(_pText, x, y); } void GraphicsDrawer::_drawOSD(const char *_pText, float _x, float & _y) { float tW, tH; - gfxContext.getTextSize(_pText, tW, tH); + g_textDrawer.getTextSize(_pText, tW, tH); const bool top = (config.posTop & config.onScreenDisplay.pos) != 0; const bool right = (config.onScreenDisplay.pos == Config::posTopRight) || (config.onScreenDisplay.pos == Config::posBottomRight); @@ -1352,7 +1353,7 @@ void GraphicsDrawer::drawOSD() const float vp = bottom ? -1 : 1; float hShift, vShift; - gfxContext.getTextSize("0", hShift, vShift); + g_textDrawer.getTextSize("0", hShift, vShift); hShift *= 0.5f; vShift *= 0.5f; const float x = hp - hShift * hp; @@ -1571,6 +1572,7 @@ void GraphicsDrawer::_initData() g_zlutTexture.init(); g_noiseTexture.init(); g_paletteTexture.init(); + g_textDrawer.init(); perf.reset(); FBInfo::fbInfo.reset(); m_texrectDrawer.init(); @@ -1590,6 +1592,7 @@ void GraphicsDrawer::_destroyData() { m_drawingState = DrawingState::Non; m_texrectDrawer.destroy(); + g_textDrawer.destroy(); g_paletteTexture.destroy(); g_zlutTexture.destroy(); g_noiseTexture.destroy(); diff --git a/src/GraphicsDrawer.h b/src/GraphicsDrawer.h index 2357c464..fb38a73d 100644 --- a/src/GraphicsDrawer.h +++ b/src/GraphicsDrawer.h @@ -7,8 +7,6 @@ #include "Graphics/Parameter.h" namespace graphics { - class DrawerImpl; - class TextDrawer; class CombinerProgram; } diff --git a/src/TextDrawer.cpp b/src/TextDrawer.cpp new file mode 100644 index 00000000..33309005 --- /dev/null +++ b/src/TextDrawer.cpp @@ -0,0 +1,374 @@ +/* Draw text on screen. + * Requires freetype library. + * Code is taken from "OpenGL source examples from the OpenGL Programming wikibook: + * http://en.wikibooks.org/wiki/OpenGL_Programming" + */ + +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include "Platform.h" +#include "DisplayWindow.h" +#include "GraphicsDrawer.h" +#include "Textures.h" +#include "Config.h" +#include "Log.h" + +#include "Graphics/Context.h" +#include "Graphics/Parameters.h" + +#include "TextDrawer.h" + +using namespace graphics; + +// Maximum texture width +#define MAXWIDTH 1024 + +TextDrawer g_textDrawer; + +/** + * The atlas struct holds a texture that contains the visible US-ASCII characters + * of a certain font rendered with a certain character height. + * It also contains an array that contains all the information necessary to + * generate the appropriate vertex and texture coordinates for each character. + * + * After the constructor is run, you don't need to use any FreeType functions anymore. + */ +struct Atlas { + CachedTexture * m_pTexture; // texture object + + int w; // width of texture in pixels + int h; // height of texture in pixels + + struct { + float ax; // advance.x + float ay; // advance.y + + float bw; // bitmap.width; + float bh; // bitmap.height; + + float bl; // bitmap_left; + float bt; // bitmap_top; + + float tx; // x offset of glyph in texture coordinates + float ty; // y offset of glyph in texture coordinates + } c[128]; // character information + + Atlas(FT_Face face, int height) + { + FT_Set_Pixel_Sizes(face, 0, height); + FT_GlyphSlot g = face->glyph; + + int roww = 0; + int rowh = 0; + w = 0; + h = 0; + + memset(c, 0, sizeof c); + + /* Find minimum size for a texture holding all visible ASCII characters */ + for (int i = 32; i < 128; i++) { + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + fprintf(stderr, "Loading character %c failed!\n", i); + continue; + } + if (roww + g->bitmap.width + 1 >= MAXWIDTH) { + w = std::max(w, roww); + h += rowh; + roww = 0; + rowh = 0; + } + roww += g->bitmap.width + 1; + rowh = std::max(rowh, (int)g->bitmap.rows); + } + + w = std::max(w, roww); + h += rowh; + + /* Create a texture that will be used to hold all ASCII glyphs */ + const FramebufferTextureFormats & fbTexFormats = gfxContext.getFramebufferTextureFormats(); + + m_pTexture = textureCache().addFrameBufferTexture(false); + m_pTexture->format = G_IM_FMT_I; + m_pTexture->clampS = 1; + m_pTexture->clampT = 1; + m_pTexture->frameBufferTexture = CachedTexture::fbOneSample; + m_pTexture->maskS = 0; + m_pTexture->maskT = 0; + m_pTexture->mirrorS = 0; + m_pTexture->mirrorT = 0; + m_pTexture->realWidth = w; + m_pTexture->realHeight = h; + m_pTexture->textureBytes = m_pTexture->realWidth * m_pTexture->realHeight * fbTexFormats.noiseFormatBytes; + textureCache().addFrameBufferTextureSize(m_pTexture->textureBytes); + + Context::InitTextureParams initParams; + initParams.handle = m_pTexture->name; + initParams.textureUnitIndex = textureIndices::Tex[0]; + initParams.width = w; + initParams.height = h; + initParams.internalFormat = fbTexFormats.noiseInternalFormat; + initParams.format = fbTexFormats.noiseFormat; + initParams.dataType = fbTexFormats.noiseType; + gfxContext.init2DTexture(initParams); + + Context::TexParameters setParams; + setParams.handle = m_pTexture->name; + setParams.textureUnitIndex = textureIndices::Tex[0]; + setParams.target = textureTarget::TEXTURE_2D; + setParams.minFilter = textureParameters::FILTER_LINEAR; + setParams.magFilter = textureParameters::FILTER_LINEAR; + setParams.wrapS = textureParameters::WRAP_CLAMP_TO_EDGE; + setParams.wrapT = textureParameters::WRAP_CLAMP_TO_EDGE; + gfxContext.setTextureParameters(setParams); + + /* Paste all glyph bitmaps into the texture, remembering the offset */ + + /* We require 1 byte alignment when uploading texture data */ + const s32 curUnpackAlignment = gfxContext.getTextureUnpackAlignment(); + gfxContext.setTextureUnpackAlignment(1); + + Context::UpdateTextureDataParams updateParams; + updateParams.handle = m_pTexture->name; + updateParams.textureUnitIndex = textureIndices::Tex[0]; + updateParams.format = initParams.format; + updateParams.internalFormat = initParams.internalFormat; + updateParams.dataType = initParams.dataType; + + int ox = 0; + int oy = 0; + rowh = 0; + + for (int i = 32; i < 128; i++) { + if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { + fprintf(stderr, "Loading character %c failed!\n", i); + continue; + } + + if (ox + g->bitmap.width + 1 >= MAXWIDTH) { + oy += rowh; + rowh = 0; + ox = 0; + } + + updateParams.x = ox; + updateParams.y = oy; + updateParams.width = g->bitmap.width; + updateParams.height = g->bitmap.rows; + updateParams.data = g->bitmap.buffer; + gfxContext.update2DTexture(updateParams); + + c[i].ax = _FIXED2FLOAT(g->advance.x, 6); + c[i].ay = _FIXED2FLOAT(g->advance.y, 6); + + c[i].bw = (float)g->bitmap.width; + c[i].bh = (float)g->bitmap.rows; + + c[i].bl = (float)g->bitmap_left; + c[i].bt = (float)g->bitmap_top; + + c[i].tx = ox / (float)w; + c[i].ty = oy / (float)h; + + rowh = std::max(rowh, (int)g->bitmap.rows); + ox += g->bitmap.width + 1; + } + + gfxContext.setTextureUnpackAlignment(curUnpackAlignment); + + LOG(LOG_VERBOSE, "Generated a %d x %d (%d kb) texture atlas\n", w, h, w * h / 1024); + } + + ~Atlas() { + textureCache().removeFrameBufferTexture(m_pTexture); + m_pTexture = nullptr; + } +}; + +static +bool getFontFileName(char * _strName) +{ +#ifdef OS_WINDOWS + char * pSysPath = getenv("WINDIR"); + if (pSysPath == nullptr) + return false; + sprintf(_strName, "%s/Fonts/%s", pSysPath, config.font.name.c_str()); +#elif defined (OS_ANDROID) + sprintf(_strName, "/system/fonts/%s", config.font.name.c_str()); +#elif defined (PANDORA) + sprintf(_strName, "/usr/share/fonts/truetype/%s", config.font.name.c_str()); +#else + sprintf(_strName, "/usr/share/fonts/truetype/freefont/%s", config.font.name.c_str()); +#endif + return true; +} + +FT_Library ft; +FT_Face face; + +void TextDrawer::init() +{ + char strBuffer[PLUGIN_PATH_SIZE]; + const char *fontfilename; + if (getFontFileName(strBuffer)) + fontfilename = strBuffer; + else + return; + + /* Initialize the FreeType2 library */ + if (FT_Init_FreeType(&ft)) { + fprintf(stderr, "Could not init freetype library\n"); + return; + } + + /* Load a font */ + if (FT_New_Face(ft, fontfilename, 0, &face)) { + fprintf(stderr, "Could not open font %s\n", fontfilename); + return; + } + + /* Create texture atlas for selected font size */ + m_atlas.reset(new Atlas(face, config.font.size)); + + m_program.reset(gfxContext.createTextDrawerShader()); +} + +void TextDrawer::destroy() +{ + m_atlas.reset(); + m_program.reset(); + FT_Done_Face(face); + FT_Done_FreeType(ft); +} + +/** + * Render text using the currently loaded font and currently set font size. + * Rendering starts at coordinates (x, y), z is always 0. + * The pixel coordinates that the FreeType2 library uses are scaled by (sx, sy). + */ +void TextDrawer::drawText(const char *_pText, float _x, float _y) const +{ + if (!m_atlas) + return; + + DisplayWindow & wnd = DisplayWindow::get(); + const float sx = 2.0f / wnd.getWidth(); + const float sy = 2.0f / wnd.getHeight(); + + const u8 *p; + + + std::vector coords; + coords.reserve(6 * strlen(_pText)); + + RectVertex rect; + rect.z = 0.0f; + rect.w = 1.0f; + + /* Loop through all characters */ + for (p = (const u8 *)_pText; *p; ++p) { + /* Calculate the vertex and texture coordinates */ + float x2 = _x + m_atlas->c[*p].bl * sx; + float y2 = -_y - m_atlas->c[*p].bt * sy; + float w = m_atlas->c[*p].bw * sx; + float h = m_atlas->c[*p].bh * sy; + + /* Advance the cursor to the start of the next character */ + _x += m_atlas->c[*p].ax * sx; + _y += m_atlas->c[*p].ay * sy; + + /* Skip glyphs that have no pixels */ + if (!w || !h) + continue; + + rect.x = x2; + rect.y = -y2; + rect.s0 = m_atlas->c[*p].tx; + rect.t0 = m_atlas->c[*p].ty; + coords.push_back(rect); + + rect.x = x2 + w; + rect.y = -y2; + rect.s0 = m_atlas->c[*p].tx + m_atlas->c[*p].bw / m_atlas->w; + rect.t0 = m_atlas->c[*p].ty; + coords.push_back(rect); + + rect.x = x2; + rect.y = -y2 - h; + rect.s0 = m_atlas->c[*p].tx; + rect.t0 = m_atlas->c[*p].ty + m_atlas->c[*p].bh / m_atlas->h; + coords.push_back(rect); + + rect.x = x2 + w; + rect.y = -y2; + rect.s0 = m_atlas->c[*p].tx + m_atlas->c[*p].bw / m_atlas->w; + rect.t0 = m_atlas->c[*p].ty; + coords.push_back(rect); + + rect.x = x2; + rect.y = -y2 - h; + rect.s0 = m_atlas->c[*p].tx; + rect.t0 = m_atlas->c[*p].ty + m_atlas->c[*p].bh / m_atlas->h; + coords.push_back(rect); + + rect.x = x2 + w; + rect.y = -y2 - h; + rect.s0 = m_atlas->c[*p].tx + m_atlas->c[*p].bw / m_atlas->w; + rect.t0 = m_atlas->c[*p].ty + m_atlas->c[*p].bh / m_atlas->h; + coords.push_back(rect); + } + + gfxContext.enable(enable::BLEND, true); + gfxContext.enable(enable::CULL_FACE, false); + gfxContext.enable(enable::DEPTH_TEST, false); + gfxContext.enableDepthWrite(false); + gfxContext.setBlending(blend::SRC_ALPHA, blend::ONE_MINUS_SRC_ALPHA); + m_program->activate(); + + Context::TexParameters setParams; + setParams.handle = m_atlas->m_pTexture->name; + setParams.textureUnitIndex = textureIndices::Tex[0]; + setParams.target = textureTarget::TEXTURE_2D; + setParams.minFilter = textureParameters::FILTER_LINEAR; + setParams.magFilter = textureParameters::FILTER_LINEAR; + setParams.wrapS = textureParameters::WRAP_CLAMP_TO_EDGE; + setParams.wrapT = textureParameters::WRAP_CLAMP_TO_EDGE; + setParams.maxMipmapLevel = Parameter(0); + gfxContext.setTextureParameters(setParams); + + Context::DrawRectParameters rectParams; + rectParams.mode = drawmode::TRIANGLES; + rectParams.rectColor.fill(0.0f); + rectParams.verticesCount = coords.size(); + rectParams.vertices = coords.data(); + rectParams.combiner = m_program.get(); + gfxContext.drawRects(rectParams); +} + +void TextDrawer::getTextSize(const char *_pText, float & _w, float & _h) const +{ + _w = _h = 0; + if (!m_atlas) + return; + + DisplayWindow & wnd = DisplayWindow::get(); + const float sx = 2.0f / wnd.getWidth(); + const float sy = 2.0f / wnd.getHeight(); + float bw, bh; + + for (const u8 *p = (const u8 *)_pText; *p; ++p) { + bw = m_atlas->c[*p].bw * sx; + bh = m_atlas->c[*p].bh * sy; + + _w += m_atlas->c[*p].ax * sx; + _h += m_atlas->c[*p].ay * sy; + } + _w += bw; + _h += bh; +} diff --git a/src/TextDrawer.h b/src/TextDrawer.h new file mode 100644 index 00000000..883160a9 --- /dev/null +++ b/src/TextDrawer.h @@ -0,0 +1,28 @@ +#ifndef TEXTDRAWER_H +#define TEXTDRAWER_H + +#include +#include "Graphics/ShaderProgram.h" +#include "Graphics/FramebufferTextureFormats.h" + +struct Atlas; + +class TextDrawer +{ +public: + void init(); + + void destroy(); + + void drawText(const char *_pText, float x, float y) const; + + void getTextSize(const char *_pText, float & _w, float & _h) const; + +private: + std::unique_ptr m_atlas; + std::unique_ptr m_program; +}; + +extern TextDrawer g_textDrawer; + +#endif // TEXTDRAWER_H diff --git a/src/mupen64plus-video-gliden64.mk b/src/mupen64plus-video-gliden64.mk index c4f88305..415e0b3a 100644 --- a/src/mupen64plus-video-gliden64.mk +++ b/src/mupen64plus-video-gliden64.mk @@ -65,6 +65,7 @@ MY_LOCAL_SRC_FILES := \ $(SRCDIR)/S2DEX2.cpp \ $(SRCDIR)/S2DEX.cpp \ $(SRCDIR)/SoftwareRender.cpp \ + $(SRCDIR)/TextDrawer.cpp \ $(SRCDIR)/TextureFilterHandler.cpp \ $(SRCDIR)/Textures.cpp \ $(SRCDIR)/Turbo3D.cpp \