mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-14 02:38:37 +12:00
291 lines
6.7 KiB
C++
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
|