Compare commits
No commits in common. "42561d5ace9f45478ef3aae3a94e1c714ed07cc1" and "37dcda3d962893a1b60488bfa9514abcf7e189d7" have entirely different histories.
42561d5ace
...
37dcda3d96
|
|
@ -3,7 +3,5 @@ cmake_minimum_required(VERSION 3.20)
|
|||
project(all)
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(applications)
|
||||
add_subdirectory(bmrshared)
|
||||
add_subdirectory(bmrshared-freetype)
|
||||
add_subdirectory(bmrshared-magic)
|
||||
add_subdirectory(bmrshared-magic)
|
||||
|
|
@ -1 +0,0 @@
|
|||
add_subdirectory(text2image)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
find_package(Boost 1.84.0 REQUIRED COMPONENTS program_options headers CONFIG)
|
||||
find_package(JPEG REQUIRED)
|
||||
|
||||
project(text2image)
|
||||
|
||||
add_executable(
|
||||
${PROJECT_NAME}
|
||||
./src/main.cpp
|
||||
)
|
||||
|
||||
set_property(
|
||||
TARGET ${PROJECT_NAME}
|
||||
PROPERTY CXX_STANDARD 20
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
bmrshared-freetype
|
||||
Boost::program_options
|
||||
Boost::headers
|
||||
JPEG::JPEG
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
|
||||
#include <bmrshared/freetype_face.hpp>
|
||||
#include <bmrshared/freetype_lib.hpp>
|
||||
#include <bmrshared/freetype_utils.hpp>
|
||||
#include <boost/gil/extension/io/jpeg.hpp>
|
||||
#include <boost/gil/image.hpp>
|
||||
#include <boost/gil/image_view.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string font_file_str;
|
||||
std::string output_file_str;
|
||||
std::string text;
|
||||
int font_size;
|
||||
|
||||
bpo::options_description desc("Options");
|
||||
|
||||
// clang-format off
|
||||
desc.add_options()
|
||||
("text,t", bpo::value<std::string>(&text), "The text to render.")
|
||||
("font,f", bpo::value<std::string>(&font_file_str), "TTF font file to use for rendering.")
|
||||
("size,s", bpo::value<int>(&font_size)->default_value(16), "Font size in pixels.")
|
||||
("output,o", bpo::value<std::string>(&output_file_str), "Output file path.")
|
||||
("help,h", "Show help information.");
|
||||
// clang-format on
|
||||
|
||||
std::filesystem::path font_file;
|
||||
std::filesystem::path output_file;
|
||||
try
|
||||
{
|
||||
bpo::variables_map vm;
|
||||
bpo::store(bpo::parse_command_line(argc, argv, desc), vm);
|
||||
bpo::notify(vm);
|
||||
|
||||
if (vm.count("help"))
|
||||
{
|
||||
std::cout << desc << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
font_file = font_file_str;
|
||||
if (!std::filesystem::is_regular_file(font_file))
|
||||
{
|
||||
throw std::runtime_error("TTF font file path is invalid.");
|
||||
}
|
||||
|
||||
output_file = output_file_str;
|
||||
if (!std::filesystem::is_directory(output_file.parent_path()))
|
||||
{
|
||||
throw std::runtime_error("Output directory is invalid.");
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error while processing commandline arguments.\n" << e.what() << "\n" << desc << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "text=" << text << "\nfont=" << font_file << "\nsize=" << font_size << "\noutput=" << output_file;
|
||||
|
||||
|
||||
auto ftLib = std::make_shared<bmrshared::freetype_lib>();
|
||||
auto ftFace = std::make_shared<bmrshared::freetype_face>(ftLib, font_file, 0, font_size, true);
|
||||
|
||||
const auto dim = bmrshared::freetype_calculate_dimensions(*ftFace, text);
|
||||
const int pixel_width = dim.width / 64;
|
||||
const int pixel_height = dim.height / 64;
|
||||
const int bearingY = dim.vertOriginOffsetY / 64;
|
||||
|
||||
boost::gil::gray8_image_t image(pixel_width, pixel_height);
|
||||
auto view = boost::gil::view(image);
|
||||
boost::gil::fill_pixels(view, boost::gil::gray8_pixel_t(0));
|
||||
|
||||
auto painter = [&](int x, int y, const uint8_t level) -> void
|
||||
{
|
||||
const auto& _x = x;
|
||||
const auto _y = y + bearingY;
|
||||
|
||||
if (_x >= 0 && _y >= 0 && _x < pixel_width && _y < pixel_height)
|
||||
{
|
||||
const auto iter = view.at({_x, _y});
|
||||
boost::gil::color_convert(boost::gil::gray8_pixel_t(level), *iter);
|
||||
}
|
||||
};
|
||||
|
||||
bmrshared::freetype_paint(*ftFace, text, painter);
|
||||
boost::gil::write_view(output_file,
|
||||
boost::gil::view(image),
|
||||
boost::gil::image_write_info<boost::gil::jpeg_tag>(95));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
find_package(freetype REQUIRED)
|
||||
find_package(Boost 1.84.0 REQUIRED COMPONENTS headers CONFIG)
|
||||
|
||||
project(bmrshared-freetype)
|
||||
|
||||
add_library(
|
||||
${PROJECT_NAME}
|
||||
./lib/freetype_lib.cpp
|
||||
./lib/freetype_face.cpp
|
||||
./lib/freetype_utils.cpp
|
||||
)
|
||||
|
||||
set_property(
|
||||
TARGET ${PROJECT_NAME}
|
||||
PROPERTY CXX_STANDARD 20
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC
|
||||
Freetype::Freetype
|
||||
Boost::headers
|
||||
)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <boost/gil/image.hpp>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_BITMAP_H
|
||||
|
||||
namespace bmrshared
|
||||
{
|
||||
class freetype_lib;
|
||||
|
||||
class freetype_face final : public std::enable_shared_from_this<freetype_face>
|
||||
{
|
||||
using character_index = FT_ULong;
|
||||
using pixel_size = FT_UInt;
|
||||
|
||||
public:
|
||||
freetype_face(std::shared_ptr<freetype_lib> lib,
|
||||
const std::string& font_path,
|
||||
pixel_size pixel_width,
|
||||
pixel_size pixel_height,
|
||||
bool render_monochrome
|
||||
);
|
||||
|
||||
~freetype_face();
|
||||
|
||||
FT_BBox get_boundbox() const;
|
||||
FT_Glyph_Metrics get_dimensions(character_index character_index) const;
|
||||
|
||||
void render(
|
||||
character_index character_index,
|
||||
std::function<void(int x,
|
||||
int y,
|
||||
const uint8_t level
|
||||
)> painter);
|
||||
|
||||
private:
|
||||
FT_Int32 get_load_flags() const;
|
||||
FT_Render_Mode get_render_mode() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<freetype_lib> m_lib;
|
||||
FT_Face m_face;
|
||||
bool m_renderMonochrome;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
namespace bmrshared
|
||||
{
|
||||
class freetype_lib final : public std::enable_shared_from_this<freetype_lib>
|
||||
{
|
||||
public:
|
||||
freetype_lib();
|
||||
~freetype_lib();
|
||||
|
||||
FT_Library get_ft_library();
|
||||
|
||||
private:
|
||||
FT_Library m_library;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
namespace bmrshared
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#pragma once
|
||||
#include <bmrshared/freetype_face.hpp>
|
||||
#include <string_view>
|
||||
|
||||
namespace bmrshared
|
||||
{
|
||||
struct dimensions final
|
||||
{
|
||||
FT_Pos width;
|
||||
FT_Pos height;
|
||||
FT_Pos vertOriginOffsetY;
|
||||
};
|
||||
|
||||
dimensions freetype_calculate_dimensions(freetype_face& face, std::string_view text);
|
||||
void freetype_paint(freetype_face& face, std::string_view text, const std::function<void(int x, int y, uint8_t level)>& paintfn);
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#include <bmrshared/freetype_face.hpp>
|
||||
#include <bmrshared/freetype_lib.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace bmrshared;
|
||||
|
||||
freetype_face::freetype_face(
|
||||
std::shared_ptr<freetype_lib> lib,
|
||||
const std::string& font_path,
|
||||
pixel_size pixel_width,
|
||||
pixel_size pixel_height,
|
||||
bool render_monochrome)
|
||||
: m_lib(lib)
|
||||
, m_renderMonochrome(render_monochrome)
|
||||
{
|
||||
FT_New_Face(
|
||||
m_lib->get_ft_library(),
|
||||
font_path.c_str(),
|
||||
0,
|
||||
&m_face);
|
||||
|
||||
FT_Set_Pixel_Sizes(
|
||||
m_face,
|
||||
pixel_width,
|
||||
pixel_height);
|
||||
}
|
||||
|
||||
freetype_face::~freetype_face()
|
||||
{
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
|
||||
FT_BBox freetype_face::get_boundbox() const
|
||||
{
|
||||
return m_face->bbox;
|
||||
}
|
||||
|
||||
FT_Glyph_Metrics freetype_face::get_dimensions(
|
||||
character_index character_index) const
|
||||
{
|
||||
// Load glyph and retrieve metrics.
|
||||
const auto glyph_index = FT_Get_Char_Index(m_face, character_index);
|
||||
FT_Load_Glyph(m_face, glyph_index, get_load_flags());
|
||||
|
||||
FT_GlyphSlot& glyphSlot = m_face->glyph;
|
||||
return glyphSlot->metrics;
|
||||
}
|
||||
|
||||
|
||||
void freetype_face::render(
|
||||
FT_ULong character_index,
|
||||
std::function<void(int x,
|
||||
int y,
|
||||
uint8_t level
|
||||
)> painter)
|
||||
{
|
||||
// Load glyph and retrieve metrics.
|
||||
const auto glyph_index = FT_Get_Char_Index(m_face, character_index);
|
||||
FT_Load_Glyph(m_face, glyph_index, get_load_flags());
|
||||
const FT_GlyphSlot& glyphSlot = m_face->glyph;
|
||||
|
||||
// render glyph
|
||||
FT_Render_Glyph(glyphSlot, get_render_mode());
|
||||
const FT_Bitmap& bitmap = glyphSlot->bitmap;
|
||||
|
||||
FT_Bitmap target;
|
||||
FT_Bitmap_Init(&target);
|
||||
FT_Bitmap_Convert(m_lib->get_ft_library(), &bitmap, &target, 1);
|
||||
|
||||
for(unsigned int x = 0; x < target.width; ++x)
|
||||
{
|
||||
for(unsigned int y = 0; y < target.rows; ++y)
|
||||
{
|
||||
const uint8_t& value = 0xff * target.buffer[x + target.width * y];
|
||||
painter(x,y,value);
|
||||
}
|
||||
}
|
||||
|
||||
FT_Bitmap_Done(m_lib->get_ft_library(), &target);
|
||||
}
|
||||
|
||||
FT_Int32 freetype_face::get_load_flags() const
|
||||
{
|
||||
FT_Int32 loadFlags = FT_LOAD_TARGET_NORMAL;
|
||||
if (m_renderMonochrome)
|
||||
{
|
||||
loadFlags = FT_LOAD_TARGET_MONO;
|
||||
}
|
||||
return loadFlags;
|
||||
}
|
||||
|
||||
FT_Render_Mode freetype_face::get_render_mode() const
|
||||
{
|
||||
return FT_RENDER_MODE_MONO;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#include <bmrshared/freetype_lib.hpp>
|
||||
|
||||
using bmrshared::freetype_lib;
|
||||
|
||||
freetype_lib::freetype_lib()
|
||||
{
|
||||
FT_Init_FreeType(&m_library);
|
||||
}
|
||||
|
||||
freetype_lib::~freetype_lib()
|
||||
{
|
||||
FT_Done_FreeType(m_library);
|
||||
}
|
||||
|
||||
FT_Library freetype_lib::get_ft_library()
|
||||
{
|
||||
return m_library;
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
// GNU Lesser General Public License v3.0
|
||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License v3.0 as published by
|
||||
// the Free Software Foundation.
|
||||
//
|
||||
#include <bmrshared/freetype_utils.hpp>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace bmrshared
|
||||
{
|
||||
|
||||
dimensions freetype_calculate_dimensions(freetype_face& face, std::string_view text)
|
||||
{
|
||||
FT_Pos line_width = 0;
|
||||
FT_Pos line_above_origin = 0;
|
||||
FT_Pos line_below_origin = 0;
|
||||
|
||||
for (const char& c : text)
|
||||
{
|
||||
const FT_Glyph_Metrics& dim = face.get_dimensions(c);
|
||||
|
||||
line_width += dim.horiAdvance;
|
||||
const auto char_above_origin = dim.horiBearingY;
|
||||
const auto char_below_origin = dim.height - dim.horiBearingY;
|
||||
|
||||
line_above_origin = std::max(line_above_origin, char_above_origin);
|
||||
line_below_origin = std::max(line_below_origin, char_below_origin);
|
||||
}
|
||||
return {.width = line_width, .height = (line_above_origin + line_below_origin), .vertOriginOffsetY = line_above_origin};
|
||||
}
|
||||
|
||||
void freetype_paint(freetype_face& face, std::string_view text, const std::function<void(int x, int y, uint8_t level)>& paintfn)
|
||||
{
|
||||
FT_Pos line_x = 0;
|
||||
FT_Pos char_offset_x = 0;
|
||||
FT_Pos char_offset_y = 0;
|
||||
auto painter_char = [&](int x, int y, uint8_t level)
|
||||
{
|
||||
paintfn((char_offset_x + x), (y - char_offset_y), level);
|
||||
};
|
||||
|
||||
for( const char& c : text)
|
||||
{
|
||||
const FT_Glyph_Metrics& dim = face.get_dimensions(c);
|
||||
char_offset_x = (line_x + (dim.horiAdvance - dim.width)/2)/64;
|
||||
char_offset_y = dim.horiBearingY/64;
|
||||
|
||||
face.render(c, painter_char);
|
||||
line_x += dim.horiAdvance;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps
|
|||
|
||||
class HelloConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
requires = "boost/1.84.0", "gtest/1.14.0", "libmagic/5.45", "freetype/2.13.3", "libjpeg/9f"
|
||||
requires = "boost/1.84.0", "gtest/1.14.0", "libmagic/5.45"
|
||||
generators = "CMakeDeps"
|
||||
build_policy = "*"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue