Initialize font style images in job

This commit is contained in:
Filip Sykala 2022-03-22 21:38:07 +01:00
parent 3772b3d0b5
commit 501f6f021f
6 changed files with 239 additions and 157 deletions

View File

@ -105,8 +105,12 @@ public:
/// </summary>
struct FontFileWithCache
{
// Pointer on data of the font file
std::shared_ptr<const FontFile> font_file;
// cache for glyph shape
// Cache for glyph shape
// IMPORTANT: accessible only in plater job thread !!!
// main thread only clear cache by set to another shared_ptr
std::shared_ptr<Emboss::Glyphs> cache;
FontFileWithCache() : font_file(nullptr), cache(nullptr) {}

View File

@ -184,6 +184,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Jobs/PlaterWorker.hpp
GUI/Jobs/ArrangeJob.hpp
GUI/Jobs/ArrangeJob.cpp
GUI/Jobs/CreateFontStyleImagesJob.cpp
GUI/Jobs/CreateFontStyleImagesJob.hpp
GUI/Jobs/EmbossJob.cpp
GUI/Jobs/EmbossJob.hpp
GUI/Jobs/RotoptimizeJob.hpp

View File

@ -0,0 +1,145 @@
#include "CreateFontStyleImagesJob.hpp"
// rasterization of ExPoly
#include "libslic3r/SLA/AGGRaster.hpp"
// for get DPI
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
using namespace Slic3r;
using namespace Slic3r::GUI;
CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleImagesData &&input)
: m_input(std::move(input))
{}
void CreateFontStyleImagesJob::process(Ctl &ctl)
{
// create shapes and calc size (bounding boxes)
std::vector<ExPolygons> name_shapes(m_input.styles.size());
std::vector<double> scales(m_input.styles.size());
images = std::vector<FontManager::StyleImage>(m_input.styles.size());
for (StyleImagesData::Item &item : m_input.styles) {
size_t index = &item - &m_input.styles.front();
ExPolygons &shapes = name_shapes[index];
shapes = Emboss::text2shapes(item.font, item.text.c_str(), item.prop);
// create image description
FontManager::StyleImage &image = images[index];
BoundingBox &bounding_box = image.bounding_box;
for (ExPolygon &shape : shapes)
bounding_box.merge(BoundingBox(shape.contour.points));
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font
auto mf = wxGetApp().mainframe;
// dot per inch for monitor
int dpi = get_dpi_for_window(mf);
double ppm = dpi / 25.4; // pixel per milimeter
double unit_per_em = item.font.font_file->unit_per_em;
double scale = item.prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
BoundingBoxf bb2(bounding_box.min.cast<double>(),
bounding_box.max.cast<double>());
bb2.scale(scale);
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x());
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y());
// crop image width
if (image.tex_size.x > m_input.max_width)
image.tex_size.x = m_input.max_width;
}
// arrange bounding boxes
int offset_y = 0;
width = 0;
for (FontManager::StyleImage &image : images) {
image.offset.y() = offset_y;
offset_y += image.tex_size.y+1;
if (width < image.tex_size.x)
width = image.tex_size.x;
}
height = offset_y;
for (FontManager::StyleImage &image : images) {
const Point &o = image.offset;
const ImVec2 &s = image.tex_size;
image.uv0 = ImVec2(o.x() / (double) width,
o.y() / (double) height);
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
(o.y() + s.y) / (double) height);
}
// Set up result
pixels = std::vector<unsigned char>(width * height, {0});
// upload sub textures
for (FontManager::StyleImage &image : images) {
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
size_t index = &image - &images.front();
double pixel_dim = SCALING_FACTOR / scales[index];
sla::PixelDim dim(pixel_dim, pixel_dim);
double gamma = 1.;
std::unique_ptr<sla::RasterBase> r =
sla::create_raster_grayscale_aa(resolution, dim, gamma);
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
// copy rastered data to pixels
sla::RasterEncoder encoder = [&offset = image.offset, &pix = pixels, w=width,h=height]
(const void *ptr, size_t width, size_t height, size_t num_components) {
assert((offset.x() + width) <= w);
assert((offset.y() + height) <= h);
const unsigned char *ptr2 = (const unsigned char *) ptr;
for (int x=0; x < width; ++x)
for (int y = 0; y < height; ++y) {
size_t index = (offset.y() + y)*w + offset.x() + x;
assert(index < w * h);
pix[index] = ptr2[y * width + x];
}
return sla::EncodedRaster();
};
r->encode(encoder);
}
}
void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &)
{
// upload texture on GPU
GLuint tex_id;
GLenum target = GL_TEXTURE_2D, format = GL_ALPHA, type = GL_UNSIGNED_BYTE;
GLint level = 0, border = 0;
glsafe(::glGenTextures(1, &tex_id));
glsafe(::glBindTexture(target, tex_id));
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
GLint w = width, h=height;
glsafe(::glTexImage2D(target, level, GL_ALPHA, w, h, border, format, type,
(const void *) pixels.data()));
// set up texture id
void *texture_id = (void *) (intptr_t) tex_id;
for (FontManager::StyleImage &image : images) {
image.texture_id = texture_id;
size_t index = &image - &images.front();
StyleImagesData::Item &style = m_input.styles[index];
// find manager image and copy to it
for (auto& it: m_input.mng->m_font_list) {
if (it.font_item.name != style.text ||
!(it.font_item.prop == style.prop))
continue;
it.image = image;
break;
}
}
// bind default texture
GLuint no_texture_id = 0;
glsafe(::glBindTexture(target, no_texture_id));
}

