163 lines
5.7 KiB
C++
163 lines
5.7 KiB
C++
/**
|
|
This file is a part of our_dick
|
|
Copyright (C) 2022 rexy712
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef OUR_DICK_ENGINE_FONT_HPP
|
|
#define OUR_DICK_ENGINE_FONT_HPP
|
|
|
|
#include <freetype2/ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include <map>
|
|
#include <optional>
|
|
#include <utility> //pair
|
|
#include <memory>
|
|
#include <algorithm> //min, max
|
|
|
|
#include "gfx/ogl/texture.hpp"
|
|
#include "config.hpp"
|
|
|
|
namespace egn{
|
|
|
|
struct font_character{
|
|
rml::vec2<size_t> atlas_offset;
|
|
rml::vec2i size;
|
|
rml::vec2i bearing;
|
|
rml::vec2i advance;
|
|
rml::vec2f texture_coords[4];
|
|
|
|
public:
|
|
float aspect_ratio(void)const;
|
|
};
|
|
|
|
class font_atlas : public gfx::ogl::texture
|
|
{
|
|
public:
|
|
using map_type = std::map<int, font_character>;
|
|
|
|
private:
|
|
map_type m_atlas_data;
|
|
size_t m_requested_width, m_requested_height;
|
|
|
|
public:
|
|
font_atlas(gfx::ogl::texture&& t, map_type&& map, size_t w, size_t h);
|
|
font_atlas(const font_atlas&) = default;
|
|
font_atlas(font_atlas&&) = default;
|
|
~font_atlas(void) = default;
|
|
|
|
font_atlas& operator=(const font_atlas&) = default;
|
|
font_atlas& operator=(font_atlas&&) = default;
|
|
|
|
map_type& metadata(void);
|
|
const map_type& metadata(void)const;
|
|
map_type&& release_metadata(void);
|
|
|
|
font_character& operator[](int character);
|
|
|
|
rml::vec2<size_t> glyph_size(void)const;
|
|
};
|
|
|
|
class font
|
|
{
|
|
private:
|
|
static inline bool s_initialized = false;
|
|
static inline FT_Library s_ft;
|
|
|
|
private:
|
|
FT_Face m_face = nullptr;
|
|
int m_depth = 3;
|
|
|
|
public:
|
|
font(const char* file, int depth = 3);
|
|
~font(void);
|
|
|
|
template<class... Ts>
|
|
font_atlas generate_atlas(size_t glyph_w, size_t glyph_h, Ts&&... ranges);
|
|
|
|
std::optional<gfx::ogl::texture> generate_glyph(char character, int width, int height);
|
|
|
|
private:
|
|
using atlas_meta = std::tuple<size_t,size_t,size_t,size_t>;
|
|
template<class... Ts>
|
|
void generate_atlas_piece_(gfx::ogl::texture& atlas, font_atlas::map_type& metadata, rml::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range, Ts&&... ranges);
|
|
void generate_atlas_piece_impl_(gfx::ogl::texture& atlas, font_atlas::map_type& metadata, rml::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range);
|
|
template<class... Ts>
|
|
atlas_meta get_atlas_size_ranges_(Ts&&... ranges);
|
|
template<class... Ts>
|
|
void get_atlas_size_ranges_recurse_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range, Ts&&... ranges);
|
|
void get_atlas_size_ranges_impl_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range);
|
|
|
|
private:
|
|
static bool initialize_freetype_(void);
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class... Ts>
|
|
void font::generate_atlas_piece_(gfx::ogl::texture& atlas, font_atlas::map_type& metadata, rml::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range, Ts&&... ranges){
|
|
generate_atlas_piece_impl_(atlas, metadata, target_pos, info, data_buffer, range);
|
|
|
|
if constexpr(sizeof...(ranges) > 0){
|
|
generate_atlas_piece_(atlas, metadata, target_pos, info, data_buffer, std::forward<Ts>(ranges)...);
|
|
}
|
|
}
|
|
template<class... Ts>
|
|
font_atlas font::generate_atlas(size_t glyph_w, size_t glyph_h, Ts&&... ranges){
|
|
rexy::debug::verbose::print("Generating font atlas for font '%s'\n", m_face->family_name);
|
|
FT_Set_Pixel_Sizes(m_face, glyph_w, glyph_h);
|
|
auto [atlas_width, atlas_height, max_glyph_width, max_glyph_height] = get_atlas_size_ranges_(std::forward<Ts>(ranges)...);
|
|
const auto format = (m_depth == 3) ? GL_RGB : GL_RED;
|
|
|
|
|
|
gfx::ogl::texture atlas(format, atlas_width, atlas_height, GL_UNSIGNED_BYTE, false);
|
|
font_atlas::map_type atlas_metadata;
|
|
rml::vec2<size_t> target_position = {0, 0};
|
|
std::unique_ptr<unsigned char[]> dest_data(new unsigned char[max_glyph_height * max_glyph_width * m_depth]);
|
|
|
|
generate_atlas_piece_(atlas, atlas_metadata, target_position, std::tuple{atlas_width, atlas_height, max_glyph_width, max_glyph_height}, dest_data.get(), std::forward<Ts>(ranges)...);
|
|
return {std::move(atlas), std::move(atlas_metadata), glyph_w, glyph_h};
|
|
}
|
|
template<class... Ts>
|
|
auto font::get_atlas_size_ranges_(Ts&&... ranges) -> atlas_meta{
|
|
size_t avg_width = 0;
|
|
size_t avg_height = 0;
|
|
size_t max_glyph_width = 0;
|
|
size_t max_glyph_height = 0;
|
|
size_t count = 0;
|
|
|
|
get_atlas_size_ranges_recurse_(avg_width, avg_height, max_glyph_width, max_glyph_height, count, std::forward<Ts>(ranges)...);
|
|
|
|
avg_width /= count;
|
|
avg_height /= count;
|
|
const size_t est_width = avg_width * (std::min(size_t{20}, count) + 1);
|
|
const size_t est_height = (max_glyph_height * (std::round(count / 20) + 1));
|
|
|
|
return {est_width / m_depth, est_height, max_glyph_width / m_depth, max_glyph_height};
|
|
|
|
}
|
|
template<class... Ts>
|
|
void font::get_atlas_size_ranges_recurse_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range, Ts&&... ranges){
|
|
get_atlas_size_ranges_impl_(avg_w, avg_h, mgw, mgh, count, range);
|
|
if constexpr(sizeof...(ranges) > 0){
|
|
get_atlas_size_ranges_recurse_(avg_w, avg_h, mgw, mgh, count, std::forward<Ts>(ranges)...);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|