rpcs3/rpcs3/Emu/RSX/Overlays/overlay_font.cpp
2019-12-03 17:23:00 +03:00

291 lines
6.7 KiB
C++

#include "stdafx.h"
#include "overlay_controls.h"
namespace rsx
{
namespace overlays
{
font::font(const char* ttf_name, f32 size)
{
// Init glyph
std::vector<u8> bytes;
std::vector<std::string> font_dirs;
std::vector<std::string> fallback_fonts;
#ifdef _WIN32
font_dirs.push_back("C:/Windows/Fonts/");
fallback_fonts.push_back("C:/Windows/Fonts/Arial.ttf");
#else
char* home = getenv("HOME");
if (home == nullptr)
home = getpwuid(getuid())->pw_dir;
font_dirs.emplace_back(home);
if (home[font_dirs[0].length() - 1] == '/')
font_dirs[0] += ".fonts/";
else
font_dirs[0] += "/.fonts/";
fallback_fonts.emplace_back("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); // ubuntu
fallback_fonts.emplace_back("/usr/share/fonts/TTF/DejaVuSans.ttf"); // arch
#endif
// Search dev_flash for the font too
font_dirs.push_back(g_cfg.vfs.get_dev_flash() + "data/font/");
font_dirs.push_back(g_cfg.vfs.get_dev_flash() + "data/font/SONY-CC/");
// Attempt to load a font from dev_flash as a last resort
fallback_fonts.push_back(g_cfg.vfs.get_dev_flash() + "data/font/SCE-PS3-VR-R-LATIN.TTF");
// Attemt to load requested font
std::string file_path;
bool font_found = false;
for (auto& font_dir : font_dirs)
{
std::string requested_file = font_dir + ttf_name;
// Append ".ttf" if not present
std::string font_lower(requested_file);
std::transform(requested_file.begin(), requested_file.end(), font_lower.begin(), ::tolower);
if (font_lower.substr(font_lower.size() - 4) != ".ttf")
requested_file += ".ttf";
file_path = requested_file;
if (fs::is_file(requested_file))
{
font_found = true;
break;
}
}
// Attemt to load a fallback if request font wasn't found
if (!font_found)
{
for (auto& fallback_font : fallback_fonts)
{
if (fs::is_file(fallback_font))
{
file_path = fallback_font;
font_found = true;
LOG_NOTICE(RSX, "Found font file '%s' as a replacement for '%s'", fallback_font.c_str(), ttf_name);
break;
}
}
}
// Read font
if (font_found)
{
fs::file f(file_path);
f.read(bytes, f.size());
}
else
{
LOG_ERROR(RSX, "Failed to initialize font '%s.ttf'", ttf_name);
return;
}
glyph_data.resize(width * height);
pack_info.resize(256);
stbtt_pack_context context;
if (!stbtt_PackBegin(&context, glyph_data.data(), width, height, 0, 1, nullptr))
{
LOG_ERROR(RSX, "Font packing failed");
return;
}
stbtt_PackSetOversampling(&context, oversample, oversample);
// Convert pt to px
size_px = ceilf(size * 96.f / 72.f);
size_pt = size;
if (!stbtt_PackFontRange(&context, bytes.data(), 0, size_px, 0, 256, pack_info.data()))
{
LOG_ERROR(RSX, "Font packing failed");
stbtt_PackEnd(&context);
return;
}
stbtt_PackEnd(&context);
font_name = ttf_name;
initialized = true;
f32 unused;
get_char('m', em_size, unused);
}
stbtt_aligned_quad font::get_char(char c, f32& x_advance, f32& y_advance)
{
if (!initialized)
return {};
stbtt_aligned_quad quad;
stbtt_GetPackedQuad(pack_info.data(), width, height, u8(c), &x_advance, &y_advance, &quad, false);
return quad;
}
void font::render_text_ex(std::vector<vertex>& result, f32& x_advance, f32& y_advance, const char* text, u32 char_limit, u16 max_width, bool wrap)
{
x_advance = 0.f;
y_advance = 0.f;
result.clear();
if (!initialized)
{
return;
}
u32 i = 0u;
bool skip_whitespace = false;
while (true)
{
if (char c = text[i++]; c && (i <= char_limit))
{
if (u8(c) >= char_count)
{
// Unsupported glyph, render null for now
c = ' ';
}
switch (c)
{
case '\n':
{
y_advance += size_px + 2.f;
x_advance = 0.f;
continue;
}
case '\r':
{
x_advance = 0.f;
continue;
}
default:
{
stbtt_aligned_quad quad;
if (skip_whitespace && text[i - 1] == ' ')
{
quad = {};
}
else
{
quad = get_char(c, x_advance, y_advance);
skip_whitespace = false;
}
if (quad.x1 > max_width)
{
bool wrapped = false;
bool non_whitespace_break = false;
if (wrap)
{
// scan previous chars
for (int j = i - 1, nb_chars = 0; j > 0; j--, nb_chars++)
{
if (text[j] == '\n')
break;
if (text[j] == ' ')
{
non_whitespace_break = true;
continue;
}
if (non_whitespace_break)
{
if (nb_chars > 1)
{
nb_chars--;
auto first_affected = result.size() - (nb_chars * 4);
f32 base_x = result[first_affected].values[0];
for (size_t n = first_affected; n < result.size(); ++n)
{
auto char_index = n / 4;
if (text[char_index] == ' ')
{
// Skip character
result[n++].vec2(0.f, 0.f);
result[n++].vec2(0.f, 0.f);
result[n++].vec2(0.f, 0.f);
result[n].vec2(0.f, 0.f);
continue;
}
result[n].values[0] -= base_x;
result[n].values[1] += size_px + 2.f;
}
x_advance = result.back().values[0];
}
else
{
x_advance = 0.f;
}
wrapped = true;
y_advance += size_px + 2.f;
if (text[i - 1] == ' ')
{
quad = {};
skip_whitespace = true;
}
else
{
quad = get_char(c, x_advance, y_advance);
}
break;
}
}
}
if (!wrapped)
{
// TODO: Ellipsize
break;
}
}
result.emplace_back(quad.x0, quad.y0, quad.s0, quad.t0);
result.emplace_back(quad.x1, quad.y0, quad.s1, quad.t0);
result.emplace_back(quad.x0, quad.y1, quad.s0, quad.t1);
result.emplace_back(quad.x1, quad.y1, quad.s1, quad.t1);
break;
}
} // switch
}
else
{
break;
}
}
}
std::vector<vertex> font::render_text(const char* text, u16 max_width, bool wrap)
{
std::vector<vertex> result;
f32 unused_x, unused_y;
render_text_ex(result, unused_x, unused_y, text, UINT32_MAX, max_width, wrap);
return result;
}
std::pair<f32, f32> font::get_char_offset(const char* text, u16 max_length, u16 max_width, bool wrap)
{
std::vector<vertex> unused;
f32 loc_x, loc_y;
render_text_ex(unused, loc_x, loc_y, text, max_length, max_width, wrap);
return {loc_x, loc_y};
}
} // namespace overlays
} // namespace rsx