From 0414eea9cf33762af70ff33985b85ff0f18fc7fc Mon Sep 17 00:00:00 2001 From: Chlumsky Date: Wed, 12 Jul 2023 18:35:31 +0200 Subject: [PATCH] Wrap the library into a single header + CPP file pair --- all-in-one/.gitignore | 3 + all-in-one/CMakeLists.txt | 63 ++++++++++++++++++ all-in-one/generate.py | 135 ++++++++++++++++++++++++++++++++++++++ all-in-one/test.cpp | 52 +++++++++++++++ all-in-one/vcpkg.json | 8 +++ 5 files changed, 261 insertions(+) create mode 100644 all-in-one/.gitignore create mode 100644 all-in-one/CMakeLists.txt create mode 100644 all-in-one/generate.py create mode 100644 all-in-one/test.cpp create mode 100644 all-in-one/vcpkg.json diff --git a/all-in-one/.gitignore b/all-in-one/.gitignore new file mode 100644 index 0000000..b863be8 --- /dev/null +++ b/all-in-one/.gitignore @@ -0,0 +1,3 @@ +msdfgen.h +msdfgen.cpp +build/ diff --git a/all-in-one/CMakeLists.txt b/all-in-one/CMakeLists.txt new file mode 100644 index 0000000..956dc4b --- /dev/null +++ b/all-in-one/CMakeLists.txt @@ -0,0 +1,63 @@ + +cmake_minimum_required(VERSION 3.15) + +option(MSDFGEN_USE_VCPKG "Use vcpkg package manager to link project dependencies" ON) +option(MSDFGEN_USE_FREETYPE "Build with the FreeType library" ON) + +if(MSDFGEN_USE_VCPKG AND MSDFGEN_USE_FREETYPE) + # Make sure that vcpkg toolchain file is set + if(NOT CMAKE_TOOLCHAIN_FILE) + if(DEFINED ENV{VCPKG_ROOT}) + set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") + else() + message(SEND_ERROR "Vcpkg toolchain not configured. Either set VCPKG_ROOT environment variable or pass -DCMAKE_TOOLCHAIN_FILE=VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake to cmake") + endif() + endif() + # Default to statically linked vcpkg triplet on Windows + if(WIN32 AND NOT VCPKG_TARGET_TRIPLET AND NOT MSDFGEN_DYNAMIC_RUNTIME) + if(CMAKE_GENERATOR_PLATFORM MATCHES "64$" AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + set(VCPKG_TARGET_TRIPLET "x64-windows-static") + elseif(CMAKE_GENERATOR_PLATFORM MATCHES "32$" OR CMAKE_GENERATOR_PLATFORM STREQUAL "x86") + set(VCPKG_TARGET_TRIPLET "x86-windows-static") + else() + if(CMAKE_GENERATOR_PLATFORM) + message(WARNING "Vcpkg triplet not explicitly specified and could not be deduced. Recommend using -DVCPKG_TARGET_TRIPLET=x64-windows-static or similar") + else() + message(WARNING "Vcpkg triplet not explicitly specified and could not be deduced. Recommend using -A to explicitly select platform (Win32 or x64)") + endif() + endif() + endif() +endif() + +project(msdfgen LANGUAGES CXX) + +if(MAX_WARNING_LEVEL) + if (MSVC) + add_compile_options(/W4) + else() + add_compile_options(-Wall -Wextra -Wpedantic) + endif() +endif() + +find_package(Python3 REQUIRED COMPONENTS Interpreter) +if(MSDFGEN_USE_FREETYPE AND NOT TARGET Freetype::Freetype) + find_package(Freetype REQUIRED) +endif() + +execute_process(COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/generate.py") + +add_library(msdfgen "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen.h" "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen.cpp") +set_property(TARGET msdfgen PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_compile_features(msdfgen PUBLIC cxx_std_11) +if(MSDFGEN_USE_FREETYPE) + target_compile_definitions(msdfgen PUBLIC MSDFGEN_USE_FREETYPE) + target_link_libraries(msdfgen PRIVATE Freetype::Freetype) +endif() + +add_executable(msdfgen-test "${CMAKE_CURRENT_SOURCE_DIR}/test.cpp") +set_property(TARGET msdfgen-test PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_link_libraries(msdfgen-test PRIVATE msdfgen) +set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT msdfgen-test) +if(MSDFGEN_USE_FREETYPE) + target_link_libraries(msdfgen-test PRIVATE Freetype::Freetype) +endif() diff --git a/all-in-one/generate.py b/all-in-one/generate.py new file mode 100644 index 0000000..840b90f --- /dev/null +++ b/all-in-one/generate.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +import os +import re + +rootDir = os.path.join(os.path.dirname(__file__), '..') + +sourceList = [ + 'core/arithmetics.hpp', + 'core/equation-solver.h', + 'core/equation-solver.cpp', + 'core/Vector2.h', + 'core/Vector2.cpp', + 'core/pixel-conversion.hpp', + 'core/BitmapRef.hpp', + 'core/Bitmap.h', + 'core/Bitmap.hpp', + 'core/Projection.h', + 'core/Projection.cpp', + 'core/SignedDistance.h', + 'core/SignedDistance.cpp', + 'core/Scanline.h', + 'core/Scanline.cpp', + 'core/EdgeColor.h', + 'core/edge-segments.h', + 'core/edge-segments.cpp', + 'core/EdgeHolder.h', + 'core/EdgeHolder.cpp', + 'core/Contour.h', + 'core/Contour.cpp', + 'core/Shape.h', + 'core/Shape.cpp', + 'core/bitmap-interpolation.hpp', + 'core/edge-coloring.h', + 'core/edge-coloring.cpp', + 'core/edge-selectors.h', + 'core/edge-selectors.cpp', + 'core/contour-combiners.h', + 'core/contour-combiners.cpp', + 'core/ShapeDistanceFinder.h', + 'core/ShapeDistanceFinder.hpp', + 'core/generator-config.h', + 'core/msdf-error-correction.h', + 'core/msdf-error-correction.cpp', + 'core/MSDFErrorCorrection.h', + 'core/MSDFErrorCorrection.cpp', + 'core/msdfgen.cpp', + 'ext/import-font.h', + 'ext/import-font.cpp', + 'ext/resolve-shape-geometry.h', + 'ext/resolve-shape-geometry.cpp', + 'msdfgen.h' +] + +header = """ +#pragma once + +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif + +#include +#include +#include +#include +#include +""" + +source = """ +#include "msdfgen.h" + +#include +#include + +#ifdef MSDFGEN_USE_FREETYPE +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#ifndef MSDFGEN_DISABLE_VARIABLE_FONTS +#include FT_MULTIPLE_MASTERS_H +#endif +#endif +""" + +with open(os.path.join(rootDir, 'LICENSE.txt'), 'r') as file: + license = file.read() +license = '\n'.join([' * '+line for line in license.strip().split('\n')]) + +for filename in sourceList: + with open(os.path.join(rootDir, *filename.split('/')), 'r') as file: + src = file.read() + src = '\n'.join([line for line in src.split('\n') if not re.match(r'^\s*#(include\s.*|pragma\s+once)\s*$', line)]) + if filename.startswith('ext/import-font.'): + src = '#ifdef MSDFGEN_USE_FREETYPE\n\n'+src+'\n\n#endif\n\n' + if filename.endswith('.h') or filename.endswith('.hpp'): + header += '\n\n'+src + if filename.endswith('.cpp'): + source += '\n\n'+src + +header = '\n'+re.sub(r'\n{3,}', '\n\n', re.sub(r'}\s*namespace\s+msdfgen\s*{', '', re.sub(r'\/\*[^\*].*?\*\/', '', header, flags=re.DOTALL))).strip()+'\n' +source = '\n'+re.sub(r'\n{3,}', '\n\n', re.sub(r'}\s*namespace\s+msdfgen\s*{', '', re.sub(r'\/\*[^\*].*?\*\/', '', source, flags=re.DOTALL))).strip()+'\n' + +header = """ +/* + * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR + * --------------------------------------------- + * A utility by Viktor Chlumsky, (c) 2014 - 2023 + * https://github.com/Chlumsky/msdfgen + * Published under the MIT license + * + * The technique used to generate multi-channel distance fields in this code + * was developed by Viktor Chlumsky in 2014 for his master's thesis, + * "Shape Decomposition for Multi-Channel Distance Fields". It provides improved + * quality of sharp corners in glyphs and other 2D shapes compared to monochrome + * distance fields. To reconstruct an image of the shape, apply the median of three + * operation on the triplet of sampled signed distance values. + * + */ +"""+header + +source = """ +/* + * MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR + * --------------------------------------------- + * https://github.com/Chlumsky/msdfgen + * +"""+license+""" + * + */ +"""+source + +with open(os.path.join(os.path.dirname(__file__), 'msdfgen.h'), 'w') as file: + file.write(header) +with open(os.path.join(os.path.dirname(__file__), 'msdfgen.cpp'), 'w') as file: + file.write(source) diff --git a/all-in-one/test.cpp b/all-in-one/test.cpp new file mode 100644 index 0000000..014278a --- /dev/null +++ b/all-in-one/test.cpp @@ -0,0 +1,52 @@ + +#include +#include "msdfgen.h" + +using namespace msdfgen; + +static bool saveRGBA(const char *filename, const BitmapConstRef &bitmap) { + if (FILE *f = fopen(filename, "wb")) { + char header[12]; + header[0] = 'R', header[1] = 'G', header[2] = 'B', header[3] = 'A'; + header[4] = char(bitmap.width>>24); + header[5] = char(bitmap.width>>16); + header[6] = char(bitmap.width>>8); + header[7] = char(bitmap.width); + header[8] = char(bitmap.height>>24); + header[9] = char(bitmap.height>>16); + header[10] = char(bitmap.height>>8); + header[11] = char(bitmap.height); + fwrite(header, 1, 12, f); + Bitmap byteBitmap(bitmap.width, bitmap.height); + byte *d = (byte *) byteBitmap; + for (const float *s = bitmap.pixels, *end = bitmap.pixels+4*bitmap.width*bitmap.height; s < end; ++d, ++s) + *d = pixelFloatToByte(*s); + fwrite((const byte *) byteBitmap, 1, 4*bitmap.width*bitmap.height, f); + fclose(f); + return true; + } + return false; +} + +int main() { +#ifdef MSDFGEN_USE_FREETYPE + bool success = false; + if (FreetypeHandle *ft = initializeFreetype()) { + if (FontHandle *font = loadFont(ft, "C:\\Windows\\Fonts\\arialbd.ttf")) { + Shape shape; + if (loadGlyph(shape, font, 'A')) { + shape.normalize(); + edgeColoringByDistance(shape, 3.0); + Bitmap msdf(32, 32); + generateMTSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0)); + success = saveRGBA("output.rgba", msdf); + } + destroyFont(font); + } + deinitializeFreetype(ft); + } + return success ? 0 : 1; +#else + return 0; +#endif +} diff --git a/all-in-one/vcpkg.json b/all-in-one/vcpkg.json new file mode 100644 index 0000000..87ac727 --- /dev/null +++ b/all-in-one/vcpkg.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/master/docs/vcpkg.schema.json", + "name": "msdfgen-all-in-one", + "version": "1.10.0", + "dependencies": [ + "freetype" + ] +}