View File

@ -0,0 +1,60 @@
#ifndef slic3r_CreateFontStyleImagesJob_hpp_
#define slic3r_CreateFontStyleImagesJob_hpp_
#include <vector>
#include <string>
#include <libslic3r/Emboss.hpp>
#include "slic3r/Utils/FontManager.hpp"
#include "Job.hpp"
namespace Slic3r::GUI {
/// <summary>
/// Data needed to create Font Style Images
/// </summary>
struct StyleImagesData
{
struct Item
{
Emboss::FontFileWithCache font;
std::string text;
FontProp prop;
};
using Items = std::vector<Item>;
// Keep styles to render
Items styles;
// maximal width in pixels of image
int max_width;
// is used in finalize to set result
// and I Can't proof of alive
FontManager *mng;
};
/// <summary>
/// Create texture with name of styles written by its style
/// NOTE: Access to glyph cache is possible only from job
/// </summary>
class CreateFontStyleImagesJob : public Job
{
StyleImagesData m_input;
// Output data
// texture size
int width, height;
// texture data
std::vector<unsigned char> pixels;
// descriptors of sub textures
std::vector<FontManager::StyleImage> images;
public:
CreateFontStyleImagesJob(StyleImagesData &&input);
void process(Ctl &ctl) override;
void finalize(bool canceled, std::exception_ptr &) override;
};
} // namespace Slic3r::GUI
#endif // slic3r_EmbossJob_hpp_

View File

