From b8a84d170a4dad3d245cddad4513831fc5291be7 Mon Sep 17 00:00:00 2001 From: Jason Brown Date: Sun, 18 Sep 2016 20:40:51 +0000 Subject: [PATCH] Added OSD Manager which depends on FreeType2. Added functions into GSDeviceOGL to render OSD and a point shader. --- cmake/SearchForStuff.cmake | 1 + cmake/SelectPcsx2Plugins.cmake | 2 +- plugins/GSdx/CMakeLists.txt | 2 + plugins/GSdx/GSDevice.cpp | 1 + plugins/GSdx/GSDevice.h | 4 + plugins/GSdx/GSDeviceOGL.cpp | 38 +++ plugins/GSdx/GSDeviceOGL.h | 4 + plugins/GSdx/GSOsdManager.cpp | 443 ++++++++++++++++++++++++++++++ plugins/GSdx/GSOsdManager.h | 108 ++++++++ plugins/GSdx/GSRenderer.cpp | 17 ++ plugins/GSdx/GSdx.cpp | 10 + plugins/GSdx/GSdx.vcxproj | 2 + plugins/GSdx/GSdx.vcxproj.filters | 6 + plugins/GSdx/stdafx.h | 5 + 14 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 plugins/GSdx/GSOsdManager.cpp create mode 100644 plugins/GSdx/GSOsdManager.h diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index 94c91fd7c8..42d7e12ba8 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -5,6 +5,7 @@ if (Linux) find_package(ALSA) endif() +find_package(Freetype) # GSdx OSD find_package(Gettext) # translation tool if(EXISTS ${PROJECT_SOURCE_DIR}/.git) find_package(Git) diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index be13a0a033..a5171288c5 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -141,7 +141,7 @@ endif() # -X11 # -zlib #--------------------------------------- -if(OPENGL_FOUND AND X11_FOUND AND GTKn_FOUND AND ZLIB_FOUND AND PNG_FOUND AND (EGL_FOUND OR NOT EGL_API)) +if(OPENGL_FOUND AND X11_FOUND AND GTKn_FOUND AND ZLIB_FOUND AND PNG_FOUND AND FREETYPE_FOUND AND (EGL_FOUND OR NOT EGL_API)) set(GSdx TRUE) elseif(NOT EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx") set(GSdx FALSE) diff --git a/plugins/GSdx/CMakeLists.txt b/plugins/GSdx/CMakeLists.txt index 287fbc3115..bb41be761a 100644 --- a/plugins/GSdx/CMakeLists.txt +++ b/plugins/GSdx/CMakeLists.txt @@ -86,6 +86,7 @@ set(GSdxSources GSRendererNull.cpp GSRendererOGL.cpp GSRendererSW.cpp + GSOsdManager.cpp GSSetting.cpp GSSetupPrimCodeGenerator.cpp GSSetupPrimCodeGenerator.x64.cpp @@ -193,6 +194,7 @@ set(GSdxFinalLibs ${GTK2_LIBRARIES} ${LIBC_LIBRARIES} ${PNG_LIBRARIES} + ${FREETYPE_LIBRARIES} ) if(EGL_API AND EGL_FOUND) diff --git a/plugins/GSdx/GSDevice.cpp b/plugins/GSdx/GSDevice.cpp index 7f182e6a32..c98bb7a154 100644 --- a/plugins/GSdx/GSDevice.cpp +++ b/plugins/GSdx/GSDevice.cpp @@ -120,6 +120,7 @@ void GSDevice::Present(const GSVector4i& r, int shader) ShaderConvert_COMPLEX_FILTER}; // FIXME Present(m_current, m_backbuffer, GSVector4(r), s_shader[shader]); + RenderOsd(m_backbuffer); } Flip(); diff --git a/plugins/GSdx/GSDevice.h b/plugins/GSdx/GSDevice.h index 8f8b9fe1cf..ae52011f09 100644 --- a/plugins/GSdx/GSDevice.h +++ b/plugins/GSdx/GSDevice.h @@ -25,6 +25,7 @@ #include "GSTexture.h" #include "GSVertex.h" #include "GSAlignedClass.h" +#include "GSOsdManager.h" enum ShaderConvert { ShaderConvert_COPY = 0, @@ -132,6 +133,8 @@ protected: virtual void DoExternalFX(GSTexture* sTex, GSTexture* dTex) {} public: + GSOsdManager m_osd; + GSDevice(); virtual ~GSDevice(); @@ -184,6 +187,7 @@ public: void FXAA(); void ShadeBoost(); void ExternalFX(); + virtual void RenderOsd(GSTexture* dt) {}; bool ResizeTexture(GSTexture** t, int w, int h); diff --git a/plugins/GSdx/GSDeviceOGL.cpp b/plugins/GSdx/GSDeviceOGL.cpp index 0c66cf27ac..38f8d9d552 100644 --- a/plugins/GSdx/GSDeviceOGL.cpp +++ b/plugins/GSdx/GSDeviceOGL.cpp @@ -24,6 +24,7 @@ #include "GSDeviceOGL.h" #include "GLState.h" #include "GSUtil.h" +#include "GSOsdManager.h" #include #include "res/glsl_source.h" @@ -61,6 +62,7 @@ GSDeviceOGL::GSDeviceOGL() , m_palette_ss(0) , m_vs_cb(NULL) , m_ps_cb(NULL) + , m_font(NULL) , m_shader(NULL) { memset(&m_merge_obj, 0, sizeof(m_merge_obj)); @@ -537,6 +539,12 @@ bool GSDeviceOGL::Create(GSWnd* wnd) fprintf(stdout, "Available VRAM/RAM:%lldMB for textures\n", GLState::available_vram >> 20u); + // **************************************************************** + // Texture Font (OSD) + // **************************************************************** + GSVector2i tex_font = m_osd.get_texture_font_size(); + m_font = new GSTextureOGL(GSTextureOGL::Texture, tex_font.x, tex_font.y, GL_R8, m_fbo_read, false); + // **************************************************************** // Finish window setup and backbuffer // **************************************************************** @@ -1393,6 +1401,36 @@ void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture EndScene(); } +void GSDeviceOGL::RenderOsd(GSTexture* dt) +{ + BeginScene(); + + m_shader->BindPipeline(m_convert.ps[ShaderConvert_OSD]); + + OMSetDepthStencilState(m_convert.dss); + OMSetBlendState((uint8)GSDeviceOGL::m_MERGE_BLEND); + OMSetRenderTargets(dt, NULL); + + if(m_osd.m_texture_dirty) { + m_osd.upload_texture_atlas(m_font); + } + + PSSetShaderResource(0, m_font); + PSSetSamplerState(m_convert.pt); + + IASetPrimitiveTopology(GL_TRIANGLES); + + // Note scaling could also be done in shader (require gl3/dx10) + size_t count = m_osd.Size(); + GSVertexPT1* dst = (GSVertexPT1*)m_va->MapVB(count); + count = m_osd.GeneratePrimitives(dst, count); + m_va->UnmapVB(); + + DrawPrimitive(); + + EndScene(); +} + void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) { GL_PUSH("DoMerge"); diff --git a/plugins/GSdx/GSDeviceOGL.h b/plugins/GSdx/GSDeviceOGL.h index 14ab756723..53960b8aa0 100644 --- a/plugins/GSdx/GSDeviceOGL.h +++ b/plugins/GSdx/GSDeviceOGL.h @@ -28,6 +28,7 @@ #include "GSUniformBufferOGL.h" #include "GSShaderOGL.h" #include "GLState.h" +#include "GSOsdManager.h" // A couple of flag to determine the blending behavior #define BLEND_A_MAX (0x100) // Impossible blending uses coeff bigger than 1 @@ -484,6 +485,8 @@ public: PSConstantBuffer m_ps_cb_cache; MiscConstantBuffer m_misc_cb_cache; + GSTextureOGL* m_font; + GSTexture* CreateSurface(int type, int w, int h, bool msaa, int format); GSTexture* FetchSurface(int type, int w, int h, bool msaa, int format); @@ -492,6 +495,7 @@ public: void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex) final; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final; + void RenderOsd(GSTexture* dt); void OMAttachRt(GSTextureOGL* rt = NULL); void OMAttachDs(GSTextureOGL* ds = NULL); diff --git a/plugins/GSdx/GSOsdManager.cpp b/plugins/GSdx/GSOsdManager.cpp new file mode 100644 index 0000000000..142f6c0c48 --- /dev/null +++ b/plugins/GSdx/GSOsdManager.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2014-2016 Gregory hainaut + * Copyright (C) 2016-2016 Jason Brown + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "stdafx.h" +#include "GSdx.h" +#include "GSOsdManager.h" + +void GSOsdManager::LoadFont() { + FT_Error error = FT_New_Face(m_library, theApp.GetConfigS("osd_fontname").c_str(), 0, &m_face); + if (error) { + m_face = NULL; + fprintf(stderr, "Failed to init the freetype face\n"); + if(error == FT_Err_Unknown_File_Format) + fprintf(stderr, "\tFreetype unknown file format\n"); + return; + } + + LoadSize(); +} + +void GSOsdManager::LoadSize() { + if (!m_face) return; + + m_size = theApp.GetConfigI("osd_fontsize"); + FT_Error error = FT_Set_Pixel_Sizes(m_face, 0, m_size);; + if (error) { + fprintf(stderr, "Failed to init the face size\n"); + return; + } + + /* This is not exact, I'm sure there's some convoluted way to determine these + * from FreeType but they don't make it easy. */ + m_atlas_w = m_size * 96; // random guess + m_atlas_h = m_size; // another random guess +} + +GSOsdManager::GSOsdManager() : m_atlas_h(0) + , m_atlas_w(0) + , m_max_width(0) + , m_texture_dirty(true) +{ + m_log_enabled = theApp.GetConfigB("osd_log_enabled"); + m_log_speed = theApp.GetConfigI("osd_log_speed"); + m_log_speed = m_log_speed < 2 ? 2 : m_log_speed > 10 ? 10 : m_log_speed; + m_monitor_enabled = theApp.GetConfigB("osd_monitor_enabled"); + m_indicator_enabled = theApp.GetConfigB("osd_indicator_enabled"); + m_osd_transparency = theApp.GetConfigI("osd_transparency"); + m_osd_transparency = m_osd_transparency < 0 ? 0 : m_osd_transparency > 200 ? 200 : m_osd_transparency; + + if (FT_Init_FreeType(&m_library)) { + fprintf(stderr, "Failed to init the freetype library\n"); + return; + } + + LoadFont(); + + /* The space character's width is used in GeneratePrimitives() */ + AddGlyph(' '); +} + +GSOsdManager::~GSOsdManager() { + FT_Done_FreeType(m_library); +} + +GSVector2i GSOsdManager::get_texture_font_size() { + return GSVector2i(m_atlas_w, m_atlas_h); +} + +void GSOsdManager::upload_texture_atlas(GSTexture* t) { + if (!m_face) return; + + if (m_char_info.size() > 96) // we only reserved space for this many glyphs + fprintf(stderr, "More than 96 glyphs needed for OSD"); + + // This can be sped up a bit by only uploading new glyphs + int x = 0; + for(auto &pair : m_char_info) { + if(FT_Load_Char(m_face, pair.first, FT_LOAD_RENDER)) { + fprintf(stderr, "failed to load char U%d\n", (int)pair.first); + continue; + } + + // Size of char + pair.second.ax = m_face->glyph->advance.x >> 6; + pair.second.ay = m_face->glyph->advance.y >> 6; + + pair.second.bw = m_face->glyph->bitmap.width; + pair.second.bh = m_face->glyph->bitmap.rows; + + pair.second.bl = m_face->glyph->bitmap_left; + pair.second.bt = m_face->glyph->bitmap_top; + + GSVector4i r(x, 0, x+pair.second.bw, pair.second.bh); + if (r.width()) + t->Update(r, m_face->glyph->bitmap.buffer, m_face->glyph->bitmap.pitch); + + if (r.width() > m_max_width) m_max_width = r.width(); + + pair.second.tx = (float)x / m_atlas_w; + pair.second.ty = (float)pair.second.bh / m_atlas_h; + pair.second.tw = (float)pair.second.bw / m_atlas_w; + + x += pair.second.bw; + } + + m_texture_dirty = false; +} + +#if __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 4 ) +/* This is dumb in that it doesn't check for malformed UTF8. This function + * is not expected to operate on user input, but only on compiled in strings */ +void dumb_utf8_to_utf32(const char *utf8, char32_t *utf32, unsigned size) { + while(*utf8 && --size) { + if((*utf8 & 0xF1) == 0xF0) { + *utf32++ = (utf8[0] & 0x07) << 18 | (utf8[1] & 0x3F) << 12 | (utf8[2] & 0x3F) << 6 | utf8[3] & 0x3F; + utf8 += 4; + } else if((*utf8 & 0xF0) == 0xE0) { + *utf32++ = (utf8[0] & 0x0F) << 12 | (utf8[1] & 0x3F) << 6 | utf8[2] & 0x3F; + utf8 += 3; + } else if((*utf8 & 0xE0) == 0xC0) { + *utf32++ = (utf8[0] & 0x1F) << 6 | utf8[1] & 0x3F; + utf8 += 2; + } else if((*utf8 & 0x80) == 0x00) { + *utf32++ = utf8[0] & 0x7F; + utf8 += 1; + } + } + + if(size) *utf32 = *utf8; // Copy NUL char +} +#endif + +void GSOsdManager::AddGlyph(char32_t codepoint) { + if (!m_face) return; + if(m_char_info.count(codepoint) == 0) { + m_texture_dirty = true; + m_char_info[codepoint]; // add it + if(FT_HAS_KERNING(m_face)) { + FT_UInt new_glyph = FT_Get_Char_Index(m_face, codepoint); + for(auto pair : m_char_info) { + FT_Vector delta; + + FT_UInt glyph_index = FT_Get_Char_Index(m_face, pair.first); + FT_Get_Kerning(m_face, glyph_index, new_glyph, FT_KERNING_DEFAULT, &delta); + m_kern_info[std::make_pair(pair.first, codepoint)] = delta.x >> 6; + } + } + } +} + +void GSOsdManager::Log(const char *utf8, uint32 color) { + if(!m_log_enabled) + return; + +#if __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 4 ) + char32_t buffer[256]; + dumb_utf8_to_utf32(utf8, buffer, countof(buffer)); + for(char32_t* c = buffer; *c; ++c) AddGlyph(*c); +#else +#if _MSC_VER == 1900 + std::wstring_convert, unsigned int> conv; +#else + std::wstring_convert, char32_t> conv; +#endif + std::u32string buffer = conv.from_bytes(utf8); + for(auto const &c : buffer) AddGlyph(c); +#endif + m_log.push_back(log_info{color, buffer, std::chrono::system_clock::time_point()}); + +} + +void GSOsdManager::Monitor(const char *key, const char *value, uint32 color) { + if(!m_monitor_enabled) + return; + + if(value && *value) { +#if __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 4 ) + char32_t buffer[256], vbuffer[256]; + dumb_utf8_to_utf32(key, buffer, countof(buffer)); + dumb_utf8_to_utf32(value, vbuffer, countof(vbuffer)); + for(char32_t* c = buffer; *c; ++c) AddGlyph(*c); + for(char32_t* c = vbuffer; *c; ++c) AddGlyph(*c); +#else +#if _MSC_VER == 1900 + std::wstring_convert, unsigned int> conv; +#else + std::wstring_convert, char32_t> conv; +#endif + std::u32string buffer = conv.from_bytes(key); + std::u32string vbuffer = conv.from_bytes(value); + for(auto const &c : buffer) AddGlyph(c); + for(auto const &c : vbuffer) AddGlyph(c); +#endif + m_monitor[buffer] = std::make_pair(vbuffer, color); + } else { +#if __GNUC__ < 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ < 4 ) + char32_t buffer[256]; + dumb_utf8_to_utf32(key, buffer, countof(buffer)); +#else +#if _MSC_VER == 1900 + std::wstring_convert, unsigned int> conv; +#else + std::wstring_convert, char32_t> conv; +#endif + std::u32string buffer = conv.from_bytes(key); +#endif + m_monitor.erase(buffer); + } +} + +void GSOsdManager::Indicate(const std::string key, bool on) { + if(!m_indicator_enabled) + return; + + m_indicator[key].on = on; +} + +void GSOsdManager::RenderGlyph(GSVertexPT1* dst, const glyph_info g, float x, float y, uint32 color) { + float x2 = x + g.bl * (2.0f/m_real_size.x); + float y2 = -y - g.bt * (2.0f/m_real_size.y); + float w = g.bw * (2.0f/m_real_size.x); + float h = g.bh * (2.0f/m_real_size.y); + + dst->p = GSVector4(x2 , -y2 , 0.0f, 0.0f); + dst->t = GSVector2(g.tx , 0.0f); + dst->c = color; + ++dst; + dst->p = GSVector4(x2 + w, -y2 , 0.0f, 0.0f); + dst->t = GSVector2(g.tx + g.tw, 0.0f); + dst->c = color; + ++dst; + dst->p = GSVector4(x2 , -y2 - h, 0.0f, 0.0f); + dst->t = GSVector2(g.tx , g.ty); + dst->c = color; + ++dst; + dst->p = GSVector4(x2 + w, -y2 , 0.0f, 0.0f); + dst->t = GSVector2(g.tx + g.tw, 0.0f); + dst->c = color; + ++dst; + dst->p = GSVector4(x2 , -y2 - h, 0.0f, 0.0f); + dst->t = GSVector2(g.tx , g.ty); + dst->c = color; + ++dst; + dst->p = GSVector4(x2 + w, -y2 - h, 0.0f, 0.0f); + dst->t = GSVector2(g.tx + g.tw, g.ty); + dst->c = color; + ++dst; +} + +void GSOsdManager::RenderString(GSVertexPT1* dst, const std::u32string msg, float x, float y, uint32 color) { + char32_t p = 0; + for(const auto & c : msg) { + if(p) { + x += m_kern_info[std::make_pair(p, c)] * (2.0f/m_real_size.x); + } + + RenderGlyph(dst, m_char_info[c], x, y, color); + + /* Advance the cursor to the start of the next character */ + x += m_char_info[c].ax * (2.0f/m_real_size.x); + y += m_char_info[c].ay * (2.0f/m_real_size.y); + + dst += 6; + + p = c; + } +} + +size_t GSOsdManager::Size() { + size_t sum = 0; + + if(m_log_enabled) { + float offset = 0; + + for(auto it = m_log.begin(); it != m_log.end(); ++it) { + float y = 1 - ((m_size+2)*(it-m_log.begin()+1)) * (2.0f/m_real_size.y); + if(y + offset < -1) break; + + std::chrono::duration elapsed; + if(it->OnScreen.time_since_epoch().count() == 0) { + elapsed = std::chrono::seconds(0); + } else { + elapsed = std::chrono::system_clock::now() - it->OnScreen; + if(elapsed > std::chrono::seconds(m_log_speed)) { + continue; + } + } + + float ratio = (elapsed - std::chrono::seconds(m_log_speed/2)).count() / std::chrono::seconds(m_log_speed/2).count(); + ratio = ratio > 1.0f ? 1.0f : ratio < 0.0f ? 0.0f : ratio; + + y += offset += ((m_size+2) * (2.0f/m_real_size.y)) * ratio; + sum += it->msg.size(); + } + } + + if(m_monitor_enabled) { + for(const auto &pair : m_monitor) { + sum += pair.first.size(); + sum += pair.second.first.size(); + } + } + + if(m_indicator_enabled) { + sum += m_indicator.size(); + } + + return sum * 6; +} + +float GSOsdManager::StringSize(const std::u32string msg) { + char32_t p = 0; + float x = 0.0; + + for(auto c : msg) { + if(p) { + x += m_kern_info[std::make_pair(p, c)] * (2.0f/m_real_size.x); + } + + /* Advance the cursor to the start of the next character */ + x += m_char_info[c].ax * (2.0f/m_real_size.x); + + p = c; + } + + return x; +} + +size_t GSOsdManager::GeneratePrimitives(GSVertexPT1* dst, size_t count) { + size_t drawn = 0; + float transparency = 1.0f - m_osd_transparency / 200.0f; + + if(m_log_enabled) { + float offset = 0; + + for(auto it = m_log.begin(); it != m_log.end();) { + float x = -1 + 8 * (2.0f/m_real_size.x); + float y = 1 - ((m_size+2)*(it-m_log.begin()+1)) * (2.0f/m_real_size.y); + + if(y + offset < -1) break; + + if(it->OnScreen.time_since_epoch().count() == 0) + it->OnScreen = std::chrono::system_clock::now(); + + std::chrono::duration elapsed = std::chrono::system_clock::now() - it->OnScreen; + if(elapsed > std::chrono::seconds(m_log_speed)) { + it = m_log.erase(it); + continue; + } + + if(it->msg.size() * 6 > count - drawn) break; + + float ratio = (elapsed - std::chrono::seconds(m_log_speed/2)).count() / std::chrono::seconds(m_log_speed/2).count(); + ratio = ratio > 1.0f ? 1.0f : ratio < 0.0f ? 0.0f : ratio; + + y += offset += ((m_size+2) * (2.0f/m_real_size.y)) * ratio; + uint32 color = it->color; + ((uint8 *)&color)[3] = (uint8)(((uint8 *)&color)[3] * (1.0f - ratio) * transparency); + RenderString(dst, it->msg, x, y, color); + dst += it->msg.size() * 6; + drawn += it->msg.size() * 6; + ++it; + } + } + + if(m_monitor_enabled) { + // pair.first is the key and second is the value and color + + // Since the monitor is right justified, but we render from left to right + // we need to find the longest string + float first_max = 0.0, second_max = 0.0; + for(const auto &pair : m_monitor) { + float first_len = StringSize(pair.first); + float second_len = StringSize(pair.second.first); + + first_max = first_max < first_len ? first_len : first_max; + second_max = second_max < second_len ? second_len : second_max; + } + + size_t line = 1; + for(const auto &pair : m_monitor) { + if((pair.first.size() + pair.second.first.size()) * 6 > count - drawn) break; + + // Calculate where to start rendering from by taking the right most position 1.0 + // and subtracting (going left) 8 scaled pixels for a margin, then subtracting + // the size of the longest key and subtracting a scaled space and finally + // subtracting the longest value + float x = 1.0f - 8 * (2.0f/m_real_size.x) - first_max - m_char_info[' '].ax * (2.0f/m_real_size.x) - second_max; + float y = -1.0f + ((m_size+2)*(2.0f/m_real_size.y)) * line++; + + uint32 color = pair.second.second; + ((uint8 *)&color)[3] = (uint8)(((uint8 *)&color)[3] * transparency); + + // Render the key + RenderString(dst, pair.first, x, y, color); + dst += pair.first.size() * 6; + drawn += pair.first.size() * 6; + + // Calculate the position for the value + x = 1.0f - 8 * (2.0f/m_real_size.x) - second_max; + + // Render the value + RenderString(dst, pair.second.first, x, y, color); + dst += pair.second.first.size() * 6; + drawn += pair.second.first.size() * 6; + } + } + + if(m_indicator_enabled) { + for(const auto pair : m_indicator) { + float alpha = 1.0; + if(!pair.second.on) { + alpha = 0.25; + } + uint32 color = pair.second.color; + ((uint8 *)&color)[3] = (uint8)(((uint8 *)&color)[3] * alpha * transparency); + RenderGlyph(dst, pair.second.glyph, pair.second.x, pair.second.y, color); + dst += 6; + drawn += 6; + } + } + + return drawn; +} + diff --git a/plugins/GSdx/GSOsdManager.h b/plugins/GSdx/GSOsdManager.h new file mode 100644 index 0000000000..1f6f434886 --- /dev/null +++ b/plugins/GSdx/GSOsdManager.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014-2016 Gregory hainaut + * Copyright (C) 2016-2016 Jason Brown + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#pragma once + +#include "stdafx.h" +#include "GSVector.h" +#include "GSVertex.h" +#include "GSTexture.h" +#include +#include FT_FREETYPE_H + +class GSOsdManager { + struct glyph_info { + int32 ax; // advance.x + int32 ay; // advance.y + + uint32 bw; // bitmap.width; + uint32 bh; // bitmap.rows; + + int32 bl; // bitmap_left; + int32 bt; // bitmap_top; + + float tx; // x offset of glyph + float ty; // y offset of glyph + float tw; // nomalized glyph width + }; + + std::map m_char_info; + std::map, FT_Pos> m_kern_info; + + FT_Library m_library; + FT_Face m_face; + FT_UInt m_size; + + uint32 m_atlas_h; + uint32 m_atlas_w; + int32 m_max_width; + + void compute_glyph_size(); + + struct log_info { + uint32 color; + std::u32string msg; + std::chrono::system_clock::time_point OnScreen; + }; + std::vector m_log; + + std::map> m_monitor; + + struct indicator_info { + glyph_info glyph; + uint32 color; + bool on; + float x, y; + }; + std::map m_indicator; + + void AddGlyph(char32_t codepoint); + void RenderGlyph(GSVertexPT1* dst, const glyph_info g, float x, float y, uint32 color); + void RenderString(GSVertexPT1* dst, const std::u32string msg, float x, float y, uint32 color); + float StringSize(const std::u32string msg); + + bool m_log_enabled; + int m_log_speed; + bool m_monitor_enabled; + bool m_indicator_enabled; + int m_osd_transparency; + + public: + + GSOsdManager(); + ~GSOsdManager(); + + void LoadFont(); + void LoadSize(); + + GSVector2i get_texture_font_size(); + + bool m_texture_dirty; + void upload_texture_atlas(GSTexture* t); + + void Log(const char *utf8, uint32 color); + void Monitor(const char *key, const char *value, uint32 color); + void Indicate(const std::string key, bool on); + + GSVector2i m_real_size; + size_t Size(); + size_t GeneratePrimitives(GSVertexPT1* dst, size_t count); +}; diff --git a/plugins/GSdx/GSRenderer.cpp b/plugins/GSdx/GSRenderer.cpp index 4bf5416249..bfe54dabc1 100644 --- a/plugins/GSdx/GSRenderer.cpp +++ b/plugins/GSdx/GSRenderer.cpp @@ -451,6 +451,23 @@ void GSRenderer::VSync(int field) // present +#if 0 + // This will scale the OSD to the PS2's output resolution. + // Will be affected by 2x, 4x, etc scaling. + m_dev->m_osd.m_real_size = m_real_size +#elif 0 + // This will scale the OSD to the window's size. + // Will maintiain the font size no matter what size the window is. + GSVector4i window_size = m_wnd->GetClientRect(); + m_dev->m_osd.m_real_size.x = window_size.v[2]; + m_dev->m_osd.m_real_size.y = window_size.v[3]; +#else + // This will scale the OSD to the native resolution. + // Will size font relative to the window's size. + // TODO this should probably be done with native calls + m_dev->m_osd.m_real_size.x = 1024; + m_dev->m_osd.m_real_size.y = 768; +#endif m_dev->Present(m_wnd->GetClientRect().fit(m_aspectratio), m_shader); // snapshot diff --git a/plugins/GSdx/GSdx.cpp b/plugins/GSdx/GSdx.cpp index dd2ce6df0f..627df96eb7 100644 --- a/plugins/GSdx/GSdx.cpp +++ b/plugins/GSdx/GSdx.cpp @@ -301,17 +301,27 @@ void GSdxApp::Init() m_default_configuration["extrathreads_height"] = "4"; m_default_configuration["filter"] = "2"; m_default_configuration["force_texture_clear"] = "0"; +#ifdef _WIN32 + m_default_configuration["fontname"] = "C:\\Windows\\Fonts\\tahoma.ttf"; +#else + m_default_configuration["fontname"] = "/usr/share/fonts/truetype/freefont/FreeSerif.ttf"; +#endif + m_default_configuration["fontsize"] = "48"; m_default_configuration["fxaa"] = "0"; + m_default_configuration["indicator_enabled"] = "0"; m_default_configuration["interlace"] = "7"; m_default_configuration["large_framebuffer"] = "1"; m_default_configuration["linear_present"] = "1"; + m_default_configuration["log_enabled"] = "1"; m_default_configuration["MaxAnisotropy"] = "0"; m_default_configuration["mipmap"] = "1"; m_default_configuration["mipmap_hw"] = "0"; m_default_configuration["ModeHeight"] = "480"; m_default_configuration["ModeWidth"] = "640"; + m_default_configuration["monitor_enabled"] = "0"; m_default_configuration["NTSC_Saturation"] = "1"; m_default_configuration["ocldev"] = ""; + m_default_configuration["osd_transparency"] = "50"; m_default_configuration["override_geometry_shader"] = "-1"; m_default_configuration["override_GL_ARB_clear_texture"] = "-1"; m_default_configuration["override_GL_ARB_draw_buffers_blend"] = "-1"; diff --git a/plugins/GSdx/GSdx.vcxproj b/plugins/GSdx/GSdx.vcxproj index e6eb29af2c..da197e1bbf 100644 --- a/plugins/GSdx/GSdx.vcxproj +++ b/plugins/GSdx/GSdx.vcxproj @@ -158,6 +158,7 @@ + @@ -247,6 +248,7 @@ + diff --git a/plugins/GSdx/GSdx.vcxproj.filters b/plugins/GSdx/GSdx.vcxproj.filters index 9cf3c8bc00..63cfe40b58 100644 --- a/plugins/GSdx/GSdx.vcxproj.filters +++ b/plugins/GSdx/GSdx.vcxproj.filters @@ -90,6 +90,9 @@ Source Files + + Source Files + Source Files @@ -371,6 +374,9 @@ Header Files + + Header Files + Header Files diff --git a/plugins/GSdx/stdafx.h b/plugins/GSdx/stdafx.h index 6a7c9bd780..be7096a88a 100644 --- a/plugins/GSdx/stdafx.h +++ b/plugins/GSdx/stdafx.h @@ -95,6 +95,11 @@ typedef int64 sint64; #include #include +#if __GNUC__ > 5 || ( __GNUC__ == 5 && __GNUC_MINOR__ >= 4 ) +#include +#include +#endif + #include #include #include