1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-02 09:03:37 +00:00

Refactor TextDrawer: remove direct use of OpenGL

Remove DummyTextDrawer.
This commit is contained in:
Sergey Lipskiy 2017-02-12 14:27:26 +07:00
parent e011ee56b5
commit 2483903b74
18 changed files with 483 additions and 491 deletions

View File

@ -350,6 +350,7 @@
</ClCompile>
<ClCompile Include="..\..\src\RSP.cpp" />
<ClCompile Include="..\..\src\SoftwareRender.cpp" />
<ClCompile Include="..\..\src\TextDrawer.cpp" />
<ClCompile Include="..\..\src\TextureFilterHandler.cpp" />
<ClCompile Include="..\..\src\Textures.cpp" />
<ClCompile Include="..\..\src\Turbo3D.cpp" />
@ -442,9 +443,7 @@
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_ColorBufferReaderWithPixelBuffer.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_ContextImpl.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_GLInfo.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_DummyTextDrawer.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_GraphicsDrawer.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_TextDrawer.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_TextureManipulationObjectFactory.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_UnbufferedDrawer.h" />
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_Utils.h" />
@ -472,6 +471,7 @@
<ClInclude Include="..\..\src\GraphicsDrawer.h" />
<ClInclude Include="..\..\src\RSP.h" />
<ClInclude Include="..\..\src\SoftwareRender.h" />
<ClInclude Include="..\..\src\TextDrawer.h" />
<ClInclude Include="..\..\src\TextureFilterHandler.h" />
<ClInclude Include="..\..\src\Textures.h" />
<ClInclude Include="..\..\src\Turbo3D.h" />

View File

@ -329,6 +329,9 @@
<ClCompile Include="..\..\src\Graphics\OpenGLContext\opengl_BufferedDrawer.cpp">
<Filter>Source Files\Graphics\OpenGL</Filter>
</ClCompile>
<ClCompile Include="..\..\src\TextDrawer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\3DMath.h">
@ -595,12 +598,6 @@
<ClInclude Include="..\..\src\GraphicsDrawer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_DummyTextDrawer.h">
<Filter>Header Files\Graphics\OpenGL</Filter>
</ClInclude>
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_TextDrawer.h">
<Filter>Header Files\Graphics\OpenGL</Filter>
</ClInclude>
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_GraphicsDrawer.h">
<Filter>Header Files\Graphics\OpenGL</Filter>
</ClInclude>
@ -622,5 +619,8 @@
<ClInclude Include="..\..\src\Graphics\OpenGLContext\opengl_BufferedDrawer.h">
<Filter>Header Files\Graphics\OpenGL</Filter>
</ClInclude>
<ClInclude Include="..\..\src\TextDrawer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -48,6 +48,7 @@ set(GLideN64_SOURCES
S2DEX2.cpp
S2DEX.cpp
SoftwareRender.cpp
TextDrawer.cpp
TextureFilterHandler.cpp
Textures.cpp
Turbo3D.cpp

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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<VertexShaderTexturedRect, GammaCorrection> GammaCorrectionShaderBase;
@ -638,6 +657,29 @@ namespace glsl {
}
};
/*---------------TexrectDrawerShader-------------*/
typedef SpecialShader<VertexShaderTexturedRect, TextDraw> 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);
}
}

View File

@ -36,6 +36,8 @@ namespace glsl {
graphics::ShaderProgram * createOrientationCorrectionShader() const;
graphics::ShaderProgram * createTextDrawerShader() const;
private:
const opengl::GLInfo & m_glinfo;
const ShaderPart * m_vertexHeader;

View File

@ -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) {

View File

@ -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<graphics::FramebufferTextureFormats> m_fbTexFormats;
std::unique_ptr<GraphicsDrawer> m_graphicsDrawer;
std::unique_ptr<TextDrawer> m_textDrawer;
std::unique_ptr<glsl::CombinerProgramBuilder> m_combinerProgramBuilder;
std::unique_ptr<glsl::SpecialShadersFactory> m_specialShadersFactory;

View File

@ -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

View File

@ -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

View File

@ -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 <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <assert.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "opengl_TextDrawerImpl.h"
#include <RSP.h>
#include <Config.h>
#include <FBOTextureFormats.h>
#include <Log.h>
#include <Graphics/Context.h>
#include <Graphics/Parameters.h>
//#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<point> 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;
}

View File

@ -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();

View File

@ -7,8 +7,6 @@
#include "Graphics/Parameter.h"
namespace graphics {
class DrawerImpl;
class TextDrawer;
class CombinerProgram;
}

374
src/TextDrawer.cpp Normal file
View File

@ -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 <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <assert.h>
#include <ft2build.h>
#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<RectVertex> 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;
}

28
src/TextDrawer.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef TEXTDRAWER_H
#define TEXTDRAWER_H
#include <memory>
#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<Atlas> m_atlas;
std::unique_ptr<graphics::ShaderProgram> m_program;
};
extern TextDrawer g_textDrawer;
#endif // TEXTDRAWER_H

View File

@ -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 \