@ -4,7 +4,9 @@
#include <imgui/imgui_internal.h> // ImTextCharFromUtf8
#include "WxFontUtils.hpp"
#include "libslic3r/Utils.hpp" // ScopeGuard
#include "slic3r/GUI/3DScene.hpp" // ::glsafe
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
using namespace Slic3r;
using namespace Slic3r::GUI;
@ -370,166 +372,32 @@ ImFont* FontManager::extend_imgui_font_range(size_t index, const std::string& te
return load_imgui_font(index, text);
}
#include "libslic3r/SLA/AGGRaster.hpp"
void FontManager::create_texture(size_t index, const std::string &text, GLuint& tex_id, ImVec2& tex_size)
{
if (index >= m_font_list.size()) return;
Item &item = m_font_list[index];
if (!item.font_file_with_cache.has_value() && !set_up_font_file(index))
return;
#include "slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp"
const FontProp &font_prop = item.font_item.prop;
ExPolygons shapes = Emboss::text2shapes(item.font_file_with_cache,
text.c_str(), font_prop);
BoundingBox bb;
for (ExPolygon &shape : shapes) bb.merge(BoundingBox(shape.contour.points));
for (ExPolygon &shape : shapes) shape.translate(-bb.min);
double scale = font_prop.size_in_mm;
BoundingBoxf bb2 = unscaled(bb);
bb2.scale(scale);
tex_size.x = bb2.max.x() - bb2.min.x();
tex_size.y = bb2.max.y() - bb2.min.y();
sla::Resolution resolution(tex_size.x,tex_size.y);
sla::PixelDim dim(1/scale, 1/scale);
const double no_gamma = 1.;
std::unique_ptr<sla::RasterBase> r =
sla::create_raster_grayscale_aa(resolution, dim, no_gamma);
for (const ExPolygon &shape : shapes) r->draw(shape);
// reserve texture on GPU
glGenTextures(1, &tex_id);
glBindTexture(GL_TEXTURE_2D, tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
sla::RasterEncoder encoder = [](const void *ptr, size_t w, size_t h, size_t num_components) -> sla::EncodedRaster {
GLsizei width = w, height = h;
GLint border = 0;
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, border, GL_ALPHA, GL_UNSIGNED_BYTE, ptr);
return sla::EncodedRaster();
};
r->encode(encoder);
glBindTexture(GL_TEXTURE_2D, 0);
}
// for get DPI
// for access to worker
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/Plater.hpp"
void FontManager::init_style_images(int max_width) {
// check already initialized
if (m_exist_style_images) return;
// create shapes and calc size (bounding boxes)
std::vector<ExPolygons> name_shapes(m_font_list.size());
std::vector<double> scales(m_font_list.size());
StyleImagesData::Items styles;
styles.reserve(m_font_list.size());
for (Item &item : m_font_list) {
FontItem & font_item = item.font_item;
const FontProp & font_prop = font_item.prop;
size_t index = &item - &m_font_list.front();
if (!item.font_file_with_cache.has_value() && !set_up_font_file(index))
continue;
ExPolygons &shapes = name_shapes[index];
shapes = Emboss::text2shapes(item.font_file_with_cache, font_item.name.c_str(), font_prop);
// create image description
item.image = StyleImage();
StyleImage &image = *item.image;
BoundingBox &bounding_box = image.bounding_box;
for (ExPolygon &shape : shapes)
bounding_box.merge(BoundingBox(shape.contour.points));
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font
auto mf = wxGetApp().mainframe;
// dot per inch for monitor
int dpi = get_dpi_for_window(mf);
double ppm = dpi / 25.4; // pixel per milimeter
double unit_per_em = item.font_file_with_cache.font_file->unit_per_em;
double scale = font_prop.size_in_mm / unit_per_em * Emboss::SHAPE_SCALE * ppm;
scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
BoundingBoxf bb2(bounding_box.min.cast<double>(),
bounding_box.max.cast<double>());
bb2.scale(scale);
image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x());
image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y());
// crop image width
if (image.tex_size.x > max_width)
image.tex_size.x = max_width;
styles.push_back({
item.font_file_with_cache,
item.font_item.name,
item.font_item.prop
});
}
// arrange bounding boxes
int offset_y = 0;
int width = 0;
for (Item &item : m_font_list) {
if (!item.image.has_value()) continue;
StyleImage &image = *item.image;
image.offset.y() = offset_y;
offset_y += image.tex_size.y+1;
if (width < image.tex_size.x)
width = image.tex_size.x;
}
int height = offset_y;
for (Item &item : m_font_list) {
if (!item.image.has_value()) continue;
StyleImage &image = *item.image;
const Point &o = image.offset;
const ImVec2 &s = image.tex_size;
image.uv0 = ImVec2(o.x() / (double) width,
o.y() / (double) height);
image.uv1 = ImVec2((o.x() + s.x) / (double) width,
(o.y() + s.y) / (double) height);
}
// reserve texture on GPU
GLuint tex_id;
GLenum target = GL_TEXTURE_2D, format = GL_ALPHA, type = GL_UNSIGNED_BYTE;
GLint level = 0, border = 0;
glsafe(::glGenTextures(1, &tex_id));
glsafe(::glBindTexture(target, tex_id));
glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
// texture size
GLint w = width, h = height;
glsafe(::glTexImage2D(target, level, GL_ALPHA, w, h, border, format, type, nullptr));
// set up texture id
void *texture_id = (void *)(intptr_t) tex_id;
for (Item &item : m_font_list)
if (item.image.has_value())
item.image->texture_id = texture_id;
// upload sub textures
for (Item &item : m_font_list) {
if (!item.image.has_value()) continue;
StyleImage &image = *item.image;
sla::Resolution resolution(image.tex_size.x, image.tex_size.y);
size_t index = &item - &m_font_list.front();
double pixel_dim = SCALING_FACTOR / scales[index];
sla::PixelDim dim(pixel_dim, pixel_dim);
double gamma = 1.;
std::unique_ptr<sla::RasterBase> r = sla::create_raster_grayscale_aa(resolution, dim, gamma);
for (const ExPolygon &shape : name_shapes[index]) r->draw(shape);
const Point& offset = image.offset;
sla::RasterEncoder encoder =
[offset, target, level, format, type]
(const void *ptr, size_t w, size_t h, size_t num_components) {
GLint sub_w = w, sub_h = h, xoffset = offset.x(), yoffset = offset.y();
glsafe(::glTexSubImage2D(target, level, xoffset, yoffset, sub_w, sub_h, format, type, ptr));
return sla::EncodedRaster();
};
// upload texture data to GPU
r->encode(encoder);
}
// bind default texture
GLuint no_texture_id = 0;
glsafe(::glBindTexture(target, no_texture_id));
auto &worker = wxGetApp().plater()->get_ui_job_worker();
StyleImagesData data{styles, max_width, this};
queue_job(worker, std::make_unique<CreateFontStyleImagesJob>(std::move(data)));
m_exist_style_images = true;
}
@ -537,14 +405,19 @@ void FontManager::init_style_images(int max_width) {
void FontManager::free_style_images() {
if (!is_activ_font()) return;
if (!m_exist_style_images) return;
GLuint tex_id = (GLuint) (intptr_t) m_font_list.front().image->texture_id;
for (Item &it : m_font_list) it.image.reset();
glsafe(::glDeleteTextures(1, &tex_id));
GLuint tex_id = 0;
for (Item &it : m_font_list) {
if (tex_id == 0 && it.image.has_value())
tex_id = (GLuint)(intptr_t) it.image->texture_id;
it.image.reset();
}
if (tex_id != 0)
glsafe(::glDeleteTextures(1, &tex_id));
m_exist_style_images = false;
}
void FontManager::free_imgui_fonts()
{
for (auto &item : m_font_list)

View File

@ -4,12 +4,11 @@
#include <optional>
#include <imgui/imgui.h>
#include <GL/glew.h>
#include "libslic3r/Emboss.hpp"
#include <libslic3r/Emboss.hpp>
class wxFont;
namespace Slic3r::GUI {
/// <summary>
/// GUI list of loaded fonts
/// Keep pointer to ImGui font pointers
@ -18,6 +17,8 @@ namespace Slic3r::GUI {
/// </summary>
class FontManager
{
friend class CreateFontStyleImagesJob;
public:
FontManager(const ImWchar *language_glyph_range);
~FontManager();
@ -109,8 +110,6 @@ public:
void init_style_images(int max_width);
void free_style_images();
void create_texture(size_t font_index, const std::string &text, GLuint& tex_id, ImVec2& tex_size);
struct Item;
// access to all managed fonts
const std::vector<Item> &get_fonts() const;
@ -187,7 +186,6 @@ private:
static bool is_text_in_ranges(const ImWchar *ranges, const std::string &text);
static bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter);
bool check_imgui_font_range(ImFont *font, const std::string &text);
void free_imgui_fonts();
bool set_up_font_file(size_t item_index);