Constness improvmenet of Emboss::FontFile / FontManager.

FontFile is returned by shared_ptr<const FontFile>, which makes it clear
that the FontFile must not be modified.
Because FontFile caches glyphs, a global mutex for all FontFile instances
was added guarding the glyph cache.
FIXME change true type font index in collection atomically, by duplicating the font!
         font_file->index = i;
This commit is contained in:
Vojtech Bubnik 2022-03-02 17:36:19 +01:00
parent 1648ae853d
commit df9220e5a8
7 changed files with 94 additions and 49 deletions

View File

@ -24,9 +24,8 @@ public:
static std::optional<stbtt_fontinfo> load_font_info(const Emboss::FontFile &font);
static std::optional<stbtt_fontinfo> load_font_info(const unsigned char *data, unsigned int index = 0);
static std::optional<Emboss::Glyph> get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness);
static std::optional<Emboss::Glyph> get_glyph(int unicode, const Emboss::FontFile &font, const FontProp &font_prop,
Emboss::Glyphs &cache, std::optional<stbtt_fontinfo> &font_info_opt);
static std::optional<Emboss::Glyph> get_glyph(const stbtt_fontinfo &font_info, int unicode_letter, float flatness);
static std::optional<Emboss::Glyph> get_glyph(int unicode, const Emboss::FontFile &font, const FontProp &font_prop, std::optional<stbtt_fontinfo> &font_info_opt);
static FontItem create_font_item(std::wstring name, std::wstring path);
@ -63,7 +62,7 @@ std::optional<stbtt_fontinfo> Private::load_font_info(
return font_info;
}
std::optional<Emboss::Glyph> Private::get_glyph(stbtt_fontinfo &font_info, int unicode_letter, float flatness)
std::optional<Emboss::Glyph> Private::get_glyph(const stbtt_fontinfo &font_info, int unicode_letter, float flatness)
{
int glyph_index = stbtt_FindGlyphIndex(&font_info, unicode_letter);
if (glyph_index == 0) {
@ -128,13 +127,9 @@ std::optional<Emboss::Glyph> Private::get_glyph(
int unicode,
const Emboss::FontFile & font,
const FontProp & font_prop,
Emboss::Glyphs & cache,
std::optional<stbtt_fontinfo> &font_info_opt)
{
const double RESOLUTION = 0.0125; // TODO: read from printer configuration
auto glyph_item = cache.find(unicode);
if (glyph_item != cache.end())
return glyph_item->second;
if (!font_info_opt.has_value()) {
font_info_opt = Private::load_font_info(font);
@ -182,8 +177,6 @@ std::optional<Emboss::Glyph> Private::get_glyph(
// unify multipoints with similar position. Could appear after union
dilate_to_unique_points(glyph_opt->shape);
cache[unicode] = *glyph_opt;
return glyph_opt;
}
@ -593,7 +586,7 @@ std::optional<Emboss::Glyph> Emboss::letter2glyph(const FontFile &font,
return Private::get_glyph(*font_info_opt, letter, flatness);
}
ExPolygons Emboss::text2shapes(FontFile & font,
ExPolygons Emboss::text2shapes(const FontFile &font,
const char * text,
const FontProp &font_prop)
{
@ -617,14 +610,26 @@ ExPolygons Emboss::text2shapes(FontFile & font,
if (wc == '\t') {
// '\t' = 4*space => same as imgui
const int count_spaces = 4;
std::optional<Glyph> space_opt = Private::get_glyph(int(' '), font, font_prop, font.cache, font_info_opt);
auto space = int(' ');
std::optional<Glyph> space_opt = font.get_glyph(space);
if (! space_opt) {
space_opt = Private::get_glyph(space, font, font_prop, font_info_opt);
if (space_opt)
font.cache_glyph(space, *space_opt);
}
if (!space_opt.has_value()) continue;
cursor.x() += count_spaces * space_opt->advance_width;
continue;
}
int unicode = static_cast<int>(wc);
std::optional<Glyph> glyph_opt = Private::get_glyph(unicode, font, font_prop, font.cache, font_info_opt);
std::optional<Glyph> glyph_opt = font.get_glyph(unicode);
if (! glyph_opt) {
glyph_opt = Private::get_glyph(unicode, font, font_prop, font_info_opt);
if (glyph_opt)
font.cache_glyph(unicode, *glyph_opt);
}
if (!glyph_opt.has_value()) continue;
// move glyph to cursor position
@ -652,7 +657,7 @@ void Emboss::apply_transformation(const FontProp &font_prop,
}
}
bool Emboss::is_italic(FontFile &font) {
bool Emboss::is_italic(const FontFile &font) {
std::optional<stbtt_fontinfo> font_info_opt =
Private::load_font_info(font);
@ -814,3 +819,32 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
transform.rotate(up_rot);
return transform;
}
static std::mutex FontFile_cache_mutex;
std::optional<Emboss::Glyph> Emboss::FontFile::get_glyph(int unicode) const
{
std::lock_guard<std::mutex> guard(FontFile_cache_mutex);
auto glyph_item = m_cache.find(unicode);
if (glyph_item != m_cache.end())
return glyph_item->second;
return {};
}
void Emboss::FontFile::cache_glyph(int unicode, Emboss::Glyph glyph) const
{
std::lock_guard<std::mutex> guard(FontFile_cache_mutex);
m_cache[unicode] = std::move(glyph);
}
void Emboss::FontFile::clear_glyph_cache() const
{
std::lock_guard<std::mutex> guard(FontFile_cache_mutex);
m_cache.clear();
}
size_t Emboss::FontFile::glyph_cache_size() const
{
std::lock_guard<std::mutex> guard(FontFile_cache_mutex);
return m_cache.size();
}

View File

@ -5,6 +5,7 @@
#include <set>
#include <optional>
#include <memory>
#include <mutex>
#include <admesh/stl.h> // indexed_triangle_set
#include "Polygon.hpp"
#include "ExPolygon.hpp"
@ -75,8 +76,6 @@ public:
// for convert font units to pixel
int unit_per_em;
Emboss::Glyphs cache; // cache of glyphs
FontFile(std::vector<unsigned char> &&buffer,
unsigned int count,
int ascent,
@ -97,6 +96,17 @@ public:
buffer.size() == other.buffer.size() &&
buffer == other.buffer;
}
std::optional<Emboss::Glyph> get_glyph(int unicode) const;
// These two methods modifying cache are marked as const, because their
// access to the cache is guarded by a global mutex specific to all FontFile instances.
void cache_glyph(int unicode, Emboss::Glyph glyph) const;
void clear_glyph_cache() const;
size_t glyph_cache_size() const;
private:
// Cache of glyphs, guarded by a global mutex specific to all FontFile instances.
mutable Emboss::Glyphs m_cache;
};
/// <summary>
@ -130,7 +140,7 @@ public:
/// <param name="text">Characters to convert</param>
/// <param name="font_prop">User defined property of the font</param>
/// <returns>Inner polygon cw(outer ccw)</returns>
static ExPolygons text2shapes(FontFile & font,
static ExPolygons text2shapes(const FontFile &font,
const char * text,
const FontProp &font_prop);
@ -149,7 +159,7 @@ public:
/// </summary>
/// <param name="font">Selector of font</param>
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
static bool is_italic(FontFile &font);
static bool is_italic(const FontFile &font);
/// <summary>
/// Project 2d point into space

View File

@ -662,7 +662,7 @@ bool GLGizmoEmboss::process()
if (m_volume == nullptr) return false;
// exist loaded font?
std::shared_ptr<Emboss::FontFile>& font_file = m_font_manager.get_font_file();
std::shared_ptr<const Emboss::FontFile>& font_file = m_font_manager.get_font_file();
if (font_file == nullptr) return false;
auto data = std::make_unique<EmbossDataUpdate>(font_file,
create_configuration(),
@ -1224,7 +1224,7 @@ void GLGizmoEmboss::draw_style_list() {
bool GLGizmoEmboss::italic_button()
{
std::optional<wxFont> &wx_font = m_font_manager.get_wx_font();
std::shared_ptr<Emboss::FontFile> &font_file = m_font_manager.get_font_file();
std::shared_ptr<const Emboss::FontFile> &font_file = m_font_manager.get_font_file();
if (!wx_font.has_value() || font_file == nullptr) {
draw_icon(IconType::italic, IconState::disabled);
return false;
@ -1266,7 +1266,7 @@ bool GLGizmoEmboss::italic_button()
bool GLGizmoEmboss::bold_button() {
std::optional<wxFont> &wx_font = m_font_manager.get_wx_font();
std::shared_ptr<Emboss::FontFile> &font_file = m_font_manager.get_font_file();
std::shared_ptr<const Emboss::FontFile> &font_file = m_font_manager.get_font_file();
if (!wx_font.has_value() || font_file==nullptr) {
draw_icon(IconType::bold, IconState::disabled);
return false;
@ -1576,7 +1576,7 @@ void GLGizmoEmboss::do_rotate(float relative_z_angle)
void GLGizmoEmboss::draw_advanced()
{
std::shared_ptr<Emboss::FontFile>& font_file = m_font_manager.get_font_file();
std::shared_ptr<const Emboss::FontFile>& font_file = m_font_manager.get_font_file();
if (font_file == nullptr) {
ImGui::Text("%s", _u8L("Advanced font options could be change only for corect font.\nStart with select correct font.").c_str());
return;
@ -1589,7 +1589,7 @@ void GLGizmoEmboss::draw_advanced()
", descent=" + std::to_string(font_file->descent) +
", lineGap=" + std::to_string(font_file->linegap) +
", unitPerEm=" + std::to_string(font_file->unit_per_em) +
", cache(" + std::to_string(font_file->cache.size()) + " glyphs)";
", cache(" + std::to_string(font_file->glyph_cache_size()) + " glyphs)";
if (font_file->count > 1) ff_property +=
", collect=" + std::to_string(font_file->index + 1) + "/" + std::to_string(font_file->count);
m_imgui->text_colored(ImGuiWrapper::COL_GREY_DARK, ff_property);
@ -1698,7 +1698,8 @@ void GLGizmoEmboss::draw_advanced()
ImGui::PushID(1 << (10 + i));
bool is_selected = i == font_file->index;
if (ImGui::Selectable(std::to_string(i).c_str(), is_selected)) {
font_file->index = i;
//FIXME change true type font index in collection atomically, by duplicating the font!
// font_file->index = i;
exist_change = true;
}
ImGui::PopID();

View File

@ -240,10 +240,10 @@ TriangleMesh EmbossCreateJob::create_default_mesh()
return triangle_mesh;
}
TriangleMesh EmbossCreateJob::create_mesh(const char * text,
Emboss::FontFile &font,
const FontProp & font_prop,
Ctl & ctl)
TriangleMesh EmbossCreateJob::create_mesh(const char * text,
const Emboss::FontFile &font,
const FontProp & font_prop,
Ctl & ctl)
{
ExPolygons shapes = Emboss::text2shapes(font, text, font_prop);
if (shapes.empty()) return {};

View File

@ -46,10 +46,10 @@ public:
/// <param name="font_prop">Property of font</param>
/// <param name="ctl">Control for job, check of cancelation</param>
/// <returns>Triangle mesh model</returns>
static TriangleMesh create_mesh(const char * text,
Emboss::FontFile &font,
const FontProp & font_prop,
Ctl & ctl);
static TriangleMesh create_mesh(const char * text,
const Emboss::FontFile &font,
const FontProp & font_prop,
Ctl & ctl);
private:
static TriangleMesh create_default_mesh();
@ -62,14 +62,14 @@ private:
struct EmbossDataBase
{
// Pointer on Data of font (glyph shapes)
std::shared_ptr<Emboss::FontFile> font_file;
std::shared_ptr<const Emboss::FontFile> font_file;
// font item is not used for create object
TextConfiguration text_configuration;
// new volume name created from text
std::string volume_name;
EmbossDataBase(std::shared_ptr<Emboss::FontFile> font_file,
TextConfiguration text_configuration,
std::string volume_name)
EmbossDataBase(std::shared_ptr<const Emboss::FontFile> font_file,
TextConfiguration text_configuration,
std::string volume_name)
: font_file(std::move(font_file))
, text_configuration(text_configuration)
, volume_name(volume_name)
@ -88,10 +88,10 @@ struct EmbossDataUpdate : public EmbossDataBase
// unique identifier of volume to change
// Change of volume change id, last change could disapear
// ObjectID volume_id;
EmbossDataUpdate(std::shared_ptr<Emboss::FontFile> font_file,
TextConfiguration text_configuration,
std::string volume_name,
ModelVolume * volume)
EmbossDataUpdate(std::shared_ptr<const Emboss::FontFile> font_file,
TextConfiguration text_configuration,
std::string volume_name,
ModelVolume * volume)
: EmbossDataBase(std::move(font_file), text_configuration, volume_name)
, volume(volume)
{}
@ -125,7 +125,7 @@ struct EmbossDataCreate: public EmbossDataBase
// It is inside of GLGizmoEmboss object,
// so I hope it will survive
EmbossDataCreate(std::shared_ptr<Emboss::FontFile> font_file,
EmbossDataCreate(std::shared_ptr<const Emboss::FontFile> font_file,
const TextConfiguration & text_configuration,
const std::string & volume_name,
ModelVolumeType volume_type,

View File

@ -146,7 +146,7 @@ void FontManager::add_fonts(FontList font_list)
add_font(fi);
}
std::shared_ptr<Emboss::FontFile> &FontManager::get_font_file()
std::shared_ptr<const Emboss::FontFile> &FontManager::get_font_file()
{
// TODO: fix not selected font
//if (!is_activ_font()) return nullptr;
@ -367,7 +367,7 @@ void FontManager::create_texture(size_t index, const std::string &text, GLuint&
if (index >= m_font_list.size()) return;
Item &item = m_font_list[index];
const FontProp &font_prop = item.font_item.prop;
std::shared_ptr<Emboss::FontFile> &font_file = item.font_file;
std::shared_ptr<const Emboss::FontFile> &font_file = item.font_file;
if (font_file == nullptr && !set_up_font_file(index)) return;
ExPolygons shapes = Emboss::text2shapes(*font_file, text.c_str(), font_prop);
@ -415,7 +415,7 @@ void FontManager::init_style_images(int max_width) {
for (Item &item : m_font_list) {
FontItem & font_item = item.font_item;
const FontProp & font_prop = font_item.prop;
std::shared_ptr<Emboss::FontFile> &font_file = item.font_file;
std::shared_ptr<const Emboss::FontFile> &font_file = item.font_file;
size_t index = &item - &m_font_list.front();
if (font_file == nullptr && !set_up_font_file(index)) continue;
if (font_file == nullptr) continue;
@ -520,10 +520,10 @@ void FontManager::init_style_images(int max_width) {
void FontManager::free_style_images() {
if (!is_activ_font()) return;
std::shared_ptr<Emboss::FontFile> &font_file =
std::shared_ptr<const Emboss::FontFile> &font_file =
m_font_list[m_font_selected].font_file;
if(font_file != nullptr)
font_file->cache.clear();
if (font_file != nullptr)
font_file->clear_glyph_cache();
if (!m_exist_style_images) return;
GLuint tex_id = (GLuint) (intptr_t) m_font_list.front().image->texture_id;

View File

@ -72,7 +72,7 @@ public:
void add_fonts(FontList font_list);
// getter on active font file for access to glyphs
std::shared_ptr<Emboss::FontFile> &get_font_file();
std::shared_ptr<const Emboss::FontFile> &get_font_file();
// getter on active font item for access to font property
const FontItem &get_font_item() const;
@ -137,7 +137,7 @@ public:
std::string truncated_name;
// share font file data with emboss job thread
std::shared_ptr<Emboss::FontFile> font_file = nullptr;
std::shared_ptr<const Emboss::FontFile> font_file;
std::optional<size_t> imgui_font_index;