Compare commits
42 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
30b6f4fd1a | |
|
|
f7eb7efdad | |
|
|
7e8d34645a | |
|
|
c27de5988d | |
|
|
d120bb3e05 | |
|
|
b0a423bf00 | |
|
|
2588a2aae3 | |
|
|
b67fb967c3 | |
|
|
f424021a2b | |
|
|
bed35b13b0 | |
|
|
3a4f181674 | |
|
|
b716311880 | |
|
|
37d185878d | |
|
|
0f48aaa727 | |
|
|
37ffbd85ee | |
|
|
ae441c9989 | |
|
|
3e73a31618 | |
|
|
f1ad23f7c4 | |
|
|
3cf874b39a | |
|
|
b1af88cfca | |
|
|
39b0f28b44 | |
|
|
f6a1bc9f76 | |
|
|
132c3af316 | |
|
|
718a0ca77c | |
|
|
6932b35c00 | |
|
|
50d1a1c275 | |
|
|
b086f93a36 | |
|
|
a99a4575a4 | |
|
|
dadc65c85c | |
|
|
b5d54f3012 | |
|
|
ae40bf4a3a | |
|
|
3ff9b05254 | |
|
|
ed96d92185 | |
|
|
a49bf0d5ea | |
|
|
0e81034513 | |
|
|
d238eecc5b | |
|
|
67510959f1 | |
|
|
84e8c25241 | |
|
|
7dea527d8d | |
|
|
690ae5fb16 | |
|
|
e44853e47e | |
|
|
c9ab9974ce |
|
|
@ -0,0 +1 @@
|
|||
* text=auto
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
Debug/
|
||||
Release/
|
||||
Debug Library/
|
||||
Release Library/
|
||||
x86/
|
||||
x64/
|
||||
/build/
|
||||
/Debug/
|
||||
/Release/
|
||||
/Debug Library/
|
||||
/Release Library/
|
||||
/x86/
|
||||
/x64/
|
||||
.vs/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
*.exe
|
||||
*.zip
|
||||
*.user
|
||||
*.sdf
|
||||
*.pdb
|
||||
|
|
@ -13,7 +18,9 @@ x64/
|
|||
*.suo
|
||||
*.VC.opendb
|
||||
*.VC.db
|
||||
bin/*.lib
|
||||
/bin/*.lib
|
||||
/bin/msdf-atlas-gen
|
||||
output.png
|
||||
CMakeUserPresets.json
|
||||
out/
|
||||
build/
|
||||
/cmake-gen.bat
|
||||
|
|
|
|||
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -1,4 +1,41 @@
|
|||
|
||||
## Version 1.3 (2024-06-01)
|
||||
|
||||
- Updated to MSDFgen 1.12
|
||||
- Switched to vcpkg as the primary dependency management system
|
||||
- Removed Visual Studio solution and Makefile - now has to be generated by CMake
|
||||
- CMake configuration overhaul, added installation configuration
|
||||
- Switched to libpng as the primary PNG file encoder
|
||||
- Added uniform grid mode (`-uniformgrid`) where atlas is laid out in a rectangular grid
|
||||
- Added options to add extra padding around glyphs (`-empadding`, `-pxpadding` and similar)
|
||||
- Added the possibility to specify asymmetrical distance range (`-aemrange`, `-apxrange`)
|
||||
- Added `-pxalign` option which governs glyph alignment with the pixel grid
|
||||
- Added `-allglyphs` option as alternative to explicit charset / glyphset
|
||||
- Added `-chars` and `-glyphs` options to specify charset / glyphset directly on command line
|
||||
- Added `-varfont` option to configure variables of variable fonts
|
||||
- Added `-version` option to print program version
|
||||
- Arguments with double dash (e.g. `--font`) now also accepted
|
||||
- Minor fix to positioning for `-type hardmask`
|
||||
- Errors are now reported to `stderr`
|
||||
- TinyXML 2 no longer required as a dependency
|
||||
|
||||
### Version 1.2.2 (2021-09-06)
|
||||
|
||||
- CMake support
|
||||
- Conan package manager support
|
||||
|
||||
### Version 1.2.1 (2021-07-09)
|
||||
|
||||
- Updated to MSDFgen 1.9.1
|
||||
|
||||
## Version 1.2 (2021-05-29)
|
||||
|
||||
- Updated to MSDFgen 1.9.
|
||||
- Multiple fonts or font sizes can now be compiled into a single atlas.
|
||||
- Added `-yorigin` option to choose if Y-coordinates increase from bottom to top or from top to bottom.
|
||||
- Added `-coloringstrategy` option to select MSDF edge coloring heuristic.
|
||||
- Shadron preview now properly loads floating-point image outputs in full range mode.
|
||||
|
||||
## Version 1.1 (2020-10-18)
|
||||
|
||||
- Updated to MSDFgen 1.8.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
include(cmake/version.cmake)
|
||||
|
||||
option(MSDF_ATLAS_BUILD_STANDALONE "Build the msdf-atlas-gen standalone executable" ON)
|
||||
option(MSDF_ATLAS_USE_VCPKG "Use vcpkg package manager to link project dependencies" ON)
|
||||
option(MSDF_ATLAS_USE_SKIA "Build with the Skia library" ON)
|
||||
option(MSDF_ATLAS_NO_ARTERY_FONT "Disable Artery Font export and do not require its submodule" OFF)
|
||||
option(MSDF_ATLAS_MSDFGEN_EXTERNAL "Do not build the msdfgen submodule but find it as an external package" OFF)
|
||||
option(MSDF_ATLAS_INSTALL "Generate installation target" OFF)
|
||||
option(MSDF_ATLAS_DYNAMIC_RUNTIME "Link dynamic runtime library instead of static" OFF)
|
||||
option(BUILD_SHARED_LIBS "Generate dynamic library files instead of static" OFF)
|
||||
|
||||
if(NOT MSDF_ATLAS_MSDFGEN_EXTERNAL)
|
||||
set(MSDFGEN_DISABLE_SVG ON CACHE BOOL "Disable unused SVG functionality to minimize dependencies")
|
||||
set(MSDFGEN_CORE_ONLY OFF CACHE INTERNAL "Only build the core msdfgen library with no dependencies (disabled for msdf-atlas-gen)" FORCE)
|
||||
set(MSDFGEN_BUILD_STANDALONE OFF CACHE BOOL "Build the msdfgen standalone executable")
|
||||
set(MSDFGEN_USE_VCPKG ${MSDF_ATLAS_USE_VCPKG} CACHE INTERNAL "Use vcpkg package manager to link msdfgen project dependencies" FORCE)
|
||||
set(MSDFGEN_USE_OPENMP OFF CACHE INTERNAL "Build with OpenMP support for multithreaded code (disabled for msdf-atlas-gen)" FORCE)
|
||||
set(MSDFGEN_USE_CPP11 ON CACHE INTERNAL "Build with C++11 enabled (always enabled for msdf-atlas-gen)" FORCE)
|
||||
set(MSDFGEN_USE_SKIA ${MSDF_ATLAS_USE_SKIA} CACHE INTERNAL "Build msdfgen with the Skia library" FORCE)
|
||||
set(MSDFGEN_INSTALL ${MSDF_ATLAS_INSTALL} CACHE INTERNAL "Generate installation target for msdfgen" FORCE)
|
||||
set(MSDFGEN_DYNAMIC_RUNTIME ${MSDF_ATLAS_DYNAMIC_RUNTIME} CACHE INTERNAL "Link dynamic runtime library instead of static for msdfgen" FORCE)
|
||||
endif()
|
||||
|
||||
get_property(MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "CMAKE_BUILD_TYPE not set, defaulting to Release")
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
if(MSDF_ATLAS_DYNAMIC_RUNTIME)
|
||||
set(MSDF_ATLAS_MSVC_RUNTIME "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(MSDF_ATLAS_MSVC_RUNTIME "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif()
|
||||
|
||||
if(MSDF_ATLAS_USE_VCPKG)
|
||||
# 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 MSDF_ATLAS_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()
|
||||
# Select project features
|
||||
if(NOT MSDF_ATLAS_VCPKG_FEATURES_SET)
|
||||
set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON)
|
||||
if(MSDF_ATLAS_USE_SKIA)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "geometry-preprocessing")
|
||||
endif()
|
||||
endif()
|
||||
set(MSDFGEN_VCPKG_FEATURES_SET ON)
|
||||
endif()
|
||||
|
||||
# Version is specified in vcpkg.json
|
||||
project(msdf-atlas-gen VERSION ${MSDF_ATLAS_VERSION} LANGUAGES CXX)
|
||||
|
||||
if(MSDF_ATLAS_MSDFGEN_EXTERNAL)
|
||||
if(NOT TARGET msdfgen::msdfgen)
|
||||
find_package(msdfgen REQUIRED)
|
||||
endif()
|
||||
else()
|
||||
add_subdirectory(msdfgen)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
if(NOT MSDFGEN_DISABLE_PNG AND NOT TARGET PNG::PNG)
|
||||
find_package(PNG REQUIRED)
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE MSDF_ATLAS_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "msdf-atlas-gen/*.h" "msdf-atlas-gen/*.hpp")
|
||||
file(GLOB_RECURSE MSDF_ATLAS_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "msdf-atlas-gen/*.cpp")
|
||||
|
||||
# msdf-atlas-gen library
|
||||
add_library(msdf-atlas-gen ${MSDF_ATLAS_HEADERS} ${MSDF_ATLAS_SOURCES})
|
||||
add_library(msdf-atlas-gen::msdf-atlas-gen ALIAS msdf-atlas-gen)
|
||||
set_target_properties(msdf-atlas-gen PROPERTIES PUBLIC_HEADER "${MSDF_ATLAS_HEADERS}")
|
||||
set_property(TARGET msdf-atlas-gen PROPERTY MSVC_RUNTIME_LIBRARY "${MSDF_ATLAS_MSVC_RUNTIME}")
|
||||
target_compile_definitions(msdf-atlas-gen PUBLIC
|
||||
MSDF_ATLAS_VERSION=${MSDF_ATLAS_VERSION}
|
||||
MSDF_ATLAS_VERSION_MAJOR=${MSDF_ATLAS_VERSION_MAJOR}
|
||||
MSDF_ATLAS_VERSION_MINOR=${MSDF_ATLAS_VERSION_MINOR}
|
||||
MSDF_ATLAS_VERSION_REVISION=${MSDF_ATLAS_VERSION_REVISION}
|
||||
MSDF_ATLAS_COPYRIGHT_YEAR=${MSDF_ATLAS_COPYRIGHT_YEAR}
|
||||
)
|
||||
target_include_directories(msdf-atlas-gen INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
)
|
||||
if(MSDF_ATLAS_NO_ARTERY_FONT)
|
||||
target_compile_definitions(msdf-atlas-gen PUBLIC MSDF_ATLAS_NO_ARTERY_FONT)
|
||||
else()
|
||||
target_include_directories(msdf-atlas-gen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/artery-font-format)
|
||||
endif()
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT msdf-atlas-gen)
|
||||
|
||||
target_compile_features(msdf-atlas-gen PUBLIC cxx_std_11)
|
||||
target_link_libraries(msdf-atlas-gen PRIVATE Threads::Threads)
|
||||
if(NOT MSDFGEN_DISABLE_PNG)
|
||||
target_link_libraries(msdf-atlas-gen PRIVATE PNG::PNG)
|
||||
endif()
|
||||
target_link_libraries(msdf-atlas-gen PUBLIC msdfgen::msdfgen)
|
||||
|
||||
if(BUILD_SHARED_LIBS AND WIN32)
|
||||
target_compile_definitions(msdf-atlas-gen PRIVATE "MSDF_ATLAS_PUBLIC=__declspec(dllexport)")
|
||||
target_compile_definitions(msdf-atlas-gen INTERFACE "MSDF_ATLAS_PUBLIC=__declspec(dllimport)")
|
||||
else()
|
||||
target_compile_definitions(msdf-atlas-gen PUBLIC MSDF_ATLAS_PUBLIC=)
|
||||
endif()
|
||||
|
||||
# msdf-atlas-gen standalone executable
|
||||
if(MSDF_ATLAS_BUILD_STANDALONE)
|
||||
set(MSDF_ATLAS_STANDALONE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/msdf-atlas-gen/main.cpp")
|
||||
if(MSVC)
|
||||
set(MSDF_ATLAS_STANDALONE_SOURCES ${MSDF_ATLAS_STANDALONE_SOURCES} "${CMAKE_CURRENT_SOURCE_DIR}/msdf-atlas-gen.rc")
|
||||
endif()
|
||||
add_executable(msdf-atlas-gen-standalone ${MSDF_ATLAS_STANDALONE_SOURCES})
|
||||
target_compile_definitions(msdf-atlas-gen-standalone PUBLIC MSDF_ATLAS_STANDALONE)
|
||||
target_compile_definitions(msdf-atlas-gen-standalone PRIVATE MSDF_ATLAS_VERSION_UNDERLINE=${MSDF_ATLAS_VERSION_UNDERLINE})
|
||||
set_property(TARGET msdf-atlas-gen-standalone PROPERTY MSVC_RUNTIME_LIBRARY "${MSDF_ATLAS_MSVC_RUNTIME}")
|
||||
set_target_properties(msdf-atlas-gen-standalone PROPERTIES
|
||||
OUTPUT_NAME msdf-atlas-gen
|
||||
ARCHIVE_OUTPUT_NAME msdf-atlas-gen-standalone
|
||||
# Avoid deleting msdf-atlas-gen.lib during clean
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
target_link_libraries(msdf-atlas-gen-standalone PRIVATE msdf-atlas-gen::msdf-atlas-gen)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT msdf-atlas-gen-standalone)
|
||||
endif()
|
||||
|
||||
# Installation
|
||||
if(MSDF_ATLAS_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
set(MSDF_ATLAS_CONFIG_PATH "lib/cmake/msdf-atlas-gen")
|
||||
|
||||
# install tree package config
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/msdf-atlas-gen-config-version.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
cmake/msdf-atlas-gen-config.cmake.in
|
||||
${MSDF_ATLAS_CONFIG_PATH}/msdf-atlas-gen-config.cmake
|
||||
INSTALL_DESTINATION ${MSDF_ATLAS_CONFIG_PATH}
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
# build tree package config
|
||||
configure_file(
|
||||
cmake/msdf-atlas-gen-config.cmake.in
|
||||
msdf-atlas-gen-config.cmake
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(TARGETS msdf-atlas-gen EXPORT msdf-atlas-gen-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/msdf-atlas-gen
|
||||
)
|
||||
if(MSVC AND BUILD_SHARED_LIBS)
|
||||
install(FILES $<TARGET_PDB_FILE:msdf-atlas-gen> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
endif()
|
||||
|
||||
export(EXPORT msdf-atlas-gen-targets NAMESPACE msdf-atlas-gen:: FILE "${CMAKE_CURRENT_BINARY_DIR}/msdf-atlas-gen-targets.cmake")
|
||||
install(EXPORT msdf-atlas-gen-targets FILE msdf-atlas-gen-targets.cmake NAMESPACE msdf-atlas-gen:: DESTINATION ${MSDF_ATLAS_CONFIG_PATH})
|
||||
|
||||
if(MSDF_ATLAS_BUILD_STANDALONE)
|
||||
install(TARGETS msdf-atlas-gen-standalone EXPORT msdf-atlas-gen-binary-targets DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
if(MSVC)
|
||||
install(FILES $<TARGET_PDB_FILE:msdf-atlas-gen-standalone> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
endif()
|
||||
export(EXPORT msdf-atlas-gen-binary-targets NAMESPACE msdf-atlas-gen-standalone:: FILE "${CMAKE_CURRENT_BINARY_DIR}/msdf-atlas-gen-binary-targets.cmake")
|
||||
install(EXPORT msdf-atlas-gen-binary-targets FILE msdf-atlas-gen-binary-targets.cmake NAMESPACE msdf-atlas-gen-standalone:: DESTINATION ${MSDF_ATLAS_CONFIG_PATH})
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${MSDF_ATLAS_CONFIG_PATH}/msdf-atlas-gen-config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/msdf-atlas-gen-config-version.cmake"
|
||||
DESTINATION ${MSDF_ATLAS_CONFIG_PATH}
|
||||
)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
{
|
||||
"version": 4,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 23,
|
||||
"patch": 0
|
||||
},
|
||||
"include": [
|
||||
"cmake/CMakePresets.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "win64",
|
||||
"displayName": "Windows x64 default config (static, vcpkg, Skia)",
|
||||
"inherits": [ "win64-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "win32",
|
||||
"displayName": "Windows x86 default config (static, vcpkg, Skia)",
|
||||
"inherits": [ "win32-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "win64-dynamic",
|
||||
"displayName": "Windows x64 dynamic config (vcpkg, Skia)",
|
||||
"inherits": [ "win64-base", "vcpkg", "skia", "dynamic-runtime", "dynamic-lib" ],
|
||||
"binaryDir": "${sourceDir}/build/win64-dynamic"
|
||||
}, {
|
||||
"name": "win32-dynamic",
|
||||
"displayName": "Windows x86 dynamic config (vcpkg, Skia)",
|
||||
"inherits": [ "win32-base", "vcpkg", "skia", "dynamic-runtime", "dynamic-lib" ],
|
||||
"binaryDir": "${sourceDir}/build/win32-dynamic"
|
||||
}, {
|
||||
"name": "win64-no-skia",
|
||||
"displayName": "Windows x64 config without Skia (static, vcpkg)",
|
||||
"inherits": [ "win64-base", "vcpkg", "no-skia" ]
|
||||
}, {
|
||||
"name": "win32-no-skia",
|
||||
"displayName": "Windows x86 config without Skia (static, vcpkg)",
|
||||
"inherits": [ "win32-base", "vcpkg", "no-skia" ]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "osx-vcpkg-rel",
|
||||
"displayName": "Mac OS release config with vcpkg and Skia (static)",
|
||||
"inherits": [ "osx-rel-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "osx-vcpkg-dbg",
|
||||
"displayName": "Mac OS debug config with vcpkg and Skia (static)",
|
||||
"inherits": [ "osx-dbg-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "osx-no-skia-rel",
|
||||
"displayName": "Mac OS release config with system libraries and no Skia (static, install)",
|
||||
"inherits": [ "osx-rel-base", "no-vcpkg", "no-skia", "install" ]
|
||||
}, {
|
||||
"name": "osx-no-skia-dbg",
|
||||
"displayName": "Mac OS debug config with system libraries and no Skia (static, install)",
|
||||
"inherits": [ "osx-dbg-base", "no-vcpkg", "no-skia", "install" ]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "linux-vcpkg-rel",
|
||||
"displayName": "Linux release config with vcpkg and Skia (static)",
|
||||
"inherits": [ "linux-rel-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "linux-vcpkg-dbg",
|
||||
"displayName": "Linux debug config with vcpkg and Skia (static)",
|
||||
"inherits": [ "linux-dbg-base", "vcpkg", "skia" ]
|
||||
}, {
|
||||
"name": "linux-no-skia-rel",
|
||||
"displayName": "Linux release config with system libraries and no Skia (static, install)",
|
||||
"inherits": [ "linux-rel-base", "no-vcpkg", "no-skia", "install" ]
|
||||
}, {
|
||||
"name": "linux-no-skia-dbg",
|
||||
"displayName": "Linux debug config with system libraries and no Skia (static, install)",
|
||||
"inherits": [ "linux-dbg-base", "no-vcpkg", "no-skia", "install" ]
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "win64-rel",
|
||||
"configurePreset": "win64",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win64-dbg",
|
||||
"configurePreset": "win64",
|
||||
"configuration": "Debug"
|
||||
}, {
|
||||
"name": "win32-rel",
|
||||
"configurePreset": "win32",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win32-dbg",
|
||||
"configurePreset": "win32",
|
||||
"configuration": "Debug"
|
||||
}, {
|
||||
"name": "win64-dynamic-rel",
|
||||
"configurePreset": "win64-dynamic",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win64-dynamic-dbg",
|
||||
"configurePreset": "win64-dynamic",
|
||||
"configuration": "Debug"
|
||||
}, {
|
||||
"name": "win32-dynamic-rel",
|
||||
"configurePreset": "win32-dynamic",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win32-dynamic-dbg",
|
||||
"configurePreset": "win32-dynamic",
|
||||
"configuration": "Debug"
|
||||
}, {
|
||||
"name": "win64-no-skia-rel",
|
||||
"configurePreset": "win64-no-skia",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win64-no-skia-dbg",
|
||||
"configurePreset": "win64-no-skia",
|
||||
"configuration": "Debug"
|
||||
}, {
|
||||
"name": "win32-no-skia-rel",
|
||||
"configurePreset": "win32-no-skia",
|
||||
"configuration": "Release"
|
||||
}, {
|
||||
"name": "win32-no-skia-dbg",
|
||||
"configurePreset": "win32-no-skia",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "osx-vcpkg-rel",
|
||||
"configurePreset": "osx-vcpkg-rel"
|
||||
}, {
|
||||
"name": "osx-vcpkg-dbg",
|
||||
"configurePreset": "osx-vcpkg-dbg"
|
||||
}, {
|
||||
"name": "osx-no-skia-rel",
|
||||
"configurePreset": "osx-no-skia-rel"
|
||||
}, {
|
||||
"name": "osx-no-skia-dbg",
|
||||
"configurePreset": "osx-no-skia-dbg"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "linux-vcpkg-rel",
|
||||
"configurePreset": "linux-vcpkg-rel"
|
||||
}, {
|
||||
"name": "linux-vcpkg-dbg",
|
||||
"configurePreset": "linux-vcpkg-dbg"
|
||||
}, {
|
||||
"name": "linux-no-skia-rel",
|
||||
"configurePreset": "linux-no-skia-rel"
|
||||
}, {
|
||||
"name": "linux-no-skia-dbg",
|
||||
"configurePreset": "linux-no-skia-dbg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Viktor Chlumsky
|
||||
Copyright (c) 2020 - 2025 Viktor Chlumsky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
all:
|
||||
mkdir -p bin
|
||||
g++ -I /usr/local/include/freetype2 -I /usr/include/freetype2 -I artery-font-format -I msdfgen/include -I msdfgen -D MSDFGEN_USE_CPP11 -D MSDF_ATLAS_STANDALONE -std=c++11 -pthread -O2 -o bin/msdf-atlas-gen msdfgen/core/*.cpp msdfgen/lib/*.cpp msdfgen/ext/*.cpp msdf-atlas-gen/*.cpp -lfreetype
|
||||
199
README.md
199
README.md
|
|
@ -5,7 +5,7 @@ This is a utility for generating compact font atlases using [MSDFgen](https://gi
|
|||
|
||||
The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, a plain image file, a CSV sheet or a structured JSON file.
|
||||
|
||||

|
||||

|
||||
|
||||
A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games.
|
||||
|
||||
|
|
@ -27,14 +27,17 @@ The atlas generator can generate the following six types of atlases.
|
|||
|
||||
Notes:
|
||||
- *Sharp corners* refers to preservation of corner sharpness when upscaled.
|
||||
- *Soft effects* refers to the support of effects that use true distance, such as glows, rounded borders, or simplified shadows.
|
||||
- *Hard effects* refers to the support of effects that use pseudo-distance, such as mitered borders or thickness adjustment.
|
||||
- *Soft effects* refers to the support of effects that use true distance, such as glows, rounded outlines, or simplified shadows.
|
||||
- *Hard effects* refers to the support of effects that use perpendicular distance, such as mitered outlines or thickness adjustment.
|
||||
|
||||
## Getting started
|
||||
|
||||
This project can be used either as a library or as a standalone console program.
|
||||
To start using the program immediately, there is a Windows binary available for download in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases).
|
||||
To build the project, you may use the included [Visual Studio solution](msdf-atlas-gen.sln) or the [Unix Makefile](Makefile).
|
||||
Examples of how to use it as a library are available at the [bottom of the page](#library-usage-examples).
|
||||
To start using the program right away, you may download a Windows binary in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases).
|
||||
To build the project from source, you may use the included [CMake script](CMakeLists.txt).
|
||||
In its default configuration, it requires [vcpkg](https://vcpkg.io/) as the provider for third-party library dependencies.
|
||||
If you set the environment variable `VCPKG_ROOT` to the vcpkg directory, the CMake configuration will take care of fetching all required packages from vcpkg.
|
||||
|
||||
## Command line arguments
|
||||
|
||||
|
|
@ -42,9 +45,17 @@ Use the following command line arguments for the standalone version of the atlas
|
|||
|
||||
### Input
|
||||
|
||||
- `-font <fontfile.ttf/otf>` – sets the input font file.
|
||||
- `-charset <charset.txt>` – sets the character set. The ASCII charset will be used if not specified. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`.
|
||||
- `-font <fontfile.ttf/otf>` (required) – sets the input font file.
|
||||
- Alternatively, use `-varfont <fontfile.ttf/otf?var0=value0&var1=value1>` to configure a variable font.
|
||||
- `-charset <charset.txt>` – sets the character set. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`.
|
||||
- `-glyphset <glyphset.txt>` – sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification).
|
||||
- `-chars` / `-glyphs <set string>` sets the above character / glyph set in-line. See [the syntax specification](#character-set-specification-syntax).
|
||||
- `-allglyphs` – sets the set of input glyphs to all glyphs present within the font file.
|
||||
- `-fontscale <scale>` – applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use [`-size`](#glyph-configuration).
|
||||
- `-fontname <name>` – sets a name for the font that will be stored in certain output files as metadata.
|
||||
- `-and` – separates multiple inputs to be combined into a single atlas.
|
||||
|
||||
If no character set or glyph set is provided, and `-allglyphs` is not used, the ASCII charset will be used.
|
||||
|
||||
### Bitmap atlas type
|
||||
|
||||
|
|
@ -55,7 +66,7 @@ Use the following command line arguments for the standalone version of the atlas
|
|||
- `hardmask` – a non-anti-aliased binary image
|
||||
- `softmask` – an anti-aliased image
|
||||
- `sdf` – a true signed distance field (SDF)
|
||||
- `psdf` – a pseudo-distance field
|
||||
- `psdf` – a signed perpendicular distance field (PSDF)
|
||||
- `msdf` (default) – a multi-channel signed distance field (MSDF)
|
||||
- `mtsdf` – a combination of MSDF and true SDF in the alpha channel
|
||||
|
||||
|
|
@ -68,50 +79,91 @@ Use the following command line arguments for the standalone version of the atlas
|
|||
- `png` – a compressed PNG image
|
||||
- `bmp` – an uncompressed BMP image
|
||||
- `tiff` – an uncompressed floating-point TIFF image
|
||||
- `rgba` – an uncompressed [RGBA](https://github.com/bzotto/rgba_bitmap) file
|
||||
- `fl32` – an uncompressed floating-point FL32 file
|
||||
- `text` – a sequence of pixel values in plain text
|
||||
- `textfloat` – a sequence of floating-point pixel values in plain text
|
||||
- `bin` – a sequence of pixel values encoded as raw bytes of data
|
||||
- `binfloat` – a sequence of pixel values encoded as raw 32-bit floating-point values
|
||||
- `binfloat` – a sequence of pixel values encoded as raw 32-bit floating-point values (little endian, `binfloatbe` for big endian)
|
||||
|
||||
If format is not specified, it may be deduced from the extension of the `-imageout` argument or other clues.
|
||||
|
||||
Please note that all color values must be interpreted as if they were linear (not sRGB) like the alpha channel, even if the image format implies otherwise.
|
||||
|
||||
### Atlas dimensions
|
||||
|
||||
`-dimensions <width> <height>` – sets fixed atlas dimensions
|
||||
|
||||
Alternativelly, the minimum possible dimensions may be selected automatically if a dimensions constraint is set instead:
|
||||
Alternatively, the minimum possible dimensions may be selected automatically if a dimensions constraint is set instead:
|
||||
|
||||
- `-pots` – a power-of-two square
|
||||
- `-potr` – a power-of-two square or rectangle (2:1)
|
||||
- `-potr` – a power-of-two square or rectangle (typically 2:1 aspect ratio)
|
||||
- `-square` – any square dimensions
|
||||
- `-square2` – square with even side length
|
||||
- `-square4` (default) – square with side length divisible by four
|
||||
|
||||
### Uniform grid atlas
|
||||
|
||||
By default, glyphs in the atlas have different dimensions and are bin-packed in an irregular fashion to maximize use of space.
|
||||
With the `-uniformgrid` switch, you can instead force all glyphs to have identical dimensions and be laid out in a grid.
|
||||
In that case, these additional options are available to customize the layout:
|
||||
|
||||
- `-uniformcols <N>` – sets the number of columns
|
||||
- `-uniformcell <width> <height>` – sets the dimensions of the grid's cells
|
||||
- `-uniformcellconstraint <none / pots / potr / square / square2 / square4>` – sets constraint for cell dimensions (see explanation of options above)
|
||||
- `-uniformorigin <off / on / horizontal / vertical>` – sets whether the glyph's origin point should be fixed at the same position in each cell
|
||||
|
||||
### Outputs
|
||||
|
||||
Any subset of the following may be specified:
|
||||
Any non-empty subset of the following may be specified:
|
||||
|
||||
- `-imageout <filename.*>` – saves the atlas bitmap as a plain image file. Format matches `-format`
|
||||
- `-json <filename.json>` – writes the atlas's layout data as well as other metrics into a structured JSON file
|
||||
- `-csv <filename.csv>` – writes the glyph layout data into a simple CSV file
|
||||
- `-json <filename.json>` – writes the atlas's layout data as well as other metrics into a structured JSON file <details><summary>JSON fields</summary>
|
||||
- `atlas` section includes the settings used to generate the atlas, including its type and dimensions. The `size` field represents the font size in pixels per em.
|
||||
- If there are multiple input fonts (`-and` parameter), the remaining data are grouped into `variants`, each representing an input font.
|
||||
- `metrics` section contains useful font metric values retrieved from the font. All values are in em's.
|
||||
- `glyphs` is an array of individual glyphs identified by Unicode character index (`unicode`) or glyph index (`index`), depending on whether character set or glyph set mode is used.
|
||||
- `advance` is the horizontal advance in em's.
|
||||
- `planeBounds` represents the glyph quad's bounds in em's relative to the baseline and horizontal cursor position.
|
||||
- `atlasBounds` represents the glyph's bounds in the atlas in pixels.
|
||||
- If available, `kerning` lists all kerning pairs and their advance adjustment (which needs to be added to the base advance of the first glyph in the pair).
|
||||
</details>
|
||||
- `-csv <filename.csv>` – writes the glyph layout data into a simple CSV file <details><summary>CSV columns</summary>
|
||||
- If there are multiple input fonts (`-and` parameter), the first column is the font index, otherwise it is skipped.
|
||||
- Character Unicode value or glyph index, depending on whether character set or glyph set mode is used.
|
||||
- Horizontal advance in em's.
|
||||
- The next 4 columns are the glyph quad's bounds in em's relative to the baseline and cursor. Depending on the `-yorigin` setting, this is either *left, bottom, right, top* (bottom-up Y) or *left, top, right, bottom* (top-down Y).
|
||||
- The last 4 columns the the glyph's bounds in the atlas in pixels. Depending on the `-yorigin` setting, this is either *left, bottom, right, top* (bottom-up Y) or *left, top, right, bottom* (top-down Y).
|
||||
</details>
|
||||
- `-arfont <filename.arfont>` – saves the atlas and its layout data as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file
|
||||
- `-shadronpreview <filename.shadron> <sample text>` – generates a [Shadron script](https://www.arteryengine.com/shadron/) that uses the generated atlas to draw a sample text as a preview
|
||||
|
||||
### Glyph configuration
|
||||
|
||||
- `-size <EM size>` – sets the size of the glyphs in the atlas in pixels per EM
|
||||
- `-minsize <EM size>` – sets the minimum size. The largest possible size that fits the same atlas dimensions will be used
|
||||
- `-emrange <EM range>` – sets the distance field range in EM's
|
||||
- `-size <em size>` – sets the size of the glyphs in the atlas in pixels per em
|
||||
- `-minsize <em size>` – sets the minimum size. The largest possible size that fits the same atlas dimensions will be used
|
||||
- `-emrange <em range>` – sets the distance field range in em's
|
||||
- `-pxrange <pixel range>` (default = 2) – sets the distance field range in output pixels
|
||||
- `-aemrange` / `-apxrange <outermost distance> <innermost distance>` – sets the distance field range asymmetrically by specifying the minimum and maximum representable signed distances (outside distances are negative!)
|
||||
- `-pxalign <off / on / horizontal / vertical>` (default = vertical) – enables or disables alignment of glyph's origin point with the pixel grid
|
||||
- `-empadding` / `-pxpadding <width>` – sets additional padding within each glyph's box (in em's / pixels)
|
||||
- `-outerempadding` / `-outerpxpadding <width>` – sets additional padding around each glyph's box
|
||||
- `-aempadding` / `-apxpadding` / `-aouterempadding` / `-aouterpxpadding <left> <bottom> <right> <top>` – sets additional padding (see above) asymmetrically with a separate width value for each side
|
||||
|
||||
### Distance field generator settings
|
||||
|
||||
- `-angle <angle>` – sets the minimum angle between adjacent edges to be considered a corner. Append D for degrees (`msdf` / `mtsdf` only)
|
||||
- `-errorcorrection <threshold>` – sets the threshold used to detect and correct potential artifacts. 0 disables error correction (`msdf` / `mtsdf` only)
|
||||
- `-coloringstrategy <simple / inktrap / distance>` – selects the edge coloring heuristic (`msdf` / `mtsdf` only)
|
||||
- `-errorcorrection <mode>` – selects the error correction algorithm. Use `help` as mode for more information (`msdf` / `mtsdf` only)
|
||||
- `-miterlimit <value>` – sets the miter limit that limits the extension of each glyph's bounding box due to very sharp corners (`psdf` / `msdf` / `mtsdf` only)
|
||||
- `-overlap` – switches to distance field generator with support for overlapping contours
|
||||
- `-nopreprocess` – disables path preprocessing which resolves self-intersections and overlapping contours
|
||||
- `-scanline` – performs an additional scanline pass to fix the signs of the distances
|
||||
- `-seed <N>` – sets the initial seed for the edge coloring heuristic
|
||||
- `-threads <N>` – sets the number of threads for the parallel computation (0 = auto)
|
||||
- `-yorigin <bottom / top>` – specifies the direction of the Y-axis in output coordinates. The default is bottom-up.
|
||||
|
||||
Use `-help` for an exhaustive list of options.
|
||||
|
||||
## Character set specification syntax
|
||||
|
||||
|
|
@ -134,3 +186,114 @@ It must be written on a separate line:
|
|||
### Glyph set specification
|
||||
|
||||
The syntax of the glyph set specification is mostly the same as that of a character set, but only numeric values (decimal and hexadecimal) are allowed.
|
||||
|
||||
## Library usage examples
|
||||
|
||||
Here are commented snippets of code that demonstrate how the project can be used as a library.
|
||||
|
||||
### Generating whole atlas at once
|
||||
|
||||
```c++
|
||||
#include <msdf-atlas-gen/msdf-atlas-gen.h>
|
||||
|
||||
using namespace msdf_atlas;
|
||||
|
||||
bool generateAtlas(const char *fontFilename) {
|
||||
bool success = false;
|
||||
// Initialize instance of FreeType library
|
||||
if (msdfgen::FreetypeHandle *ft = msdfgen::initializeFreetype()) {
|
||||
// Load font file
|
||||
if (msdfgen::FontHandle *font = msdfgen::loadFont(ft, fontFilename)) {
|
||||
// Storage for glyph geometry and their coordinates in the atlas
|
||||
std::vector<GlyphGeometry> glyphs;
|
||||
// FontGeometry is a helper class that loads a set of glyphs from a single font.
|
||||
// It can also be used to get additional font metrics, kerning information, etc.
|
||||
FontGeometry fontGeometry(&glyphs);
|
||||
// Load a set of character glyphs:
|
||||
// The second argument can be ignored unless you mix different font sizes in one atlas.
|
||||
// In the last argument, you can specify a charset other than ASCII.
|
||||
// To load specific glyph indices, use loadGlyphs instead.
|
||||
fontGeometry.loadCharset(font, 1.0, Charset::ASCII);
|
||||
// Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies.
|
||||
const double maxCornerAngle = 3.0;
|
||||
for (GlyphGeometry &glyph : glyphs)
|
||||
glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
|
||||
// TightAtlasPacker class computes the layout of the atlas.
|
||||
TightAtlasPacker packer;
|
||||
// Set atlas parameters:
|
||||
// setDimensions or setDimensionsConstraint to find the best value
|
||||
packer.setDimensionsConstraint(TightAtlasPacker::DimensionsConstraint::SQUARE);
|
||||
// setScale for a fixed size or setMinimumScale to use the largest that fits
|
||||
packer.setMinimumScale(24.0);
|
||||
// setPixelRange or setUnitRange
|
||||
packer.setPixelRange(2.0);
|
||||
packer.setMiterLimit(1.0);
|
||||
// Compute atlas layout - pack glyphs
|
||||
packer.pack(glyphs.data(), glyphs.size());
|
||||
// Get final atlas dimensions
|
||||
int width = 0, height = 0;
|
||||
packer.getDimensions(width, height);
|
||||
// The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap.
|
||||
ImmediateAtlasGenerator<
|
||||
float, // pixel type of buffer for individual glyphs depends on generator function
|
||||
3, // number of atlas color channels
|
||||
msdfGenerator, // function to generate bitmaps for individual glyphs
|
||||
BitmapAtlasStorage<byte, 3> // class that stores the atlas bitmap
|
||||
// For example, a custom atlas storage class that stores it in VRAM can be used.
|
||||
> generator(width, height);
|
||||
// GeneratorAttributes can be modified to change the generator's default settings.
|
||||
GeneratorAttributes attributes;
|
||||
generator.setAttributes(attributes);
|
||||
generator.setThreadCount(4);
|
||||
// Generate atlas bitmap
|
||||
generator.generate(glyphs.data(), glyphs.size());
|
||||
// The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef.
|
||||
// The glyphs array (or fontGeometry) contains positioning data for typesetting text.
|
||||
success = my_project::submitAtlasBitmapAndLayout(generator.atlasStorage(), glyphs);
|
||||
// Cleanup
|
||||
msdfgen::destroyFont(font);
|
||||
}
|
||||
msdfgen::deinitializeFreetype(ft);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic atlas
|
||||
|
||||
The `DynamicAtlas` class allows you to add glyphs to the atlas "on-the-fly" as they are needed. In this example, the `ImmediateAtlasGenerator` is used as the underlying atlas generator, which however isn't ideal for this purpose because it may launch new threads every time. In practice, you would typically define your own atlas generator class that properly handles your specific performance and synchronization requirements.
|
||||
|
||||
Acquiring the `GlyphGeometry` objects can be adapted from the previous example.
|
||||
|
||||
```c++
|
||||
#include <msdf-atlas-gen/msdf-atlas-gen.h>
|
||||
|
||||
using namespace msdf_atlas;
|
||||
|
||||
using MyDynamicAtlas = DynamicAtlas<ImmediateAtlasGenerator<float, 3, msdfGenerator, BitmapAtlasStorage<byte, 3>>>;
|
||||
|
||||
const double pixelRange = 2.0;
|
||||
const double glyphScale = 32.0;
|
||||
const double miterLimit = 1.0;
|
||||
const double maxCornerAngle = 3.0;
|
||||
|
||||
MyDynamicAtlas atlas;
|
||||
|
||||
void addGlyphsToAtlas(GlyphGeometry *glyphs, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
// Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies.
|
||||
glyphs[i].edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
|
||||
// Finalize glyph box size based on the parameters
|
||||
glyphs[i].wrapBox(glyphScale, pixelRange/glyphScale, miterLimit);
|
||||
}
|
||||
// Add glyphs to atlas - invokes the underlying atlas generator
|
||||
// Adding multiple glyphs at once may improve packing efficiency.
|
||||
MyDynamicAtlas::ChangeFlags change = atlas.add(glyphs, count);
|
||||
if (change&MyDynamicAtlas::RESIZED) {
|
||||
// Atlas has been enlarged - can be handled here or directly in custom generator class
|
||||
}
|
||||
// Glyph positioning data is now stored in glyphs.
|
||||
}
|
||||
```
|
||||
|
||||
The atlas storage (and its bitmap) can be accessed as `dynamicAtlas.atlasGenerator().atlasStorage()`.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 00ac3d8f964ec00a836c2bb5aeb126235ac98234
|
||||
Subproject commit af79386abe0857fe1c30be97eec760dbd84022c5
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"version": 4,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 23,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "release-only",
|
||||
"displayName": "Release only configuration",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}, {
|
||||
"name": "debug-only",
|
||||
"displayName": "Debug only configuration",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
}, {
|
||||
"name": "win64-base",
|
||||
"displayName": "Windows 64-bit base configuration",
|
||||
"hidden": true,
|
||||
"architecture": "x64",
|
||||
"binaryDir": "${sourceDir}/build/win64",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
}, {
|
||||
"name": "win32-base",
|
||||
"displayName": "Windows 32-bit base configuration",
|
||||
"hidden": true,
|
||||
"architecture": "Win32",
|
||||
"binaryDir": "${sourceDir}/build/win32",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
}, {
|
||||
"name": "osx-base",
|
||||
"displayName": "Mac OS base configuration",
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/osx",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
}
|
||||
}, {
|
||||
"name": "osx-rel-base",
|
||||
"displayName": "Mac OS base release configuration",
|
||||
"inherits": [ "osx-base", "release-only" ],
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/osx-rel"
|
||||
}, {
|
||||
"name": "osx-dbg-base",
|
||||
"displayName": "Mac OS base debug configuration",
|
||||
"inherits": [ "osx-base", "debug-only" ],
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/osx-dbg"
|
||||
}, {
|
||||
"name": "linux-base",
|
||||
"displayName": "Linux base configuration",
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/linux",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
}, {
|
||||
"name": "linux-rel-base",
|
||||
"displayName": "Linux base release configuration",
|
||||
"inherits": [ "linux-base", "release-only" ],
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/linux-rel"
|
||||
}, {
|
||||
"name": "linux-dbg-base",
|
||||
"displayName": "Linux base debug configuration",
|
||||
"inherits": [ "linux-base", "debug-only" ],
|
||||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/linux-dbg"
|
||||
}, {
|
||||
"name": "vcpkg",
|
||||
"displayName": "Configuration with vcpkg as dependency management system",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_USE_VCPKG": "ON"
|
||||
}
|
||||
}, {
|
||||
"name": "no-vcpkg",
|
||||
"displayName": "Configuration with dependencies not managed by vcpkg",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_USE_VCPKG": "OFF"
|
||||
}
|
||||
}, {
|
||||
"name": "skia",
|
||||
"displayName": "Configuration with Skia geometry preprocessing",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_USE_SKIA": "ON"
|
||||
}
|
||||
}, {
|
||||
"name": "no-skia",
|
||||
"displayName": "Configuration without Skia geometry preprocessing",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_USE_SKIA": "OFF"
|
||||
}
|
||||
}, {
|
||||
"name": "install",
|
||||
"displayName": "Configuration with installation targets",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_INSTALL": "ON"
|
||||
}
|
||||
}, {
|
||||
"name": "static-runtime",
|
||||
"displayName": "Configuration that links against the static runtime",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_DYNAMIC_RUNTIME": "OFF"
|
||||
}
|
||||
}, {
|
||||
"name": "dynamic-runtime",
|
||||
"displayName": "Configuration that links against the dynamic runtime",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"MSDF_ATLAS_DYNAMIC_RUNTIME": "ON"
|
||||
}
|
||||
}, {
|
||||
"name": "static-lib",
|
||||
"displayName": "Configuration that builds and links msdfgen statically",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"BUILD_SHARED_LIBS": "OFF"
|
||||
}
|
||||
}, {
|
||||
"name": "dynamic-lib",
|
||||
"displayName": "Configuration that builds and links msdfgen dynamically",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"BUILD_SHARED_LIBS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
set(MSDF_ATLAS_STANDALONE_AVAILABLE @MSDF_ATLAS_BUILD_STANDALONE@)
|
||||
set(MSDF_ATLAS_NO_PNG @MSDFGEN_DISABLE_PNG@)
|
||||
|
||||
if(NOT MSDF_ATLAS_NO_PNG)
|
||||
find_dependency(PNG REQUIRED)
|
||||
endif()
|
||||
find_dependency(msdfgen REQUIRED)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/msdf-atlas-gen-targets.cmake")
|
||||
|
||||
if(MSDF_ATLAS_STANDALONE_AVAILABLE)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/msdf-atlas-gen-binary-targets.cmake")
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
|
||||
set_target_properties(msdf-atlas-gen-standalone::msdf-atlas-gen-standalone PROPERTIES IMPORTED_GLOBAL TRUE)
|
||||
endif()
|
||||
add_executable(msdf-atlas-gen::msdf-atlas-gen-run ALIAS msdf-atlas-gen-standalone::msdf-atlas-gen-standalone)
|
||||
set(MSDF_ATLAS_GEN_EXECUTABLE "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/msdf-atlas-gen@CMAKE_EXECUTABLE_SUFFIX@")
|
||||
endif()
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
# This script reads version from vcpkg.json and sets it to ${MSDF_ATLAS_VERSION} etc.
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/../vcpkg.json" MSDF_ATLAS_VCPKG_JSON)
|
||||
|
||||
string(REGEX MATCH "\"version\"[ \t\n\r]*:[ \t\n\r]*\"[^\"]*\"" MSDF_ATLAS_TMP_VERSION_PAIR ${MSDF_ATLAS_VCPKG_JSON})
|
||||
string(REGEX REPLACE "\"version\"[ \t\n\r]*:[ \t\n\r]*\"([^\"]*)\"" "\\1" MSDF_ATLAS_VERSION ${MSDF_ATLAS_TMP_VERSION_PAIR})
|
||||
string(REGEX REPLACE "^([0-9]*)\\.([0-9]*)\\.([0-9]*)" "\\1" MSDF_ATLAS_VERSION_MAJOR ${MSDF_ATLAS_VERSION})
|
||||
string(REGEX REPLACE "^([0-9]*)\\.([0-9]*)\\.([0-9]*)" "\\2" MSDF_ATLAS_VERSION_MINOR ${MSDF_ATLAS_VERSION})
|
||||
string(REGEX REPLACE "^([0-9]*)\\.([0-9]*)\\.([0-9]*)" "\\3" MSDF_ATLAS_VERSION_REVISION ${MSDF_ATLAS_VERSION})
|
||||
string(LENGTH ${MSDF_ATLAS_VERSION} MSDF_ATLAS_VERSION_LENGTH)
|
||||
string(REPEAT "-" ${MSDF_ATLAS_VERSION_LENGTH} MSDF_ATLAS_VERSION_UNDERLINE)
|
||||
string(TIMESTAMP MSDF_ATLAS_COPYRIGHT_YEAR "%Y")
|
||||
|
||||
unset(MSDF_ATLAS_TMP_VERSION_PAIR)
|
||||
unset(MSDF_ATLAS_VERSION_LENGTH)
|
||||
unset(MSDF_ATLAS_VCPKG_JSON)
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,61 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Msdfgen", "msdfgen\Msdfgen.vcxproj", "{84BE2D91-F071-4151-BE12-61460464C494}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msdf-atlas-gen", "msdf-atlas-gen.vcxproj", "{223EDB94-5B35-45F2-A584-273DE6E45F6F}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{84BE2D91-F071-4151-BE12-61460464C494} = {84BE2D91-F071-4151-BE12-61460464C494}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug Library|x64 = Debug Library|x64
|
||||
Debug Library|x86 = Debug Library|x86
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release Library|x64 = Release Library|x64
|
||||
Release Library|x86 = Release Library|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug Library|x64.ActiveCfg = Debug Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug Library|x64.Build.0 = Debug Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug Library|x86.ActiveCfg = Debug Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug Library|x86.Build.0 = Debug Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug|x64.ActiveCfg = Debug Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug|x64.Build.0 = Debug Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug|x86.ActiveCfg = Debug Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Debug|x86.Build.0 = Debug Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release Library|x64.ActiveCfg = Release Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release Library|x64.Build.0 = Release Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release Library|x86.ActiveCfg = Release Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release Library|x86.Build.0 = Release Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release|x64.ActiveCfg = Release Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release|x64.Build.0 = Release Library|x64
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release|x86.ActiveCfg = Release Library|Win32
|
||||
{84BE2D91-F071-4151-BE12-61460464C494}.Release|x86.Build.0 = Release Library|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug Library|x64.ActiveCfg = Debug Library|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug Library|x64.Build.0 = Debug Library|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug Library|x86.ActiveCfg = Debug Library|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug Library|x86.Build.0 = Debug Library|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug|x64.Build.0 = Debug|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release Library|x64.ActiveCfg = Release Library|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release Library|x64.Build.0 = Release Library|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release Library|x86.ActiveCfg = Release Library|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release Library|x86.Build.0 = Release Library|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release|x64.ActiveCfg = Release|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release|x64.Build.0 = Release|x64
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{223EDB94-5B35-45F2-A584-273DE6E45F6F}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -1,372 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug Library|Win32">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug Library|x64">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|Win32">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|x64">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{223EDB94-5B35-45F2-A584-273DE6E45F6F}</ProjectGuid>
|
||||
<RootNamespace>msdfatlasgen</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>bin\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>bin\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<TargetName>msdf-atlas-gen</TargetName>
|
||||
<OutDir>$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\msdfgen\freetype\win32;$(SolutionDir)$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\debug;msdfgen\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\msdfgen\freetype\win64;$(SolutionDir)$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\debug;msdfgen\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\msdfgen\freetype\win32;$(SolutionDir)bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Lib>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\release;msdfgen\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\msdfgen\freetype\win64;$(SolutionDir)$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\release;msdfgen\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="msdf-atlas-gen\artery-font-export.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\bitmap-blit.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\charset-parser.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\Charset.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\csv-export.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\glyph-generators.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\GlyphGeometry.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\image-encode.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\json-export.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\main.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\RectanglePacker.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\shadron-preview-generator.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\size-selectors.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\TightAtlasPacker.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\utf8.cpp" />
|
||||
<ClCompile Include="msdf-atlas-gen\Workload.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\artery-font-export.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\AtlasGenerator.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\AtlasStorage.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\bitmap-blit.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\BitmapAtlasStorage.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\BitmapAtlasStorage.hpp" />
|
||||
<ClInclude Include="msdf-atlas-gen\csv-export.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.hpp" />
|
||||
<ClInclude Include="msdf-atlas-gen\glyph-generators.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\image-encode.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\Charset.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\GlyphGeometry.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\image-save.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\image-save.hpp" />
|
||||
<ClInclude Include="msdf-atlas-gen\ImmediateAtlasGenerator.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\ImmediateAtlasGenerator.hpp" />
|
||||
<ClInclude Include="msdf-atlas-gen\json-export.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\msdf-atlas-gen.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\rectangle-packing.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\rectangle-packing.hpp" />
|
||||
<ClInclude Include="msdf-atlas-gen\Rectangle.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\RectanglePacker.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\Remap.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\shadron-preview-generator.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\size-selectors.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\GlyphBox.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\types.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\utf8.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\Workload.h" />
|
||||
<ClInclude Include="msdf-atlas-gen\TightAtlasPacker.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="msdf-atlas-gen.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Template Source Files">
|
||||
<UniqueIdentifier>{ee785f45-c1cf-48ae-b864-f27237b077c1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="msdf-atlas-gen\artery-font-export.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\bitmap-blit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\csv-export.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\glyph-generators.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\GlyphGeometry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\Charset.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\charset-parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\image-encode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\json-export.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\RectanglePacker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\shadron-preview-generator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\size-selectors.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\TightAtlasPacker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\utf8.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msdf-atlas-gen\Workload.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\artery-font-export.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\AtlasGenerator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\AtlasStorage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\BitmapAtlasStorage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\BitmapAtlasStorage.hpp">
|
||||
<Filter>Template Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\bitmap-blit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\csv-export.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\DynamicAtlas.hpp">
|
||||
<Filter>Template Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\GlyphBox.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\glyph-generators.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\GlyphGeometry.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\Charset.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\image-encode.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\image-save.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\image-save.hpp">
|
||||
<Filter>Template Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\ImmediateAtlasGenerator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\ImmediateAtlasGenerator.hpp">
|
||||
<Filter>Template Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\json-export.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\msdf-atlas-gen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\Rectangle.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\RectanglePacker.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\rectangle-packing.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\rectangle-packing.hpp">
|
||||
<Filter>Template Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\Remap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\shadron-preview-generator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\size-selectors.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\TightAtlasPacker.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\types.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\utf8.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msdf-atlas-gen\Workload.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="msdf-atlas-gen.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="icon.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -31,9 +31,8 @@ public:
|
|||
|
||||
/// Configuration of signed distance field generator
|
||||
struct GeneratorAttributes {
|
||||
bool overlapSupport = false;
|
||||
msdfgen::MSDFGeneratorConfig config;
|
||||
bool scanlinePass = false;
|
||||
double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
|
||||
};
|
||||
|
||||
/// A function that generates the bitmap for a single glyph
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ BitmapAtlasStorage<T, N>::operator msdfgen::BitmapRef<T, N>() {
|
|||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::operator msdfgen::Bitmap<T, N>() && {
|
||||
return (msdfgen::Bitmap<T, N>() &&) bitmap;
|
||||
return (msdfgen::Bitmap<T, N> &&) bitmap;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
#include <set>
|
||||
#include "types.h"
|
||||
|
||||
#ifndef MSDF_ATLAS_PUBLIC
|
||||
#define MSDF_ATLAS_PUBLIC
|
||||
#endif
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represents a set of Unicode codepoints (characters)
|
||||
|
|
@ -12,7 +16,7 @@ class Charset {
|
|||
|
||||
public:
|
||||
/// The set of the 95 printable ASCII characters
|
||||
static const Charset ASCII;
|
||||
static MSDF_ATLAS_PUBLIC const Charset ASCII;
|
||||
|
||||
/// Adds a codepoint
|
||||
void add(unicode_t cp);
|
||||
|
|
@ -24,8 +28,10 @@ public:
|
|||
std::set<unicode_t>::const_iterator begin() const;
|
||||
std::set<unicode_t>::const_iterator end() const;
|
||||
|
||||
/// Load character set from a text file with the correct syntax
|
||||
/// Load character set from a text file with compliant syntax
|
||||
bool load(const char *filename, bool disableCharLiterals = false);
|
||||
/// Parse character set from a string with compliant syntax
|
||||
bool parse(const char *str, size_t strLength, bool disableCharLiterals = false);
|
||||
|
||||
private:
|
||||
std::set<unicode_t> codepoints;
|
||||
|
|
|
|||
|
|
@ -16,25 +16,34 @@ template <class AtlasGenerator>
|
|||
class DynamicAtlas {
|
||||
|
||||
public:
|
||||
enum ChangeFlag {
|
||||
NO_CHANGE = 0x00,
|
||||
RESIZED = 0x01,
|
||||
REARRANGED = 0x02
|
||||
};
|
||||
typedef int ChangeFlags;
|
||||
|
||||
DynamicAtlas();
|
||||
/// Initializes generator with dimensions and custom arguments for generator
|
||||
template <typename... ARGS>
|
||||
explicit DynamicAtlas(int minSide, ARGS... args);
|
||||
/// Creates with a configured generator. The generator must not contain any prior glyphs!
|
||||
explicit DynamicAtlas(AtlasGenerator &&generator);
|
||||
/// Adds a batch of glyphs. Adding more than one glyph at a time may improve packing efficiency
|
||||
void add(GlyphGeometry *glyphs, int count);
|
||||
ChangeFlags add(GlyphGeometry *glyphs, int count, bool allowRearrange = false);
|
||||
/// Allows access to generator. Do not add glyphs to the generator directly!
|
||||
AtlasGenerator & atlasGenerator();
|
||||
const AtlasGenerator & atlasGenerator() const;
|
||||
AtlasGenerator &atlasGenerator();
|
||||
const AtlasGenerator &atlasGenerator() const;
|
||||
|
||||
private:
|
||||
AtlasGenerator generator;
|
||||
RectanglePacker packer;
|
||||
int glyphCount;
|
||||
int side;
|
||||
int spacing;
|
||||
int glyphCount;
|
||||
int totalArea;
|
||||
RectanglePacker packer;
|
||||
AtlasGenerator generator;
|
||||
std::vector<Rectangle> rectangles;
|
||||
std::vector<Remap> remapBuffer;
|
||||
int totalArea;
|
||||
GeneratorAttributes genAttribs;
|
||||
int padding;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,64 @@
|
|||
|
||||
#include "DynamicAtlas.h"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <class AtlasGenerator>
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : glyphCount(0), side(0), totalArea(0), padding(0) { }
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : side(0), spacing(0), glyphCount(0), totalArea(0) { }
|
||||
|
||||
template <class AtlasGenerator>
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : generator((AtlasGenerator &&) generator), glyphCount(0), side(0), totalArea(0), padding(0) { }
|
||||
template <typename... ARGS>
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas(int minSide, ARGS... args) : side(minSide > 0 ? ceilToPOT(minSide) : 0), spacing(0), glyphCount(0), totalArea(0), packer(side+spacing, side+spacing), generator(side, side, args...) { }
|
||||
|
||||
template <class AtlasGenerator>
|
||||
void DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count) {
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : side(0), spacing(0), glyphCount(0), totalArea(0), generator((AtlasGenerator &&) generator) { }
|
||||
|
||||
template <class AtlasGenerator>
|
||||
typename DynamicAtlas<AtlasGenerator>::ChangeFlags DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count, bool allowRearrange) {
|
||||
ChangeFlags changeFlags = 0;
|
||||
int start = rectangles.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (!glyphs[i].isWhitespace()) {
|
||||
int w, h;
|
||||
glyphs[i].getBoxSize(w, h);
|
||||
Rectangle rect = { 0, 0, w+padding, h+padding };
|
||||
Rectangle rect = { 0, 0, w+spacing, h+spacing };
|
||||
rectangles.push_back(rect);
|
||||
Remap remapEntry = { };
|
||||
remapEntry.index = glyphCount+i;
|
||||
remapEntry.width = w;
|
||||
remapEntry.height = h;
|
||||
remapBuffer.push_back(remapEntry);
|
||||
totalArea += (w+padding)*(h+padding);
|
||||
totalArea += (w+spacing)*(h+spacing);
|
||||
}
|
||||
}
|
||||
if ((int) rectangles.size() > start) {
|
||||
int oldSide = side;
|
||||
int packerStart = start;
|
||||
while (packer.pack(rectangles.data()+packerStart, rectangles.size()-packerStart) > 0) {
|
||||
side = side+!side<<1;
|
||||
int remaining;
|
||||
while ((remaining = packer.pack(rectangles.data()+packerStart, rectangles.size()-packerStart)) > 0) {
|
||||
side = (side|!side)<<1;
|
||||
while (side*side < totalArea)
|
||||
side <<= 1;
|
||||
packer = RectanglePacker(side+padding, side+padding);
|
||||
packerStart = 0;
|
||||
if (allowRearrange) {
|
||||
packer = RectanglePacker(side+spacing, side+spacing);
|
||||
packerStart = 0;
|
||||
} else {
|
||||
packer.expand(side+spacing, side+spacing);
|
||||
packerStart = rectangles.size()-remaining;
|
||||
}
|
||||
changeFlags |= RESIZED;
|
||||
}
|
||||
if (packerStart < start) {
|
||||
for (int i = 0; i < start; ++i) {
|
||||
for (int i = packerStart; i < start; ++i) {
|
||||
Remap &remap = remapBuffer[i];
|
||||
remap.source = remap.target;
|
||||
remap.target.x = rectangles[i].x;
|
||||
remap.target.y = rectangles[i].y;
|
||||
}
|
||||
generator.rearrange(side, side, remapBuffer.data(), start);
|
||||
} else if (side != oldSide)
|
||||
changeFlags |= REARRANGED;
|
||||
} else if (changeFlags&RESIZED)
|
||||
generator.resize(side, side);
|
||||
for (int i = start; i < (int) rectangles.size(); ++i) {
|
||||
remapBuffer[i].target.x = rectangles[i].x;
|
||||
|
|
@ -52,17 +66,18 @@ void DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count) {
|
|||
glyphs[remapBuffer[i].index-glyphCount].placeBox(rectangles[i].x, rectangles[i].y);
|
||||
}
|
||||
}
|
||||
generator.generate(glyphs, count, genAttribs);
|
||||
generator.generate(glyphs, count);
|
||||
glyphCount += count;
|
||||
return changeFlags;
|
||||
}
|
||||
|
||||
template <class AtlasGenerator>
|
||||
AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() {
|
||||
AtlasGenerator &DynamicAtlas<AtlasGenerator>::atlasGenerator() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
template <class AtlasGenerator>
|
||||
const AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() const {
|
||||
const AtlasGenerator &DynamicAtlas<AtlasGenerator>::atlasGenerator() const {
|
||||
return generator;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
|
||||
#include "FontGeometry.h"
|
||||
|
||||
#define DEFAULT_FONT_UNITS_PER_EM 2048.0
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
FontGeometry::GlyphRange::GlyphRange() : glyphs(), rangeStart(), rangeEnd() { }
|
||||
|
||||
FontGeometry::GlyphRange::GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd) : glyphs(glyphs), rangeStart(rangeStart), rangeEnd(rangeEnd) { }
|
||||
|
||||
size_t FontGeometry::GlyphRange::size() const {
|
||||
return glyphs->size();
|
||||
}
|
||||
|
||||
bool FontGeometry::GlyphRange::empty() const {
|
||||
return glyphs->empty();
|
||||
}
|
||||
|
||||
const GlyphGeometry *FontGeometry::GlyphRange::begin() const {
|
||||
return glyphs->data()+rangeStart;
|
||||
}
|
||||
|
||||
const GlyphGeometry *FontGeometry::GlyphRange::end() const {
|
||||
return glyphs->data()+rangeEnd;
|
||||
}
|
||||
|
||||
FontGeometry::FontGeometry() : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(&ownGlyphs), rangeStart(0), rangeEnd(0) { }
|
||||
|
||||
FontGeometry::FontGeometry(std::vector<GlyphGeometry> *glyphStorage) : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT) {
|
||||
glyphs = glyphStorage ? glyphStorage : &ownGlyphs;
|
||||
rangeStart = glyphs->size();
|
||||
rangeEnd = glyphs->size();
|
||||
}
|
||||
|
||||
FontGeometry::FontGeometry(FontGeometry &&orig) : geometryScale(orig.geometryScale), metrics(orig.metrics), preferredIdentifierType(orig.preferredIdentifierType), glyphs(orig.glyphs), rangeStart(orig.rangeStart), rangeEnd(orig.rangeEnd), glyphsByIndex((std::map<int, size_t> &&) orig.glyphsByIndex), glyphsByCodepoint((std::map<unicode_t, size_t> &&) orig.glyphsByCodepoint), kerning((std::map<std::pair<int, int>, double> &&) orig.kerning), ownGlyphs((std::vector<GlyphGeometry> &&) orig.ownGlyphs), name((std::string &&) orig.name) {
|
||||
if (glyphs == &orig.ownGlyphs)
|
||||
glyphs = &ownGlyphs;
|
||||
}
|
||||
|
||||
FontGeometry &FontGeometry::operator=(FontGeometry &&orig) {
|
||||
if (this != &orig) {
|
||||
geometryScale = orig.geometryScale;
|
||||
metrics = orig.metrics;
|
||||
glyphs = orig.glyphs == &orig.ownGlyphs ? &ownGlyphs : orig.glyphs;
|
||||
rangeStart = orig.rangeStart;
|
||||
rangeEnd = orig.rangeEnd;
|
||||
glyphsByIndex = (std::map<int, size_t> &&) orig.glyphsByIndex;
|
||||
glyphsByCodepoint = (std::map<unicode_t, size_t> &&) orig.glyphsByCodepoint;
|
||||
kerning = (std::map<std::pair<int, int>, double> &&) orig.kerning;
|
||||
ownGlyphs = (std::vector<GlyphGeometry> &&) orig.ownGlyphs;
|
||||
name = (std::string &&) orig.name;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
int FontGeometry::loadGlyphRange(msdfgen::FontHandle *font, double fontScale, unsigned rangeStart, unsigned rangeEnd, bool preprocessGeometry, bool enableKerning) {
|
||||
if (!(glyphs->size() == this->rangeEnd && loadMetrics(font, fontScale)))
|
||||
return -1;
|
||||
glyphs->reserve(glyphs->size()+(rangeEnd-rangeStart));
|
||||
int loaded = 0;
|
||||
for (unsigned index = rangeStart; index < rangeEnd; ++index) {
|
||||
GlyphGeometry glyph;
|
||||
if (glyph.load(font, geometryScale, msdfgen::GlyphIndex(index), preprocessGeometry)) {
|
||||
addGlyph((GlyphGeometry &&) glyph);
|
||||
++loaded;
|
||||
}
|
||||
}
|
||||
if (enableKerning)
|
||||
loadKerning(font);
|
||||
preferredIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
int FontGeometry::loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry, bool enableKerning) {
|
||||
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
|
||||
return -1;
|
||||
glyphs->reserve(glyphs->size()+glyphset.size());
|
||||
int loaded = 0;
|
||||
for (unicode_t index : glyphset) {
|
||||
GlyphGeometry glyph;
|
||||
if (glyph.load(font, geometryScale, msdfgen::GlyphIndex(index), preprocessGeometry)) {
|
||||
addGlyph((GlyphGeometry &&) glyph);
|
||||
++loaded;
|
||||
}
|
||||
}
|
||||
if (enableKerning)
|
||||
loadKerning(font);
|
||||
preferredIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
int FontGeometry::loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry, bool enableKerning) {
|
||||
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
|
||||
return -1;
|
||||
glyphs->reserve(glyphs->size()+charset.size());
|
||||
int loaded = 0;
|
||||
for (unicode_t cp : charset) {
|
||||
GlyphGeometry glyph;
|
||||
if (glyph.load(font, geometryScale, cp, preprocessGeometry)) {
|
||||
addGlyph((GlyphGeometry &&) glyph);
|
||||
++loaded;
|
||||
}
|
||||
}
|
||||
if (enableKerning)
|
||||
loadKerning(font);
|
||||
preferredIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool FontGeometry::loadMetrics(msdfgen::FontHandle *font, double fontScale) {
|
||||
if (!msdfgen::getFontMetrics(metrics, font, msdfgen::FONT_SCALING_NONE))
|
||||
return false;
|
||||
if (metrics.emSize <= 0)
|
||||
metrics.emSize = DEFAULT_FONT_UNITS_PER_EM;
|
||||
geometryScale = fontScale/metrics.emSize;
|
||||
metrics.emSize *= geometryScale;
|
||||
metrics.ascenderY *= geometryScale;
|
||||
metrics.descenderY *= geometryScale;
|
||||
metrics.lineHeight *= geometryScale;
|
||||
metrics.underlineY *= geometryScale;
|
||||
metrics.underlineThickness *= geometryScale;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontGeometry::addGlyph(const GlyphGeometry &glyph) {
|
||||
if (glyphs->size() != rangeEnd)
|
||||
return false;
|
||||
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
|
||||
if (glyph.getCodepoint())
|
||||
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
|
||||
glyphs->push_back(glyph);
|
||||
++rangeEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontGeometry::addGlyph(GlyphGeometry &&glyph) {
|
||||
if (glyphs->size() != rangeEnd)
|
||||
return false;
|
||||
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
|
||||
if (glyph.getCodepoint())
|
||||
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
|
||||
glyphs->push_back((GlyphGeometry &&) glyph);
|
||||
++rangeEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
int FontGeometry::loadKerning(msdfgen::FontHandle *font) {
|
||||
int loaded = 0;
|
||||
for (size_t i = rangeStart; i < rangeEnd; ++i)
|
||||
for (size_t j = rangeStart; j < rangeEnd; ++j) {
|
||||
double advance;
|
||||
if (msdfgen::getKerning(advance, font, (*glyphs)[i].getGlyphIndex(), (*glyphs)[j].getGlyphIndex(), msdfgen::FONT_SCALING_NONE) && advance) {
|
||||
kerning[std::make_pair<int, int>((*glyphs)[i].getIndex(), (*glyphs)[j].getIndex())] = geometryScale*advance;
|
||||
++loaded;
|
||||
}
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void FontGeometry::setName(const char *name) {
|
||||
if (name)
|
||||
this->name = name;
|
||||
else
|
||||
this->name.clear();
|
||||
}
|
||||
|
||||
double FontGeometry::getGeometryScale() const {
|
||||
return geometryScale;
|
||||
}
|
||||
|
||||
const msdfgen::FontMetrics &FontGeometry::getMetrics() const {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
GlyphIdentifierType FontGeometry::getPreferredIdentifierType() const {
|
||||
return preferredIdentifierType;
|
||||
}
|
||||
|
||||
FontGeometry::GlyphRange FontGeometry::getGlyphs() const {
|
||||
return GlyphRange(glyphs, rangeStart, rangeEnd);
|
||||
}
|
||||
|
||||
const GlyphGeometry *FontGeometry::getGlyph(msdfgen::GlyphIndex index) const {
|
||||
std::map<int, size_t>::const_iterator it = glyphsByIndex.find(index.getIndex());
|
||||
if (it != glyphsByIndex.end())
|
||||
return &(*glyphs)[it->second];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GlyphGeometry *FontGeometry::getGlyph(unicode_t codepoint) const {
|
||||
std::map<unicode_t, size_t>::const_iterator it = glyphsByCodepoint.find(codepoint);
|
||||
if (it != glyphsByCodepoint.end())
|
||||
return &(*glyphs)[it->second];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FontGeometry::getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const {
|
||||
const GlyphGeometry *glyph1 = getGlyph(index1);
|
||||
if (!glyph1)
|
||||
return false;
|
||||
advance = glyph1->getAdvance();
|
||||
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(index1.getIndex(), index2.getIndex()));
|
||||
if (it != kerning.end())
|
||||
advance += it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontGeometry::getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const {
|
||||
const GlyphGeometry *glyph1, *glyph2;
|
||||
if (!((glyph1 = getGlyph(codepoint1)) && (glyph2 = getGlyph(codepoint2))))
|
||||
return false;
|
||||
advance = glyph1->getAdvance();
|
||||
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(glyph1->getIndex(), glyph2->getIndex()));
|
||||
if (it != kerning.end())
|
||||
advance += it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::map<std::pair<int, int>, double> &FontGeometry::getKerning() const {
|
||||
return kerning;
|
||||
}
|
||||
|
||||
const char *FontGeometry::getName() const {
|
||||
if (name.empty())
|
||||
return nullptr;
|
||||
return name.c_str();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "Charset.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represents the geometry of all glyphs of a given font or font variant
|
||||
class FontGeometry {
|
||||
|
||||
public:
|
||||
class GlyphRange {
|
||||
public:
|
||||
GlyphRange();
|
||||
GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd);
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
const GlyphGeometry *begin() const;
|
||||
const GlyphGeometry *end() const;
|
||||
private:
|
||||
const std::vector<GlyphGeometry> *glyphs;
|
||||
size_t rangeStart, rangeEnd;
|
||||
};
|
||||
|
||||
FontGeometry();
|
||||
explicit FontGeometry(std::vector<GlyphGeometry> *glyphStorage);
|
||||
FontGeometry(FontGeometry &&orig);
|
||||
FontGeometry &operator=(FontGeometry &&orig);
|
||||
|
||||
/// Loads the consecutive range of glyphs between rangeStart (inclusive) and rangeEnd (exclusive), returns the number of successfully loaded glyphs
|
||||
int loadGlyphRange(msdfgen::FontHandle *font, double fontScale, unsigned rangeStart, unsigned rangeEnd, bool preprocessGeometry = true, bool enableKerning = true);
|
||||
/// Loads all glyphs in a glyphset (Charset elements are glyph indices), returns the number of successfully loaded glyphs
|
||||
int loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry = true, bool enableKerning = true);
|
||||
/// Loads all glyphs in a charset (Charset elements are Unicode codepoints), returns the number of successfully loaded glyphs
|
||||
int loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry = true, bool enableKerning = true);
|
||||
|
||||
/// Only loads font metrics and geometry scale from font
|
||||
bool loadMetrics(msdfgen::FontHandle *font, double fontScale);
|
||||
/// Adds a loaded glyph
|
||||
bool addGlyph(const GlyphGeometry &glyph);
|
||||
bool addGlyph(GlyphGeometry &&glyph);
|
||||
/// Loads kerning pairs for all glyphs that are currently present, returns the number of loaded kerning pairs
|
||||
int loadKerning(msdfgen::FontHandle *font);
|
||||
/// Sets a name to be associated with the font
|
||||
void setName(const char *name);
|
||||
|
||||
/// Returns the geometry scale to be used when loading glyphs
|
||||
double getGeometryScale() const;
|
||||
/// Returns the processed font metrics
|
||||
const msdfgen::FontMetrics &getMetrics() const;
|
||||
/// Returns the type of identifier that was used to load glyphs
|
||||
GlyphIdentifierType getPreferredIdentifierType() const;
|
||||
/// Returns the list of all glyphs
|
||||
GlyphRange getGlyphs() const;
|
||||
/// Finds a glyph by glyph index or Unicode codepoint, returns null if not found
|
||||
const GlyphGeometry *getGlyph(msdfgen::GlyphIndex index) const;
|
||||
const GlyphGeometry *getGlyph(unicode_t codepoint) const;
|
||||
/// Outputs the advance between two glyphs with kerning taken into consideration, returns false on failure
|
||||
bool getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const;
|
||||
bool getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const;
|
||||
/// Returns the complete mapping of kerning pairs (by glyph indices) and their respective advance values
|
||||
const std::map<std::pair<int, int>, double> &getKerning() const;
|
||||
/// Returns the name associated with the font or null if not set
|
||||
const char *getName() const;
|
||||
|
||||
private:
|
||||
double geometryScale;
|
||||
msdfgen::FontMetrics metrics;
|
||||
GlyphIdentifierType preferredIdentifierType;
|
||||
std::vector<GlyphGeometry> *glyphs;
|
||||
size_t rangeStart, rangeEnd;
|
||||
std::map<int, size_t> glyphsByIndex;
|
||||
std::map<unicode_t, size_t> glyphsByCodepoint;
|
||||
std::map<std::pair<int, int>, double> kerning;
|
||||
std::vector<GlyphGeometry> ownGlyphs;
|
||||
std::string name;
|
||||
|
||||
FontGeometry(const FontGeometry &);
|
||||
FontGeometry &operator=(const FontGeometry &);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Rectangle.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// The glyph box - its bounds in plane and atlas
|
||||
|
|
@ -10,9 +12,7 @@ struct GlyphBox {
|
|||
struct {
|
||||
double l, b, r, t;
|
||||
} bounds;
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
} rect;
|
||||
Rectangle rect;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
|
||||
namespace msdf_atlas {
|
||||
|
||||
GlyphGeometry::GlyphGeometry() : index(), codepoint(), bounds(), advance(), box() { }
|
||||
GlyphGeometry::GlyphGeometry() : index(), codepoint(), geometryScale(), bounds(), advance(), box() { }
|
||||
|
||||
bool GlyphGeometry::load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry) {
|
||||
if (font && msdfgen::loadGlyph(shape, font, index, &advance) && shape.validate()) {
|
||||
bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry) {
|
||||
if (font && msdfgen::loadGlyph(shape, font, index, msdfgen::FONT_SCALING_NONE, &advance) && shape.validate()) {
|
||||
this->index = index.getIndex();
|
||||
this->geometryScale = geometryScale;
|
||||
codepoint = 0;
|
||||
advance *= geometryScale;
|
||||
#ifdef MSDFGEN_USE_SKIA
|
||||
if (preprocessGeometry)
|
||||
msdfgen::resolveShapeGeometry(shape);
|
||||
|
|
@ -34,10 +36,10 @@ bool GlyphGeometry::load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, b
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry) {
|
||||
bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry) {
|
||||
msdfgen::GlyphIndex index;
|
||||
if (msdfgen::getGlyphIndex(index, font, codepoint)) {
|
||||
if (load(font, index, preprocessGeometry)) {
|
||||
if (load(font, geometryScale, index, preprocessGeometry)) {
|
||||
this->codepoint = codepoint;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -45,35 +47,142 @@ bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint, bool pr
|
|||
return false;
|
||||
}
|
||||
|
||||
void GlyphGeometry::edgeColoring(double angleThreshold, unsigned long long seed) {
|
||||
msdfgen::edgeColoringInkTrap(shape, angleThreshold, seed);
|
||||
void GlyphGeometry::edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed) {
|
||||
fn(shape, angleThreshold, seed);
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
|
||||
void GlyphGeometry::wrapBox(const GlyphAttributes &glyphAttributes) {
|
||||
double scale = glyphAttributes.scale*geometryScale;
|
||||
msdfgen::Range range = glyphAttributes.range/geometryScale;
|
||||
Padding fullPadding = (glyphAttributes.innerPadding+glyphAttributes.outerPadding)/geometryScale;
|
||||
box.range = range;
|
||||
box.scale = scale;
|
||||
if (bounds.l < bounds.r && bounds.b < bounds.t) {
|
||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||
l -= .5*range, b -= .5*range;
|
||||
r += .5*range, t += .5*range;
|
||||
if (miterLimit > 0)
|
||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
||||
double w = scale*(r-l);
|
||||
double h = scale*(t-b);
|
||||
box.rect.w = (int) ceil(w)+1;
|
||||
box.rect.h = (int) ceil(h)+1;
|
||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||
l += range.lower, b += range.lower;
|
||||
r -= range.lower, t -= range.lower;
|
||||
if (glyphAttributes.miterLimit > 0)
|
||||
shape.boundMiters(l, b, r, t, -range.lower, glyphAttributes.miterLimit, 1);
|
||||
l -= fullPadding.l, b -= fullPadding.b;
|
||||
r += fullPadding.r, t += fullPadding.t;
|
||||
if (glyphAttributes.pxAlignOriginX) {
|
||||
int sl = (int) floor(scale*l-.5);
|
||||
int sr = (int) ceil(scale*r+.5);
|
||||
box.rect.w = sr-sl;
|
||||
box.translate.x = -sl/scale;
|
||||
} else {
|
||||
double w = scale*(r-l);
|
||||
box.rect.w = (int) ceil(w)+1;
|
||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||
}
|
||||
if (glyphAttributes.pxAlignOriginY) {
|
||||
int sb = (int) floor(scale*b-.5);
|
||||
int st = (int) ceil(scale*t+.5);
|
||||
box.rect.h = st-sb;
|
||||
box.translate.y = -sb/scale;
|
||||
} else {
|
||||
double h = scale*(t-b);
|
||||
box.rect.h = (int) ceil(h)+1;
|
||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||
}
|
||||
box.outerPadding = glyphAttributes.scale*glyphAttributes.outerPadding;
|
||||
} else {
|
||||
box.rect.w = 0, box.rect.h = 0;
|
||||
box.translate = msdfgen::Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin) {
|
||||
GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = range;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOrigin;
|
||||
attribs.pxAlignOriginY = pxAlignOrigin;
|
||||
wrapBox(attribs);
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||
GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = range;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||
wrapBox(attribs);
|
||||
}
|
||||
|
||||
void GlyphGeometry::frameBox(const GlyphAttributes &glyphAttributes, int width, int height, const double *fixedX, const double *fixedY) {
|
||||
double scale = glyphAttributes.scale*geometryScale;
|
||||
msdfgen::Range range = glyphAttributes.range/geometryScale;
|
||||
Padding fullPadding = (glyphAttributes.innerPadding+glyphAttributes.outerPadding)/geometryScale;
|
||||
box.range = range;
|
||||
box.scale = scale;
|
||||
box.rect.w = width;
|
||||
box.rect.h = height;
|
||||
if (fixedX && fixedY) {
|
||||
box.translate.x = *fixedX/geometryScale;
|
||||
box.translate.y = *fixedY/geometryScale;
|
||||
} else {
|
||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
||||
l += range.lower, b += range.lower;
|
||||
r -= range.lower, t -= range.lower;
|
||||
if (glyphAttributes.miterLimit > 0)
|
||||
shape.boundMiters(l, b, r, t, -range.lower, glyphAttributes.miterLimit, 1);
|
||||
l -= fullPadding.l, b -= fullPadding.b;
|
||||
r += fullPadding.r, t += fullPadding.t;
|
||||
if (fixedX)
|
||||
box.translate.x = *fixedX/geometryScale;
|
||||
else if (glyphAttributes.pxAlignOriginX) {
|
||||
int sl = (int) floor(scale*l-.5);
|
||||
int sr = (int) ceil(scale*r+.5);
|
||||
box.translate.x = (-sl+(box.rect.w-(sr-sl))/2)/scale;
|
||||
} else {
|
||||
double w = scale*(r-l);
|
||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
||||
}
|
||||
if (fixedY)
|
||||
box.translate.y = *fixedY/geometryScale;
|
||||
else if (glyphAttributes.pxAlignOriginY) {
|
||||
int sb = (int) floor(scale*b-.5);
|
||||
int st = (int) ceil(scale*t+.5);
|
||||
box.translate.y = (-sb+(box.rect.h-(st-sb))/2)/scale;
|
||||
} else {
|
||||
double h = scale*(t-b);
|
||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
||||
}
|
||||
}
|
||||
box.outerPadding = glyphAttributes.scale*glyphAttributes.outerPadding;
|
||||
}
|
||||
|
||||
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin) {
|
||||
GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = range;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOrigin;
|
||||
attribs.pxAlignOriginY = pxAlignOrigin;
|
||||
frameBox(attribs, width, height, fixedX, fixedY);
|
||||
}
|
||||
|
||||
void GlyphGeometry::frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY) {
|
||||
GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = range;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||
frameBox(attribs, width, height, fixedX, fixedY);
|
||||
}
|
||||
|
||||
void GlyphGeometry::placeBox(int x, int y) {
|
||||
box.rect.x = x, box.rect.y = y;
|
||||
}
|
||||
|
||||
void GlyphGeometry::setBoxRect(const Rectangle &rect) {
|
||||
box.rect = rect;
|
||||
}
|
||||
|
||||
int GlyphGeometry::getIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
|
@ -96,14 +205,26 @@ int GlyphGeometry::getIdentifier(GlyphIdentifierType type) const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
const msdfgen::Shape & GlyphGeometry::getShape() const {
|
||||
double GlyphGeometry::getGeometryScale() const {
|
||||
return geometryScale;
|
||||
}
|
||||
|
||||
const msdfgen::Shape &GlyphGeometry::getShape() const {
|
||||
return shape;
|
||||
}
|
||||
|
||||
const msdfgen::Shape::Bounds &GlyphGeometry::getShapeBounds() const {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getAdvance() const {
|
||||
return advance;
|
||||
}
|
||||
|
||||
Rectangle GlyphGeometry::getBoxRect() const {
|
||||
return box.rect;
|
||||
}
|
||||
|
||||
void GlyphGeometry::getBoxRect(int &x, int &y, int &w, int &h) const {
|
||||
x = box.rect.x, y = box.rect.y;
|
||||
w = box.rect.w, h = box.rect.h;
|
||||
|
|
@ -113,10 +234,14 @@ void GlyphGeometry::getBoxSize(int &w, int &h) const {
|
|||
w = box.rect.w, h = box.rect.h;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getBoxRange() const {
|
||||
msdfgen::Range GlyphGeometry::getBoxRange() const {
|
||||
return box.range;
|
||||
}
|
||||
|
||||
msdfgen::Projection GlyphGeometry::getBoxProjection() const {
|
||||
return msdfgen::Projection(msdfgen::Vector2(box.scale), box.translate);
|
||||
}
|
||||
|
||||
double GlyphGeometry::getBoxScale() const {
|
||||
return box.scale;
|
||||
}
|
||||
|
|
@ -127,20 +252,21 @@ msdfgen::Vector2 GlyphGeometry::getBoxTranslate() const {
|
|||
|
||||
void GlyphGeometry::getQuadPlaneBounds(double &l, double &b, double &r, double &t) const {
|
||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||
l = -box.translate.x+.5/box.scale;
|
||||
b = -box.translate.y+.5/box.scale;
|
||||
r = -box.translate.x+(box.rect.w-.5)/box.scale;
|
||||
t = -box.translate.y+(box.rect.h-.5)/box.scale;
|
||||
double invBoxScale = 1/box.scale;
|
||||
l = geometryScale*(-box.translate.x+(box.outerPadding.l+.5)*invBoxScale);
|
||||
b = geometryScale*(-box.translate.y+(box.outerPadding.b+.5)*invBoxScale);
|
||||
r = geometryScale*(-box.translate.x+(-box.outerPadding.r+box.rect.w-.5)*invBoxScale);
|
||||
t = geometryScale*(-box.translate.y+(-box.outerPadding.t+box.rect.h-.5)*invBoxScale);
|
||||
} else
|
||||
l = 0, b = 0, r = 0, t = 0;
|
||||
}
|
||||
|
||||
void GlyphGeometry::getQuadAtlasBounds(double &l, double &b, double &r, double &t) const {
|
||||
if (box.rect.w > 0 && box.rect.h > 0) {
|
||||
l = box.rect.x+.5;
|
||||
b = box.rect.y+.5;
|
||||
r = box.rect.x+box.rect.w-.5;
|
||||
t = box.rect.y+box.rect.h-.5;
|
||||
l = box.rect.x+box.outerPadding.l+.5;
|
||||
b = box.rect.y+box.outerPadding.b+.5;
|
||||
r = box.rect.x-box.outerPadding.r+box.rect.w-.5;
|
||||
t = box.rect.y-box.outerPadding.t+box.rect.h-.5;
|
||||
} else
|
||||
l = 0, b = 0, r = 0, t = 0;
|
||||
}
|
||||
|
|
@ -158,4 +284,8 @@ GlyphGeometry::operator GlyphBox() const {
|
|||
return box;
|
||||
}
|
||||
|
||||
msdfgen::Range operator+(msdfgen::Range a, msdfgen::Range b) {
|
||||
return msdfgen::Range(a.lower+b.lower, a.upper+b.upper);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,24 +4,42 @@
|
|||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Padding.h"
|
||||
#include "GlyphBox.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represent's the shape geometry of a single glyph as well as its configuration
|
||||
/// Represents the shape geometry of a single glyph as well as its configuration
|
||||
class GlyphGeometry {
|
||||
|
||||
public:
|
||||
struct GlyphAttributes {
|
||||
double scale;
|
||||
msdfgen::Range range;
|
||||
Padding innerPadding, outerPadding;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
};
|
||||
|
||||
GlyphGeometry();
|
||||
/// Loads glyph geometry from font
|
||||
bool load(msdfgen::FontHandle *font, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
|
||||
bool load(msdfgen::FontHandle *font, unicode_t codepoint, bool preprocessGeometry = true);
|
||||
bool load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
|
||||
bool load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry = true);
|
||||
/// Applies edge coloring to glyph shape
|
||||
void edgeColoring(double angleThreshold, unsigned long long seed);
|
||||
void edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed);
|
||||
/// Computes the dimensions of the glyph's box as well as the transformation for the generator function
|
||||
void wrapBox(double scale, double range, double miterLimit);
|
||||
void wrapBox(const GlyphAttributes &glyphAttributes);
|
||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOrigin = false);
|
||||
void wrapBox(double scale, double range, double miterLimit, bool pxAlignOriginX, bool pxAlignOriginY);
|
||||
/// Computes the glyph's transformation and alignment (unless specified) for given dimensions
|
||||
void frameBox(const GlyphAttributes &glyphAttributes, int width, int height, const double *fixedX, const double *fixedY);
|
||||
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOrigin = false);
|
||||
void frameBox(double scale, double range, double miterLimit, int width, int height, const double *fixedX, const double *fixedY, bool pxAlignOriginX, bool pxAlignOriginY);
|
||||
/// Sets the glyph's box's position in the atlas
|
||||
void placeBox(int x, int y);
|
||||
/// Sets the glyph's box's rectangle in the atlas
|
||||
void setBoxRect(const Rectangle &rect);
|
||||
/// Returns the glyph's index within the font
|
||||
int getIndex() const;
|
||||
/// Returns the glyph's index as a msdfgen::GlyphIndex
|
||||
|
|
@ -30,16 +48,24 @@ public:
|
|||
unicode_t getCodepoint() const;
|
||||
/// Returns the glyph's identifier specified by the supplied identifier type
|
||||
int getIdentifier(GlyphIdentifierType type) const;
|
||||
/// Returns the glyph's geometry scale
|
||||
double getGeometryScale() const;
|
||||
/// Returns the glyph's shape
|
||||
const msdfgen::Shape & getShape() const;
|
||||
const msdfgen::Shape &getShape() const;
|
||||
/// Returns the glyph's shape's raw bounds
|
||||
const msdfgen::Shape::Bounds &getShapeBounds() const;
|
||||
/// Returns the glyph's advance
|
||||
double getAdvance() const;
|
||||
/// Returns the glyph's box in the atlas
|
||||
Rectangle getBoxRect() const;
|
||||
/// Outputs the position and dimensions of the glyph's box in the atlas
|
||||
void getBoxRect(int &x, int &y, int &w, int &h) const;
|
||||
/// Outputs the dimensions of the glyph's box in the atlas
|
||||
void getBoxSize(int &w, int &h) const;
|
||||
/// Returns the range needed to generate the glyph's SDF
|
||||
double getBoxRange() const;
|
||||
msdfgen::Range getBoxRange() const;
|
||||
/// Returns the projection needed to generate the glyph's bitmap
|
||||
msdfgen::Projection getBoxProjection() const;
|
||||
/// Returns the scale needed to generate the glyph's bitmap
|
||||
double getBoxScale() const;
|
||||
/// Returns the translation vector needed to generate the glyph's bitmap
|
||||
|
|
@ -56,18 +82,20 @@ public:
|
|||
private:
|
||||
int index;
|
||||
unicode_t codepoint;
|
||||
double geometryScale;
|
||||
msdfgen::Shape shape;
|
||||
msdfgen::Shape::Bounds bounds;
|
||||
double advance;
|
||||
struct {
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
} rect;
|
||||
double range;
|
||||
Rectangle rect;
|
||||
msdfgen::Range range;
|
||||
double scale;
|
||||
msdfgen::Vector2 translate;
|
||||
Padding outerPadding;
|
||||
} box;
|
||||
|
||||
};
|
||||
|
||||
msdfgen::Range operator+(msdfgen::Range a, msdfgen::Range b);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,636 @@
|
|||
|
||||
#include "GridAtlasPacker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static bool squareConstraint(DimensionsConstraint constraint) {
|
||||
switch (constraint) {
|
||||
case DimensionsConstraint::SQUARE:
|
||||
case DimensionsConstraint::EVEN_SQUARE:
|
||||
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
|
||||
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
|
||||
return true;
|
||||
case DimensionsConstraint::NONE:
|
||||
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::lowerToConstraint(int &width, int &height, DimensionsConstraint constraint) {
|
||||
if (squareConstraint(constraint))
|
||||
width = height = std::min(width, height);
|
||||
switch (constraint) {
|
||||
case DimensionsConstraint::NONE:
|
||||
case DimensionsConstraint::SQUARE:
|
||||
break;
|
||||
case DimensionsConstraint::EVEN_SQUARE:
|
||||
width &= ~1;
|
||||
height &= ~1;
|
||||
break;
|
||||
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
|
||||
width &= ~3;
|
||||
height &= ~3;
|
||||
break;
|
||||
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
|
||||
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
|
||||
if (width > 0)
|
||||
width = floorToPOT(width);
|
||||
if (height > 0)
|
||||
height = floorToPOT(height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GridAtlasPacker::raiseToConstraint(int &width, int &height, DimensionsConstraint constraint) {
|
||||
if (squareConstraint(constraint))
|
||||
width = height = std::max(width, height);
|
||||
switch (constraint) {
|
||||
case DimensionsConstraint::NONE:
|
||||
case DimensionsConstraint::SQUARE:
|
||||
break;
|
||||
case DimensionsConstraint::EVEN_SQUARE:
|
||||
width += width&1;
|
||||
height += height&1;
|
||||
break;
|
||||
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
|
||||
width += -width&3;
|
||||
height += -height&3;
|
||||
break;
|
||||
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
|
||||
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
|
||||
if (width > 0)
|
||||
width = ceilToPOT(width);
|
||||
if (height > 0)
|
||||
height = ceilToPOT(height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double GridAtlasPacker::dimensionsRating(int width, int height, bool aligned) const {
|
||||
return ((double) width*width+(double) height*height)*(aligned ? 1-alignedColumnsBias : 1);
|
||||
}
|
||||
|
||||
GridAtlasPacker::GridAtlasPacker() :
|
||||
columns(-1), rows(-1),
|
||||
width(-1), height(-1),
|
||||
cellWidth(-1), cellHeight(-1),
|
||||
spacing(0),
|
||||
dimensionsConstraint(DimensionsConstraint::NONE),
|
||||
cellDimensionsConstraint(DimensionsConstraint::NONE),
|
||||
hFixed(false), vFixed(false),
|
||||
scale(-1),
|
||||
minScale(1),
|
||||
fixedX(0), fixedY(0),
|
||||
unitRange(0),
|
||||
pxRange(0),
|
||||
miterLimit(0),
|
||||
pxAlignOriginX(false), pxAlignOriginY(false),
|
||||
scaleMaximizationTolerance(.001),
|
||||
alignedColumnsBias(.125),
|
||||
cutoff(false)
|
||||
{ }
|
||||
|
||||
msdfgen::Shape::Bounds GridAtlasPacker::getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double outerRange) const {
|
||||
static const double LARGE_VALUE = 1e240;
|
||||
msdfgen::Shape::Bounds maxBounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
double geometryScale = glyph->getGeometryScale();
|
||||
double shapeOuterRange = outerRange/geometryScale;
|
||||
geometryScale *= scale;
|
||||
const msdfgen::Shape::Bounds &shapeBounds = glyph->getShapeBounds();
|
||||
double l = shapeBounds.l, b = shapeBounds.b, r = shapeBounds.r, t = shapeBounds.t;
|
||||
l -= shapeOuterRange, b -= shapeOuterRange;
|
||||
r += shapeOuterRange, t += shapeOuterRange;
|
||||
if (miterLimit > 0)
|
||||
glyph->getShape().boundMiters(l, b, r, t, shapeOuterRange, miterLimit, 1);
|
||||
l *= geometryScale, b *= geometryScale;
|
||||
r *= geometryScale, t *= geometryScale;
|
||||
maxBounds.l = std::min(maxBounds.l, l);
|
||||
maxBounds.b = std::min(maxBounds.b, b);
|
||||
maxBounds.r = std::max(maxBounds.r, r);
|
||||
maxBounds.t = std::max(maxBounds.t, t);
|
||||
maxWidth = std::max(maxWidth, r-l);
|
||||
maxHeight = std::max(maxHeight, t-b);
|
||||
}
|
||||
}
|
||||
if (maxBounds.l >= maxBounds.r || maxBounds.b >= maxBounds.t)
|
||||
maxBounds = msdfgen::Shape::Bounds();
|
||||
Padding fullPadding = scale*(innerUnitPadding+outerUnitPadding)+innerPxPadding+outerPxPadding;
|
||||
pad(maxBounds, fullPadding);
|
||||
maxWidth += fullPadding.l+fullPadding.r;
|
||||
maxHeight += fullPadding.b+fullPadding.t;
|
||||
// If origin is pixel-aligned but not fixed, one pixel has to be added to max dimensions to allow for aligning the origin by shifting by < 1 pixel
|
||||
if (hFixed)
|
||||
maxWidth = maxBounds.r-maxBounds.l;
|
||||
else if (pxAlignOriginX)
|
||||
maxWidth += 1;
|
||||
if (vFixed)
|
||||
maxHeight = maxBounds.t-maxBounds.b;
|
||||
else if (pxAlignOriginY)
|
||||
maxHeight += 1;
|
||||
return maxBounds;
|
||||
}
|
||||
|
||||
double GridAtlasPacker::scaleToFit(GlyphGeometry *glyphs, int count, int cellWidth, int cellHeight, msdfgen::Shape::Bounds &maxBounds, double &maxWidth, double &maxHeight) const {
|
||||
static const int BIG_VALUE = 1<<28;
|
||||
if (cellWidth <= 0)
|
||||
cellWidth = BIG_VALUE;
|
||||
if (cellHeight <= 0)
|
||||
cellHeight = BIG_VALUE;
|
||||
--cellWidth, --cellHeight; // Implicit half-pixel padding from each side to make sure that no representable values are beyond outermost pixel centers
|
||||
cellWidth -= spacing, cellHeight -= spacing;
|
||||
bool lastResult = false;
|
||||
#define TRY_FIT(scale) (maxWidth = 0, maxHeight = 0, maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, (scale), -(unitRange.lower+pxRange.lower/(scale))), lastResult = maxWidth <= cellWidth && maxHeight <= cellHeight)
|
||||
double minScale = 1, maxScale = 1;
|
||||
if (TRY_FIT(1)) {
|
||||
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_FIT(maxScale)))
|
||||
minScale = maxScale;
|
||||
} else {
|
||||
while (minScale > 1e-32 && ((minScale = .5*maxScale), !TRY_FIT(minScale)))
|
||||
maxScale = minScale;
|
||||
}
|
||||
if (minScale == maxScale)
|
||||
return 0;
|
||||
while (minScale/maxScale < 1-scaleMaximizationTolerance) {
|
||||
double midScale = .5*(minScale+maxScale);
|
||||
if (TRY_FIT(midScale))
|
||||
minScale = midScale;
|
||||
else
|
||||
maxScale = midScale;
|
||||
}
|
||||
if (!lastResult)
|
||||
TRY_FIT(minScale);
|
||||
return minScale;
|
||||
}
|
||||
|
||||
// Can this spaghetti code be simplified?
|
||||
// Idea: Maybe it could be rewritten into a while (not all properties deduced) cycle, and compute one value in each iteration
|
||||
int GridAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||
if (!count)
|
||||
return 0;
|
||||
GridAtlasPacker initial(*this);
|
||||
int cellCount = 0;
|
||||
if (columns > 0 && rows > 0)
|
||||
cellCount = columns*rows;
|
||||
else {
|
||||
// Count non-whitespace glyphs only
|
||||
for (const GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace())
|
||||
++cellCount;
|
||||
}
|
||||
if (columns > 0)
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
else if (rows > 0)
|
||||
columns = (cellCount+rows-1)/rows;
|
||||
else if (width > 0 && cellWidth > 0) {
|
||||
columns = (width+spacing)/cellWidth;
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
}
|
||||
}
|
||||
|
||||
if (width < 0 && cellWidth > 0 && columns > 0)
|
||||
width = columns*cellWidth;
|
||||
if (height < 0 && cellHeight > 0 && rows > 0)
|
||||
height = rows*cellHeight;
|
||||
if (width != initial.width || height != initial.height)
|
||||
raiseToConstraint(width, height, dimensionsConstraint);
|
||||
|
||||
if (cellWidth < 0 && width > 0 && columns > 0)
|
||||
cellWidth = (width+spacing)/columns;
|
||||
if (cellHeight < 0 && height > 0 && rows > 0)
|
||||
cellHeight = (height+spacing)/rows;
|
||||
if (cellWidth != initial.cellWidth || cellHeight != initial.cellHeight) {
|
||||
bool positiveCellWidth = cellWidth > 0, positiveCellHeight = cellHeight > 0;
|
||||
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
if ((cellWidth == 0 && positiveCellWidth) || (cellHeight == 0 && positiveCellHeight))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((cellWidth > 0 && cellWidth-spacing-1 <= -2*pxRange.lower) || (cellHeight > 0 && cellHeight-spacing-1 <= -2*pxRange.lower)) // cells definitely too small
|
||||
return -1;
|
||||
|
||||
msdfgen::Shape::Bounds maxBounds = { };
|
||||
double maxWidth = 0, maxHeight = 0;
|
||||
|
||||
if (scale <= 0) {
|
||||
|
||||
// If both pxRange and miterLimit is non-zero, miter bounds have to be computed for all potential scales
|
||||
if (pxRange.lower != pxRange.upper && miterLimit > 0) {
|
||||
|
||||
if (cellWidth > 0 || cellHeight > 0) {
|
||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (scale < minScale) {
|
||||
scale = minScale;
|
||||
cutoff = true;
|
||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, -(unitRange.lower+pxRange.lower/scale));
|
||||
}
|
||||
}
|
||||
|
||||
else if (width > 0 && height > 0) {
|
||||
double bestAlignedScale = 0;
|
||||
int bestCols = 0, bestAlignedCols = 0;
|
||||
for (int q = (int) sqrt(cellCount)+1; q > 0; --q) {
|
||||
int cols = q;
|
||||
int rows = (cellCount+cols-1)/cols;
|
||||
int tWidth = (width+spacing)/cols;
|
||||
int tHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||
if (tWidth > 0 && tHeight > 0) {
|
||||
double curScale = scaleToFit(glyphs, count, tWidth, tHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (curScale > scale) {
|
||||
scale = curScale;
|
||||
bestCols = cols;
|
||||
}
|
||||
if (cols*tWidth == width && curScale > bestAlignedScale) {
|
||||
bestAlignedScale = curScale;
|
||||
bestAlignedCols = cols;
|
||||
}
|
||||
}
|
||||
cols = (cellCount+q-1)/q;
|
||||
rows = (cellCount+cols-1)/cols;
|
||||
tWidth = (width+spacing)/cols;
|
||||
tHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||
if (tWidth > 0 && tHeight > 0) {
|
||||
double curScale = scaleToFit(glyphs, count, tWidth, tHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (curScale > scale) {
|
||||
scale = curScale;
|
||||
bestCols = cols;
|
||||
}
|
||||
if (cols*tWidth == width && curScale > bestAlignedScale) {
|
||||
bestAlignedScale = curScale;
|
||||
bestAlignedCols = cols;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bestCols)
|
||||
return -1;
|
||||
// If columns can be aligned with total width at a slight cost to glyph scale, use that number of columns instead
|
||||
if (bestAlignedScale >= minScale && (alignedColumnsBias+1)*bestAlignedScale >= scale) {
|
||||
scale = bestAlignedScale;
|
||||
bestCols = bestAlignedCols;
|
||||
}
|
||||
|
||||
columns = bestCols;
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
cellWidth = (width+spacing)/columns;
|
||||
cellHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (scale < minScale)
|
||||
scale = -1;
|
||||
}
|
||||
|
||||
if (scale <= 0) {
|
||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, minScale, -(unitRange.lower+pxRange.lower/minScale));
|
||||
cellWidth = (int) ceil(maxWidth)+spacing+1;
|
||||
cellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
scale = scaleToFit(glyphs, count, cellWidth, cellHeight, maxBounds, maxWidth, maxHeight);
|
||||
if (scale < minScale)
|
||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale = minScale, -(unitRange.lower+pxRange.lower/minScale));
|
||||
}
|
||||
|
||||
if (initial.rows < 0 && initial.cellHeight < 0) {
|
||||
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
|
||||
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
|
||||
cellWidth = optimalCellWidth;
|
||||
cellHeight = optimalCellHeight;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Padding pxPadding = innerPxPadding+outerPxPadding;
|
||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, 1, -unitRange.lower);
|
||||
// Undo pxPadding added by getMaxBounds before pixel scale is known
|
||||
pad(maxBounds, -pxPadding);
|
||||
maxWidth -= pxPadding.l+pxPadding.r;
|
||||
maxHeight -= pxPadding.b+pxPadding.t;
|
||||
int hSlack = 0, vSlack = 0;
|
||||
if (pxAlignOriginX && !hFixed) {
|
||||
maxWidth -= 1; // Added by getMaxBounds
|
||||
hSlack = 1;
|
||||
}
|
||||
if (pxAlignOriginY && !vFixed) {
|
||||
maxHeight -= 1; // Added by getMaxBounds
|
||||
vSlack = 1;
|
||||
}
|
||||
|
||||
double extraPxWidth = -2*pxRange.lower+pxPadding.l+pxPadding.r;
|
||||
double extraPxHeight = -2*pxRange.lower+pxPadding.b+pxPadding.t;
|
||||
double hScale = 0, vScale = 0;
|
||||
if (cellWidth > 0)
|
||||
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
if (cellHeight > 0)
|
||||
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
if (hScale || vScale) {
|
||||
if (hScale && vScale)
|
||||
scale = std::min(hScale, vScale);
|
||||
else
|
||||
scale = hScale+vScale;
|
||||
if (scale < minScale) {
|
||||
scale = minScale;
|
||||
cutoff = true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (width > 0 && height > 0) {
|
||||
double bestAlignedScale = 0;
|
||||
int bestCols = 0, bestAlignedCols = 0;
|
||||
// TODO optimize to only test up to sqrt(cellCount) cols and rows like in the above branch (for (int q = (int) sqrt(cellCount)+1; ...)
|
||||
for (int cols = 1; cols < width; ++cols) {
|
||||
int rows = (cellCount+cols-1)/cols;
|
||||
int tWidth = (width+spacing)/cols;
|
||||
int tHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(tWidth, tHeight, cellDimensionsConstraint);
|
||||
if (tWidth > 0 && tHeight > 0) {
|
||||
hScale = (tWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
vScale = (tHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
double curScale = std::min(hScale, vScale);
|
||||
if (curScale > scale) {
|
||||
scale = curScale;
|
||||
bestCols = cols;
|
||||
}
|
||||
if (cols*tWidth == width && curScale > bestAlignedScale) {
|
||||
bestAlignedScale = curScale;
|
||||
bestAlignedCols = cols;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bestCols)
|
||||
return -1;
|
||||
// If columns can be aligned with total width at a slight cost to glyph scale, use that number of columns instead
|
||||
if (bestAlignedScale >= minScale && (alignedColumnsBias+1)*bestAlignedScale >= scale) {
|
||||
scale = bestAlignedScale;
|
||||
bestCols = bestAlignedCols;
|
||||
}
|
||||
|
||||
columns = bestCols;
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
cellWidth = (width+spacing)/columns;
|
||||
cellHeight = (height+spacing)/rows;
|
||||
lowerToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
if (scale < minScale)
|
||||
scale = -1;
|
||||
}
|
||||
|
||||
if (scale <= 0) {
|
||||
cellWidth = (int) ceil(minScale*maxWidth+extraPxWidth)+hSlack+spacing+1;
|
||||
cellHeight = (int) ceil(minScale*maxHeight+extraPxHeight)+vSlack+spacing+1;
|
||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
hScale = (cellWidth-hSlack-spacing-extraPxWidth-1)/maxWidth;
|
||||
vScale = (cellHeight-vSlack-spacing-extraPxHeight-1)/maxHeight;
|
||||
scale = std::min(hScale, vScale);
|
||||
}
|
||||
|
||||
if (initial.rows < 0 && initial.cellHeight < 0) {
|
||||
int optimalCellWidth = cellWidth, optimalCellHeight = (int) ceil(scale*maxHeight+extraPxHeight)+vSlack+spacing+1;
|
||||
raiseToConstraint(optimalCellWidth, optimalCellHeight, cellDimensionsConstraint);
|
||||
if (optimalCellHeight < cellHeight && optimalCellWidth <= cellWidth) {
|
||||
cellWidth = optimalCellWidth;
|
||||
cellHeight = optimalCellHeight;
|
||||
}
|
||||
}
|
||||
maxBounds.l *= scale, maxBounds.b *= scale;
|
||||
maxBounds.r *= scale, maxBounds.t *= scale;
|
||||
maxWidth *= scale, maxHeight *= scale;
|
||||
// Redo addition of pxPadding once scale is known
|
||||
pad(maxBounds, pxPadding);
|
||||
maxWidth += pxPadding.l+pxPadding.r;
|
||||
maxHeight += pxPadding.b+pxPadding.t;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
maxBounds = getMaxBounds(maxWidth, maxHeight, glyphs, count, scale, -(unitRange.lower+pxRange.lower/scale));
|
||||
int optimalCellWidth = (int) ceil(maxWidth)+spacing+1;
|
||||
int optimalCellHeight = (int) ceil(maxHeight)+spacing+1;
|
||||
if (cellWidth < 0 || cellHeight < 0) {
|
||||
cellWidth = optimalCellWidth;
|
||||
cellHeight = optimalCellHeight;
|
||||
raiseToConstraint(cellWidth, cellHeight, cellDimensionsConstraint);
|
||||
} else if (cellWidth < optimalCellWidth || cellHeight < optimalCellHeight)
|
||||
cutoff = true;
|
||||
}
|
||||
|
||||
// Compute fixed origin
|
||||
if (hFixed) {
|
||||
if (pxAlignOriginX) {
|
||||
int sl = (int) floor(maxBounds.l-.5);
|
||||
int sr = (int) ceil(maxBounds.r+.5);
|
||||
fixedX = (-sl+(cellWidth-spacing-(sr-sl))/2)/scale;
|
||||
} else
|
||||
fixedX = (-maxBounds.l+.5*(cellWidth-spacing-maxWidth))/scale;
|
||||
}
|
||||
if (vFixed) {
|
||||
if (pxAlignOriginY) {
|
||||
int sb = (int) floor(maxBounds.b-.5);
|
||||
int st = (int) ceil(maxBounds.t+.5);
|
||||
fixedY = (-sb+(cellHeight-spacing-(st-sb))/2)/scale;
|
||||
} else
|
||||
fixedY = (-maxBounds.b+.5*(cellHeight-spacing-maxHeight))/scale;
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
if (columns <= 0) {
|
||||
double bestRating = -1;
|
||||
for (int q = (int) sqrt(cellCount)+1; q > 0; --q) {
|
||||
int cols = q;
|
||||
int rows = (cellCount+cols-1)/cols;
|
||||
int curWidth = cols*cellWidth, curHeight = rows*cellHeight;
|
||||
raiseToConstraint(curWidth, curHeight, dimensionsConstraint);
|
||||
double rating = dimensionsRating(curWidth, curHeight, cols*cellWidth == curWidth);
|
||||
if (rating < bestRating || bestRating < 0) {
|
||||
bestRating = rating;
|
||||
columns = cols;
|
||||
}
|
||||
rows = q;
|
||||
cols = (cellCount+rows-1)/rows;
|
||||
curWidth = cols*cellWidth, curHeight = rows*cellHeight;
|
||||
raiseToConstraint(curWidth, curHeight, dimensionsConstraint);
|
||||
rating = dimensionsRating(curWidth, curHeight, cols*cellWidth == curWidth);
|
||||
if (rating < bestRating || bestRating < 0) {
|
||||
bestRating = rating;
|
||||
columns = cols;
|
||||
}
|
||||
}
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
}
|
||||
width = columns*cellWidth, height = rows*cellHeight;
|
||||
raiseToConstraint(width, height, dimensionsConstraint);
|
||||
// raiseToConstraint may have increased dimensions significantly, rerun if cell dimensions can be optimized.
|
||||
if (dimensionsConstraint != DimensionsConstraint::NONE && initial.cellWidth < 0 && initial.cellHeight < 0) {
|
||||
cellWidth = initial.cellWidth;
|
||||
cellHeight = initial.cellHeight;
|
||||
columns = initial.columns;
|
||||
rows = initial.rows;
|
||||
scale = initial.scale;
|
||||
return pack(glyphs, count);
|
||||
}
|
||||
}
|
||||
|
||||
if (columns < 0) {
|
||||
columns = (width+spacing)/cellWidth;
|
||||
rows = (cellCount+columns-1)/columns;
|
||||
}
|
||||
if (rows*cellHeight > height)
|
||||
rows = height/cellHeight;
|
||||
|
||||
GlyphGeometry::GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = unitRange+pxRange/scale;
|
||||
attribs.innerPadding = innerUnitPadding+1/scale*innerPxPadding;
|
||||
attribs.outerPadding = outerUnitPadding+1/scale*outerPxPadding;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||
int col = 0, row = 0;
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
glyph->frameBox(attribs, cellWidth-spacing, cellHeight-spacing, hFixed ? &fixedX : nullptr, vFixed ? &fixedY : nullptr);
|
||||
glyph->placeBox(col*cellWidth, height-(row+1)*cellHeight);
|
||||
if (++col >= columns) {
|
||||
if (++row >= rows) {
|
||||
return end-glyph-1;
|
||||
}
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setFixedOrigin(bool horizontal, bool vertical) {
|
||||
hFixed = horizontal, vFixed = vertical;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setCellDimensions(int width, int height) {
|
||||
cellWidth = width, cellHeight = height;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::unsetCellDimensions() {
|
||||
cellWidth = -1, cellHeight = -1;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setCellDimensionsConstraint(DimensionsConstraint dimensionsConstraint) {
|
||||
cellDimensionsConstraint = dimensionsConstraint;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setColumns(int columns) {
|
||||
this->columns = columns;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setRows(int rows) {
|
||||
this->rows = rows;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::unsetColumns() {
|
||||
columns = -1;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::unsetRows() {
|
||||
rows = -1;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setDimensions(int width, int height) {
|
||||
this->width = width, this->height = height;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::unsetDimensions() {
|
||||
width = -1, height = -1;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setDimensionsConstraint(DimensionsConstraint dimensionsConstraint) {
|
||||
this->dimensionsConstraint = dimensionsConstraint;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setSpacing(int spacing) {
|
||||
this->spacing = spacing;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setScale(double scale) {
|
||||
this->scale = scale;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setMinimumScale(double minScale) {
|
||||
this->minScale = minScale;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||
this->unitRange = unitRange;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||
this->pxRange = pxRange;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setMiterLimit(double miterLimit) {
|
||||
this->miterLimit = miterLimit;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setOriginPixelAlignment(bool align) {
|
||||
pxAlignOriginX = align, pxAlignOriginY = align;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setInnerUnitPadding(const Padding &padding) {
|
||||
innerUnitPadding = padding;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setOuterUnitPadding(const Padding &padding) {
|
||||
outerUnitPadding = padding;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setInnerPixelPadding(const Padding &padding) {
|
||||
innerPxPadding = padding;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::setOuterPixelPadding(const Padding &padding) {
|
||||
outerPxPadding = padding;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::getDimensions(int &width, int &height) const {
|
||||
width = this->width, height = this->height;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::getCellDimensions(int &width, int &height) const {
|
||||
width = cellWidth, height = cellHeight;
|
||||
}
|
||||
|
||||
int GridAtlasPacker::getColumns() const {
|
||||
return columns;
|
||||
}
|
||||
|
||||
int GridAtlasPacker::getRows() const {
|
||||
return rows;
|
||||
}
|
||||
|
||||
double GridAtlasPacker::getScale() const {
|
||||
return scale;
|
||||
}
|
||||
|
||||
msdfgen::Range GridAtlasPacker::getPixelRange() const {
|
||||
return pxRange+scale*unitRange;
|
||||
}
|
||||
|
||||
void GridAtlasPacker::getFixedOrigin(double &x, double &y) {
|
||||
x = fixedX-.5/scale;
|
||||
y = fixedY-.5/scale;
|
||||
}
|
||||
|
||||
bool GridAtlasPacker::hasCutoff() const {
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Padding.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* This class computes the layout of a static uniform grid atlas and may optionally
|
||||
* also find the minimum required dimensions and/or the maximum glyph scale
|
||||
*/
|
||||
class GridAtlasPacker {
|
||||
|
||||
public:
|
||||
GridAtlasPacker();
|
||||
|
||||
/// Computes the layout for the array of glyphs. Returns 0 on success
|
||||
int pack(GlyphGeometry *glyphs, int count);
|
||||
|
||||
/// Sets whether the origin point should be at the same position in each glyph, separately for horizontal and vertical dimension
|
||||
void setFixedOrigin(bool horizontal, bool vertical);
|
||||
void setCellDimensions(int width, int height);
|
||||
void unsetCellDimensions();
|
||||
void setCellDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
|
||||
void setColumns(int columns);
|
||||
void setRows(int rows);
|
||||
void unsetColumns();
|
||||
void unsetRows();
|
||||
|
||||
/// Sets the atlas's fixed dimensions
|
||||
void setDimensions(int width, int height);
|
||||
/// Sets the atlas's dimensions to be determined during pack
|
||||
void unsetDimensions();
|
||||
/// Sets the constraint to be used when determining dimensions
|
||||
void setDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
|
||||
/// Sets the spacing between glyph boxes
|
||||
void setSpacing(int spacing);
|
||||
/// Sets fixed glyph scale
|
||||
void setScale(double scale);
|
||||
/// Sets the minimum glyph scale
|
||||
void setMinimumScale(double minScale);
|
||||
/// Sets the unit component of the total distance range
|
||||
void setUnitRange(msdfgen::Range unitRange);
|
||||
/// Sets the pixel component of the total distance range
|
||||
void setPixelRange(msdfgen::Range pxRange);
|
||||
/// Sets the miter limit for bounds computation
|
||||
void setMiterLimit(double miterLimit);
|
||||
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||
void setOriginPixelAlignment(bool align);
|
||||
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||
/// Sets the unit component of width of additional padding that is part of each glyph quad
|
||||
void setInnerUnitPadding(const Padding &padding);
|
||||
/// Sets the unit component of width of additional padding around each glyph quad
|
||||
void setOuterUnitPadding(const Padding &padding);
|
||||
/// Sets the pixel component of width of additional padding that is part of each glyph quad
|
||||
void setInnerPixelPadding(const Padding &padding);
|
||||
/// Sets the pixel component of width of additional padding around each glyph quad
|
||||
void setOuterPixelPadding(const Padding &padding);
|
||||
|
||||
/// Outputs the atlas's final dimensions
|
||||
void getDimensions(int &width, int &height) const;
|
||||
/// Outputs the horizontal and vertical difference between the bottom left corners of consecutive grid cells
|
||||
void getCellDimensions(int &width, int &height) const;
|
||||
/// Returns the final number of grid columns
|
||||
int getColumns() const;
|
||||
/// Returns the final number of grid rows
|
||||
int getRows() const;
|
||||
/// Returns the final glyph scale
|
||||
double getScale() const;
|
||||
/// Returns the final combined pixel range (including converted unit range)
|
||||
msdfgen::Range getPixelRange() const;
|
||||
/// Outputs the position of the origin within each cell, each value is only valid if the origin is fixed in the respective dimension
|
||||
void getFixedOrigin(double &x, double &y);
|
||||
/// Returns true if the explicitly constrained cell dimensions aren't large enough to fit each glyph fully
|
||||
bool hasCutoff() const;
|
||||
|
||||
private:
|
||||
int columns, rows;
|
||||
int width, height;
|
||||
int cellWidth, cellHeight;
|
||||
int spacing;
|
||||
DimensionsConstraint dimensionsConstraint;
|
||||
DimensionsConstraint cellDimensionsConstraint;
|
||||
bool hFixed, vFixed;
|
||||
double scale;
|
||||
double minScale;
|
||||
double fixedX, fixedY;
|
||||
msdfgen::Range unitRange;
|
||||
msdfgen::Range pxRange;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
Padding innerUnitPadding, outerUnitPadding;
|
||||
Padding innerPxPadding, outerPxPadding;
|
||||
double scaleMaximizationTolerance;
|
||||
double alignedColumnsBias;
|
||||
bool cutoff;
|
||||
|
||||
static void lowerToConstraint(int &width, int &height, DimensionsConstraint constraint);
|
||||
static void raiseToConstraint(int &width, int &height, DimensionsConstraint constraint);
|
||||
|
||||
double dimensionsRating(int width, int height, bool aligned) const;
|
||||
msdfgen::Shape::Bounds getMaxBounds(double &maxWidth, double &maxHeight, GlyphGeometry *glyphs, int count, double scale, double outerRange) const;
|
||||
double scaleToFit(GlyphGeometry *glyphs, int count, int cellWidth, int cellHeight, msdfgen::Shape::Bounds &maxBounds, double &maxWidth, double &maxHeight) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ class ImmediateAtlasGenerator {
|
|||
public:
|
||||
ImmediateAtlasGenerator();
|
||||
ImmediateAtlasGenerator(int width, int height);
|
||||
template <typename... ARGS>
|
||||
ImmediateAtlasGenerator(int width, int height, ARGS... storageArgs);
|
||||
void generate(const GlyphGeometry *glyphs, int count);
|
||||
void rearrange(int width, int height, const Remap *remapping, int count);
|
||||
void resize(int width, int height);
|
||||
|
|
@ -28,12 +30,15 @@ public:
|
|||
/// Sets the number of threads to be run by generate
|
||||
void setThreadCount(int threadCount);
|
||||
/// Allows access to the underlying AtlasStorage
|
||||
const AtlasStorage & atlasStorage() const;
|
||||
const AtlasStorage &atlasStorage() const;
|
||||
/// Returns the layout of the contained glyphs as a list of GlyphBoxes
|
||||
const std::vector<GlyphBox> &getLayout() const;
|
||||
|
||||
private:
|
||||
AtlasStorage storage;
|
||||
std::vector<GlyphBox> layout;
|
||||
std::vector<T> glyphBuffer;
|
||||
std::vector<byte> errorCorrectionBuffer;
|
||||
GeneratorAttributes attributes;
|
||||
int threadCount;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator() :
|
|||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator(int width, int height) : storage(width, height), threadCount(1) { }
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
template <typename... ARGS>
|
||||
ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator(int width, int height, ARGS... storageArgs) : storage(width, height, storageArgs...), threadCount(1) { }
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::generate(const GlyphGeometry *glyphs, int count) {
|
||||
int maxBoxArea = 0;
|
||||
|
|
@ -22,14 +26,21 @@ void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::generate(const GlyphGe
|
|||
int threadBufferSize = N*maxBoxArea;
|
||||
if (threadCount*threadBufferSize > (int) glyphBuffer.size())
|
||||
glyphBuffer.resize(threadCount*threadBufferSize);
|
||||
if (threadCount*maxBoxArea > (int) errorCorrectionBuffer.size())
|
||||
errorCorrectionBuffer.resize(threadCount*maxBoxArea);
|
||||
std::vector<GeneratorAttributes> threadAttributes(threadCount);
|
||||
for (int i = 0; i < threadCount; ++i) {
|
||||
threadAttributes[i] = attributes;
|
||||
threadAttributes[i].config.errorCorrection.buffer = errorCorrectionBuffer.data()+i*maxBoxArea;
|
||||
}
|
||||
|
||||
Workload([this, &glyphs, threadBufferSize](int i, int threadNo) -> bool {
|
||||
Workload([this, glyphs, &threadAttributes, threadBufferSize](int i, int threadNo) -> bool {
|
||||
const GlyphGeometry &glyph = glyphs[i];
|
||||
if (!glyph.isWhitespace()) {
|
||||
int l, b, w, h;
|
||||
glyph.getBoxRect(l, b, w, h);
|
||||
msdfgen::BitmapRef<T, N> glyphBitmap(glyphBuffer.data()+threadNo*threadBufferSize, w, h);
|
||||
GEN_FN(glyphBitmap, glyph, attributes);
|
||||
GEN_FN(glyphBitmap, glyph, threadAttributes[threadNo]);
|
||||
storage.put(l, b, msdfgen::BitmapConstRef<T, N>(glyphBitmap));
|
||||
}
|
||||
return true;
|
||||
|
|
@ -63,8 +74,13 @@ void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::setThreadCount(int thr
|
|||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
const AtlasStorage & ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::atlasStorage() const {
|
||||
const AtlasStorage &ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::atlasStorage() const {
|
||||
return storage;
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
const std::vector<GlyphBox> &ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::getLayout() const {
|
||||
return layout;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#include "Padding.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
void pad(msdfgen::Shape::Bounds &bounds, const Padding &padding) {
|
||||
bounds.l -= padding.l;
|
||||
bounds.b -= padding.b;
|
||||
bounds.r += padding.r;
|
||||
bounds.t += padding.t;
|
||||
}
|
||||
|
||||
Padding operator-(const Padding &padding) {
|
||||
return Padding(-padding.l, -padding.b, -padding.r, -padding.t);
|
||||
}
|
||||
|
||||
Padding operator+(const Padding &a, const Padding &b) {
|
||||
return Padding(a.l+b.l, a.b+b.b, a.r+b.r, a.t+b.t);
|
||||
}
|
||||
|
||||
Padding operator-(const Padding &a, const Padding &b) {
|
||||
return Padding(a.l-b.l, a.b-b.b, a.r-b.r, a.t-b.t);
|
||||
}
|
||||
|
||||
Padding operator*(double factor, const Padding &padding) {
|
||||
return Padding(factor*padding.l, factor*padding.b, factor*padding.r, factor*padding.t);
|
||||
}
|
||||
|
||||
Padding operator*(const Padding &padding, double factor) {
|
||||
return Padding(padding.l*factor, padding.b*factor, padding.r*factor, padding.t*factor);
|
||||
}
|
||||
|
||||
Padding operator/(const Padding &padding, double divisor) {
|
||||
return Padding(padding.l/divisor, padding.b/divisor, padding.r/divisor, padding.t/divisor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
struct Padding {
|
||||
double l, b, r, t;
|
||||
|
||||
inline Padding(double uniformPadding = 0) : l(uniformPadding), b(uniformPadding), r(uniformPadding), t(uniformPadding) { }
|
||||
inline Padding(double l, double b, double r, double t) : l(l), b(b), r(r), t(t) { }
|
||||
};
|
||||
|
||||
void pad(msdfgen::Shape::Bounds &bounds, const Padding &padding);
|
||||
|
||||
Padding operator-(const Padding &padding);
|
||||
Padding operator+(const Padding &a, const Padding &b);
|
||||
Padding operator-(const Padding &a, const Padding &b);
|
||||
Padding operator*(double factor, const Padding &padding);
|
||||
Padding operator*(const Padding &padding, double factor);
|
||||
Padding operator/(const Padding &padding, double divisor);
|
||||
|
||||
}
|
||||
|
|
@ -25,12 +25,26 @@ RectanglePacker::RectanglePacker(int width, int height) {
|
|||
spaces.push_back(Rectangle { 0, 0, width, height });
|
||||
}
|
||||
|
||||
void RectanglePacker::expand(int width, int height) {
|
||||
if (width > 0 && height > 0) {
|
||||
int oldWidth = 0, oldHeight = 0;
|
||||
for (const Rectangle &space : spaces) {
|
||||
if (space.x+space.w > oldWidth)
|
||||
oldWidth = space.x+space.w;
|
||||
if (space.y+space.h > oldHeight)
|
||||
oldHeight = space.y+space.h;
|
||||
}
|
||||
spaces.push_back(Rectangle { 0, 0, width, height });
|
||||
splitSpace(int(spaces.size()-1), oldWidth, oldHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void RectanglePacker::splitSpace(int index, int w, int h) {
|
||||
Rectangle space = spaces[index];
|
||||
removeFromUnorderedVector(spaces, index);
|
||||
Rectangle a = { space.x, space.y+h, w, space.h-h };
|
||||
Rectangle b = { space.x+w, space.y, space.w-w, h };
|
||||
if (w*(space.h-h) <= h*(space.w-w))
|
||||
if (w*(space.h-h) < h*(space.w-w))
|
||||
a.w = space.w;
|
||||
else
|
||||
b.h = space.h;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ class RectanglePacker {
|
|||
public:
|
||||
RectanglePacker();
|
||||
RectanglePacker(int width, int height);
|
||||
/// Expands the packing area - both width and height must be greater or equal to the previous value
|
||||
void expand(int width, int height);
|
||||
/// Packs the rectangle array, returns how many didn't fit (0 on success)
|
||||
int pack(Rectangle *rectangles, int count);
|
||||
int pack(OrientedRectangle *rectangles, int count);
|
||||
|
|
|
|||
|
|
@ -8,16 +8,37 @@
|
|||
|
||||
namespace msdf_atlas {
|
||||
|
||||
int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, int padding, double scale, double range, double miterLimit) {
|
||||
TightAtlasPacker::TightAtlasPacker() :
|
||||
width(-1), height(-1),
|
||||
spacing(0),
|
||||
dimensionsConstraint(DimensionsConstraint::POWER_OF_TWO_SQUARE),
|
||||
scale(-1),
|
||||
minScale(1),
|
||||
unitRange(0),
|
||||
pxRange(0),
|
||||
miterLimit(0),
|
||||
pxAlignOriginX(false), pxAlignOriginY(false),
|
||||
scaleMaximizationTolerance(.001)
|
||||
{ }
|
||||
|
||||
int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const {
|
||||
// Wrap glyphs into boxes
|
||||
std::vector<Rectangle> rectangles;
|
||||
std::vector<GlyphGeometry *> rectangleGlyphs;
|
||||
rectangles.reserve(count);
|
||||
rectangleGlyphs.reserve(count);
|
||||
GlyphGeometry::GlyphAttributes attribs = { };
|
||||
attribs.scale = scale;
|
||||
attribs.range = unitRange+pxRange/scale;
|
||||
attribs.innerPadding = innerUnitPadding+innerPxPadding/scale;
|
||||
attribs.outerPadding = outerUnitPadding+outerPxPadding/scale;
|
||||
attribs.miterLimit = miterLimit;
|
||||
attribs.pxAlignOriginX = pxAlignOriginX;
|
||||
attribs.pxAlignOriginY = pxAlignOriginY;
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
Rectangle rect = { };
|
||||
glyph->wrapBox(scale, range, miterLimit);
|
||||
glyph->wrapBox(attribs);
|
||||
glyph->getBoxSize(rect.w, rect.h);
|
||||
if (rect.w > 0 && rect.h > 0) {
|
||||
rectangles.push_back(rect);
|
||||
|
|
@ -36,26 +57,27 @@ int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstr
|
|||
std::pair<int, int> dimensions = std::make_pair(width, height);
|
||||
switch (dimensionsConstraint) {
|
||||
case DimensionsConstraint::POWER_OF_TWO_SQUARE:
|
||||
dimensions = packRectangles<SquarePowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), padding);
|
||||
dimensions = packRectangles<SquarePowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
|
||||
break;
|
||||
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
|
||||
dimensions = packRectangles<PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), padding);
|
||||
dimensions = packRectangles<PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), spacing);
|
||||
break;
|
||||
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), padding);
|
||||
dimensions = packRectangles<SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), spacing);
|
||||
break;
|
||||
case DimensionsConstraint::EVEN_SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), padding);
|
||||
dimensions = packRectangles<SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), spacing);
|
||||
break;
|
||||
case DimensionsConstraint::SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<> >(rectangles.data(), rectangles.size(), padding);
|
||||
default:
|
||||
dimensions = packRectangles<SquareSizeSelector<> >(rectangles.data(), rectangles.size(), spacing);
|
||||
break;
|
||||
}
|
||||
if (!(dimensions.first > 0 && dimensions.second > 0))
|
||||
return -1;
|
||||
width = dimensions.first, height = dimensions.second;
|
||||
} else {
|
||||
if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, padding))
|
||||
if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, spacing))
|
||||
return result;
|
||||
}
|
||||
// Set glyph box placement
|
||||
|
|
@ -64,20 +86,21 @@ int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstr
|
|||
return 0;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::packAndScale(GlyphGeometry *glyphs, int count, int width, int height, int padding, double unitRange, double pxRange, double miterLimit, double tolerance) {
|
||||
double TightAtlasPacker::packAndScale(GlyphGeometry *glyphs, int count) const {
|
||||
bool lastResult = false;
|
||||
#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), width, height, padding, (scale), unitRange+pxRange/(scale), miterLimit))
|
||||
int w = width, h = height;
|
||||
#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), w, h, (scale)))
|
||||
double minScale = 1, maxScale = 1;
|
||||
if (TRY_PACK(1)) {
|
||||
while (maxScale < 1e+32 && TRY_PACK(maxScale = 2*minScale))
|
||||
while (maxScale < 1e+32 && ((maxScale = 2*minScale), TRY_PACK(maxScale)))
|
||||
minScale = maxScale;
|
||||
} else {
|
||||
while (minScale > 1e-32 && !TRY_PACK(minScale = .5*maxScale))
|
||||
while (minScale > 1e-32 && ((minScale = .5*maxScale), !TRY_PACK(minScale)))
|
||||
maxScale = minScale;
|
||||
}
|
||||
if (minScale == maxScale)
|
||||
return 0;
|
||||
while (minScale/maxScale < 1-tolerance) {
|
||||
while (minScale/maxScale < 1-scaleMaximizationTolerance) {
|
||||
double midScale = .5*(minScale+maxScale);
|
||||
if (TRY_PACK(midScale))
|
||||
minScale = midScale;
|
||||
|
|
@ -89,31 +112,17 @@ double TightAtlasPacker::packAndScale(GlyphGeometry *glyphs, int count, int widt
|
|||
return minScale;
|
||||
}
|
||||
|
||||
TightAtlasPacker::TightAtlasPacker() :
|
||||
width(-1), height(-1),
|
||||
padding(0),
|
||||
dimensionsConstraint(DimensionsConstraint::POWER_OF_TWO_SQUARE),
|
||||
scale(-1),
|
||||
minScale(1),
|
||||
unitRange(0),
|
||||
pxRange(0),
|
||||
miterLimit(0),
|
||||
scaleMaximizationTolerance(.001)
|
||||
{ }
|
||||
|
||||
int TightAtlasPacker::pack(GlyphGeometry *glyphs, int count) {
|
||||
double initialScale = scale > 0 ? scale : minScale;
|
||||
if (initialScale > 0) {
|
||||
if (int remaining = tryPack(glyphs, count, dimensionsConstraint, width, height, padding, initialScale, unitRange+pxRange/initialScale, miterLimit))
|
||||
if (int remaining = tryPack(glyphs, count, dimensionsConstraint, width, height, initialScale))
|
||||
return remaining;
|
||||
} else if (width < 0 || height < 0)
|
||||
return -1;
|
||||
if (scale <= 0)
|
||||
scale = packAndScale(glyphs, count, width, height, padding, unitRange, pxRange, miterLimit, scaleMaximizationTolerance);
|
||||
scale = packAndScale(glyphs, count);
|
||||
if (scale <= 0)
|
||||
return -1;
|
||||
pxRange += scale*unitRange;
|
||||
unitRange = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -129,8 +138,8 @@ void TightAtlasPacker::setDimensionsConstraint(DimensionsConstraint dimensionsCo
|
|||
this->dimensionsConstraint = dimensionsConstraint;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setPadding(int padding) {
|
||||
this->padding = padding;
|
||||
void TightAtlasPacker::setSpacing(int spacing) {
|
||||
this->spacing = spacing;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setScale(double scale) {
|
||||
|
|
@ -141,11 +150,11 @@ void TightAtlasPacker::setMinimumScale(double minScale) {
|
|||
this->minScale = minScale;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setUnitRange(double unitRange) {
|
||||
void TightAtlasPacker::setUnitRange(msdfgen::Range unitRange) {
|
||||
this->unitRange = unitRange;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setPixelRange(double pxRange) {
|
||||
void TightAtlasPacker::setPixelRange(msdfgen::Range pxRange) {
|
||||
this->pxRange = pxRange;
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +162,30 @@ void TightAtlasPacker::setMiterLimit(double miterLimit) {
|
|||
this->miterLimit = miterLimit;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setOriginPixelAlignment(bool align) {
|
||||
pxAlignOriginX = align, pxAlignOriginY = align;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setOriginPixelAlignment(bool alignX, bool alignY) {
|
||||
pxAlignOriginX = alignX, pxAlignOriginY = alignY;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setInnerUnitPadding(const Padding &padding) {
|
||||
innerUnitPadding = padding;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setOuterUnitPadding(const Padding &padding) {
|
||||
outerUnitPadding = padding;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setInnerPixelPadding(const Padding &padding) {
|
||||
innerPxPadding = padding;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setOuterPixelPadding(const Padding &padding) {
|
||||
outerPxPadding = padding;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
||||
width = this->width, height = this->height;
|
||||
}
|
||||
|
|
@ -161,8 +194,8 @@ double TightAtlasPacker::getScale() const {
|
|||
return scale;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::getPixelRange() const {
|
||||
return pxRange;
|
||||
msdfgen::Range TightAtlasPacker::getPixelRange() const {
|
||||
return pxRange+scale*unitRange;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +1,77 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Padding.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* This class computes the layout of a static atlas and may optionally
|
||||
* This class computes the layout of a static tightly packed atlas and may optionally
|
||||
* also find the minimum required dimensions and/or the maximum glyph scale
|
||||
*/
|
||||
class TightAtlasPacker {
|
||||
|
||||
public:
|
||||
/// Constraints for the atlas's dimensions - see size selectors for more info
|
||||
enum class DimensionsConstraint {
|
||||
POWER_OF_TWO_SQUARE,
|
||||
POWER_OF_TWO_RECTANGLE,
|
||||
MULTIPLE_OF_FOUR_SQUARE,
|
||||
EVEN_SQUARE,
|
||||
SQUARE
|
||||
};
|
||||
|
||||
TightAtlasPacker();
|
||||
|
||||
/// Computes the layout for the array of glyphs. Returns 0 on success
|
||||
int pack(GlyphGeometry *glyphs, int count);
|
||||
|
||||
/// Sets the atlas's dimensions to be fixed
|
||||
/// Sets the atlas's fixed dimensions
|
||||
void setDimensions(int width, int height);
|
||||
/// Sets the atlas's dimensions to be determined during pack
|
||||
void unsetDimensions();
|
||||
/// Sets the constraint to be used when determining dimensions
|
||||
void setDimensionsConstraint(DimensionsConstraint dimensionsConstraint);
|
||||
/// Sets the padding between glyph boxes
|
||||
void setPadding(int padding);
|
||||
/// Sets the spacing between glyph boxes
|
||||
void setSpacing(int spacing);
|
||||
/// Sets fixed glyph scale
|
||||
void setScale(double scale);
|
||||
/// Sets the minimum glyph scale
|
||||
void setMinimumScale(double minScale);
|
||||
/// Sets the unit component of the total distance range
|
||||
void setUnitRange(double unitRange);
|
||||
void setUnitRange(msdfgen::Range unitRange);
|
||||
/// Sets the pixel component of the total distance range
|
||||
void setPixelRange(double pxRange);
|
||||
void setPixelRange(msdfgen::Range pxRange);
|
||||
/// Sets the miter limit for bounds computation
|
||||
void setMiterLimit(double miterLimit);
|
||||
/// Sets whether each glyph's origin point should stay aligned with the pixel grid
|
||||
void setOriginPixelAlignment(bool align);
|
||||
void setOriginPixelAlignment(bool alignX, bool alignY);
|
||||
/// Sets the unit component of width of additional padding that is part of each glyph quad
|
||||
void setInnerUnitPadding(const Padding &padding);
|
||||
/// Sets the unit component of width of additional padding around each glyph quad
|
||||
void setOuterUnitPadding(const Padding &padding);
|
||||
/// Sets the pixel component of width of additional padding that is part of each glyph quad
|
||||
void setInnerPixelPadding(const Padding &padding);
|
||||
/// Sets the pixel component of width of additional padding around each glyph quad
|
||||
void setOuterPixelPadding(const Padding &padding);
|
||||
|
||||
/// Outputs the atlas's final dimensions
|
||||
void getDimensions(int &width, int &height) const;
|
||||
/// Returns the final glyph scale
|
||||
double getScale() const;
|
||||
/// Returns the final combined pixel range (including converted unit range)
|
||||
double getPixelRange() const;
|
||||
msdfgen::Range getPixelRange() const;
|
||||
|
||||
private:
|
||||
int width, height;
|
||||
int padding;
|
||||
int spacing;
|
||||
DimensionsConstraint dimensionsConstraint;
|
||||
double scale;
|
||||
double minScale;
|
||||
double unitRange;
|
||||
double pxRange;
|
||||
msdfgen::Range unitRange;
|
||||
msdfgen::Range pxRange;
|
||||
double miterLimit;
|
||||
bool pxAlignOriginX, pxAlignOriginY;
|
||||
Padding innerUnitPadding, outerUnitPadding;
|
||||
Padding innerPxPadding, outerPxPadding;
|
||||
double scaleMaximizationTolerance;
|
||||
|
||||
static int tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, int padding, double scale, double range, double miterLimit);
|
||||
static double packAndScale(GlyphGeometry *glyphs, int count, int width, int height, int padding, double unitRange, double pxRange, double miterLimit, double tolerance);
|
||||
int tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, double scale) const;
|
||||
double packAndScale(GlyphGeometry *glyphs, int count) const;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
|
||||
#include "artery-font-export.h"
|
||||
|
||||
#ifndef MSDF_ATLAS_NO_ARTERY_FONT
|
||||
|
||||
#include <artery-font/std-artery-font.h>
|
||||
#include <artery-font/stdio-serialization.h>
|
||||
#include "GlyphGeometry.h"
|
||||
#include "image-encode.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
|
@ -53,76 +56,98 @@ artery_font::PixelFormat getPixelFormat<float>() {
|
|||
}
|
||||
|
||||
template <typename REAL, typename T, int N>
|
||||
bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) {
|
||||
bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) {
|
||||
artery_font::StdArteryFont<REAL> arfont = { };
|
||||
arfont.metadataFormat = artery_font::METADATA_NONE;
|
||||
|
||||
if (glyphCount > 0) {
|
||||
msdfgen::FontMetrics fontMetrics;
|
||||
if (!msdfgen::getFontMetrics(fontMetrics, font))
|
||||
return false;
|
||||
double fsScale = 1/fontMetrics.emSize;
|
||||
artery_font::StdFontVariant<REAL> fontVariant = { };
|
||||
fontVariant.codepointType = convertCodepointType(properties.glyphIdentifierType);
|
||||
arfont.variants = artery_font::StdList<typename artery_font::StdArteryFont<REAL>::Variant>(fontCount);
|
||||
for (int i = 0; i < fontCount; ++i) {
|
||||
const FontGeometry &font = fonts[i];
|
||||
GlyphIdentifierType identifierType = font.getPreferredIdentifierType();
|
||||
const msdfgen::FontMetrics &fontMetrics = font.getMetrics();
|
||||
typename artery_font::StdArteryFont<REAL>::Variant &fontVariant = arfont.variants[i] = typename artery_font::StdArteryFont<REAL>::Variant();
|
||||
fontVariant.codepointType = convertCodepointType(identifierType);
|
||||
fontVariant.imageType = convertImageType(properties.imageType);
|
||||
fontVariant.metrics.fontSize = REAL(properties.fontSize);
|
||||
if (properties.imageType != ImageType::HARD_MASK)
|
||||
fontVariant.metrics.distanceRange = REAL(properties.pxRange);
|
||||
fontVariant.metrics.emSize = REAL(fsScale*fontMetrics.emSize);
|
||||
fontVariant.metrics.ascender = REAL(fsScale*fontMetrics.ascenderY);
|
||||
fontVariant.metrics.descender = REAL(fsScale*fontMetrics.descenderY);
|
||||
fontVariant.metrics.lineHeight = REAL(fsScale*fontMetrics.lineHeight);
|
||||
fontVariant.metrics.underlineY = REAL(fsScale*fontMetrics.underlineY);
|
||||
fontVariant.metrics.underlineThickness = REAL(fsScale*fontMetrics.underlineThickness);
|
||||
fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(glyphCount);
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[i];
|
||||
glyph.codepoint = glyphs[i].getIdentifier(properties.glyphIdentifierType);
|
||||
fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
|
||||
if (properties.imageType != ImageType::HARD_MASK) {
|
||||
fontVariant.metrics.distanceRange = REAL(properties.pxRange.upper-properties.pxRange.lower);
|
||||
fontVariant.metrics.distanceRangeMiddle = REAL(.5*(properties.pxRange.lower+properties.pxRange.upper));
|
||||
}
|
||||
fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
|
||||
fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
|
||||
fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
|
||||
fontVariant.metrics.lineHeight = REAL(fontMetrics.lineHeight);
|
||||
fontVariant.metrics.underlineY = REAL(fontMetrics.underlineY);
|
||||
fontVariant.metrics.underlineThickness = REAL(fontMetrics.underlineThickness);
|
||||
const char *name = font.getName();
|
||||
if (name)
|
||||
(std::string &) fontVariant.name = name;
|
||||
fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(font.getGlyphs().size());
|
||||
int j = 0;
|
||||
for (const GlyphGeometry &glyphGeom : font.getGlyphs()) {
|
||||
artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[j++];
|
||||
glyph.codepoint = glyphGeom.getIdentifier(identifierType);
|
||||
glyph.image = 0;
|
||||
double l, b, r, t;
|
||||
glyphs[i].getQuadPlaneBounds(l, b, r, t);
|
||||
glyph.planeBounds.l = REAL(fsScale*l);
|
||||
glyph.planeBounds.b = REAL(fsScale*b);
|
||||
glyph.planeBounds.r = REAL(fsScale*r);
|
||||
glyph.planeBounds.t = REAL(fsScale*t);
|
||||
glyphs[i].getQuadAtlasBounds(l, b, r, t);
|
||||
glyphGeom.getQuadPlaneBounds(l, b, r, t);
|
||||
glyph.planeBounds.l = REAL(l);
|
||||
glyph.planeBounds.b = REAL(b);
|
||||
glyph.planeBounds.r = REAL(r);
|
||||
glyph.planeBounds.t = REAL(t);
|
||||
glyphGeom.getQuadAtlasBounds(l, b, r, t);
|
||||
glyph.imageBounds.l = REAL(l);
|
||||
glyph.imageBounds.b = REAL(b);
|
||||
glyph.imageBounds.r = REAL(r);
|
||||
glyph.imageBounds.t = REAL(t);
|
||||
glyph.advance.h = REAL(fsScale*glyphs[i].getAdvance());
|
||||
glyph.advance.h = REAL(glyphGeom.getAdvance());
|
||||
glyph.advance.v = REAL(0);
|
||||
for (int j = 0; j < glyphCount; ++j) {
|
||||
double kerning;
|
||||
if (msdfgen::getKerning(kerning, font, glyphs[i].getGlyphIndex(), glyphs[j].getGlyphIndex()) && kerning) {
|
||||
artery_font::KernPair<REAL> kernPair = { };
|
||||
kernPair.codepoint1 = glyphs[i].getIdentifier(properties.glyphIdentifierType);
|
||||
kernPair.codepoint2 = glyphs[j].getIdentifier(properties.glyphIdentifierType);
|
||||
kernPair.advance.h = REAL(fsScale*kerning);
|
||||
fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair);
|
||||
}
|
||||
}
|
||||
}
|
||||
arfont.variants.vector.push_back((artery_font::StdFontVariant<REAL> &&) fontVariant);
|
||||
switch (identifierType) {
|
||||
case GlyphIdentifierType::GLYPH_INDEX:
|
||||
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
|
||||
artery_font::KernPair<REAL> kernPair = { };
|
||||
kernPair.codepoint1 = elem.first.first;
|
||||
kernPair.codepoint2 = elem.first.second;
|
||||
kernPair.advance.h = REAL(elem.second);
|
||||
((std::vector<artery_font::KernPair<REAL> > &) fontVariant.kernPairs).push_back((artery_font::KernPair<REAL> &&) kernPair);
|
||||
}
|
||||
break;
|
||||
case GlyphIdentifierType::UNICODE_CODEPOINT:
|
||||
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
|
||||
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(elem.first.first));
|
||||
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(elem.first.second));
|
||||
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
|
||||
artery_font::KernPair<REAL> kernPair = { };
|
||||
kernPair.codepoint1 = glyph1->getCodepoint();
|
||||
kernPair.codepoint2 = glyph2->getCodepoint();
|
||||
kernPair.advance.h = REAL(elem.second);
|
||||
((std::vector<artery_font::KernPair<REAL> > &) fontVariant.kernPairs).push_back((artery_font::KernPair<REAL> &&) kernPair);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
arfont.images = artery_font::StdList<typename artery_font::StdArteryFont<REAL>::Image>(1);
|
||||
{
|
||||
artery_font::StdImage image = { };
|
||||
typename artery_font::StdArteryFont<REAL>::Image &image = arfont.images[0] = typename artery_font::StdArteryFont<REAL>::Image();
|
||||
image.width = atlas.width;
|
||||
image.height = atlas.height;
|
||||
image.channels = N;
|
||||
image.imageType = convertImageType(properties.imageType);
|
||||
switch (properties.imageFormat) {
|
||||
#ifndef MSDFGEN_DISABLE_PNG
|
||||
case ImageFormat::PNG:
|
||||
image.encoding = artery_font::IMAGE_PNG;
|
||||
image.pixelFormat = artery_font::PIXEL_UNSIGNED8;
|
||||
if (!encodePng(image.data.vector, atlas))
|
||||
if (!encodePng((std::vector<byte> &) image.data, atlas))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
case ImageFormat::TIFF:
|
||||
image.encoding = artery_font::IMAGE_TIFF;
|
||||
image.pixelFormat = artery_font::PIXEL_FLOAT32;
|
||||
if (!encodeTiff(image.data.vector, atlas))
|
||||
if (!encodeTiff((std::vector<byte> &) image.data, atlas))
|
||||
return false;
|
||||
break;
|
||||
case ImageFormat::BINARY:
|
||||
|
|
@ -136,24 +161,38 @@ bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, in
|
|||
return false;
|
||||
image.encoding = artery_font::IMAGE_RAW_BINARY;
|
||||
image.rawBinaryFormat.rowLength = N*sizeof(T)*atlas.width;
|
||||
image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP;
|
||||
image.data = artery_font::StdByteArray(N*sizeof(T)*atlas.width*atlas.height);
|
||||
memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height);
|
||||
switch (properties.yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP;
|
||||
memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height);
|
||||
break;
|
||||
case YDirection::TOP_DOWN: {
|
||||
image.rawBinaryFormat.orientation = artery_font::ORIENTATION_TOP_DOWN;
|
||||
byte *imageData = (byte *) image.data;
|
||||
for (int y = atlas.height-1; y >= 0; --y) {
|
||||
memcpy(imageData, atlas.pixels+N*atlas.width*y, N*sizeof(T)*atlas.width);
|
||||
imageData += N*sizeof(T)*atlas.width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
arfont.images.vector.push_back((artery_font::StdImage &&) image);
|
||||
}
|
||||
|
||||
return artery_font::writeFile(arfont, filename);
|
||||
}
|
||||
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef MSDF_ATLAS_NO_ARTERY_FONT
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "FontGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
struct ArteryFontExportProperties {
|
||||
GlyphIdentifierType glyphIdentifierType;
|
||||
double fontSize;
|
||||
double pxRange;
|
||||
msdfgen::Range pxRange;
|
||||
ImageType imageType;
|
||||
ImageFormat imageFormat;
|
||||
YDirection yDirection;
|
||||
};
|
||||
|
||||
/// Encodes the atlas bitmap and its layout into an Artery Atlas Font file
|
||||
template <typename REAL, typename T, int N>
|
||||
bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,11 +2,22 @@
|
|||
#include "bitmap-blit.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
#define BOUND_AREA() { \
|
||||
if (dx < 0) w += dx, sx -= dx, dx = 0; \
|
||||
if (dy < 0) h += dy, sy -= dy, dy = 0; \
|
||||
if (sx < 0) w += sx, dx -= sx, sx = 0; \
|
||||
if (sy < 0) h += sy, dy -= sy, sy = 0; \
|
||||
w = std::max(0, std::min(w, std::min(dst.width-dx, src.width-sx))); \
|
||||
h = std::max(0, std::min(h, std::min(dst.height-dy, src.height-sy))); \
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
void blitSameType(const msdfgen::BitmapRef<T, N> &dst, const msdfgen::BitmapConstRef<T, N> &src, int dx, int dy, int sx, int sy, int w, int h) {
|
||||
BOUND_AREA();
|
||||
for (int y = 0; y < h; ++y)
|
||||
memcpy(dst(dx, dy+y), src(sx, sy+y), sizeof(T)*N*w);
|
||||
}
|
||||
|
|
@ -21,6 +32,7 @@ BLIT_SAME_TYPE_IMPL(float, 3)
|
|||
BLIT_SAME_TYPE_IMPL(float, 4)
|
||||
|
||||
void blit(const msdfgen::BitmapRef<byte, 1> &dst, const msdfgen::BitmapConstRef<float, 1> &src, int dx, int dy, int sx, int sy, int w, int h) {
|
||||
BOUND_AREA();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
|
|
@ -31,6 +43,7 @@ void blit(const msdfgen::BitmapRef<byte, 1> &dst, const msdfgen::BitmapConstRef<
|
|||
}
|
||||
|
||||
void blit(const msdfgen::BitmapRef<byte, 3> &dst, const msdfgen::BitmapConstRef<float, 3> &src, int dx, int dy, int sx, int sy, int w, int h) {
|
||||
BOUND_AREA();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
|
|
@ -43,6 +56,7 @@ void blit(const msdfgen::BitmapRef<byte, 3> &dst, const msdfgen::BitmapConstRef<
|
|||
}
|
||||
|
||||
void blit(const msdfgen::BitmapRef<byte, 4> &dst, const msdfgen::BitmapConstRef<float, 4> &src, int dx, int dy, int sx, int sy, int w, int h) {
|
||||
BOUND_AREA();
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ namespace msdf_atlas {
|
|||
|
||||
/*
|
||||
* Copies a rectangular section from source bitmap to destination bitmap.
|
||||
* Width and height are not checked and must not exceed bitmap bounds!
|
||||
*/
|
||||
|
||||
void blit(const msdfgen::BitmapRef<byte, 1> &dst, const msdfgen::BitmapConstRef<byte, 1> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
|
|
|
|||
|
|
@ -25,36 +25,6 @@ static char escapedChar(char c) {
|
|||
}
|
||||
}
|
||||
|
||||
static int readWord(std::string &str, FILE *f) {
|
||||
while (true) {
|
||||
int c = fgetc(f);
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
|
||||
str.push_back((char) c);
|
||||
else
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
static bool readString(std::string &str, FILE *f, char terminator) {
|
||||
bool escape = false;
|
||||
while (true) {
|
||||
int c = fgetc(f);
|
||||
if (c < 0)
|
||||
return false;
|
||||
if (escape) {
|
||||
str.push_back(escapedChar((char) c));
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == terminator)
|
||||
return true;
|
||||
else if (c == '\\')
|
||||
escape = true;
|
||||
else
|
||||
str.push_back((char) c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parseInt(int &i, const char *str) {
|
||||
i = 0;
|
||||
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { // hex
|
||||
|
|
@ -84,6 +54,181 @@ static bool parseInt(int &i, const char *str) {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <int (READ_CHAR)(void *)>
|
||||
static int readWord(void *userData, std::string &str) {
|
||||
while (true) {
|
||||
int c = READ_CHAR(userData);
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
|
||||
str.push_back((char) c);
|
||||
else
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
template <int (READ_CHAR)(void *)>
|
||||
static bool readString(void *userData, std::string &str, char terminator) {
|
||||
bool escape = false;
|
||||
while (true) {
|
||||
int c = READ_CHAR(userData);
|
||||
if (c < 0)
|
||||
return false;
|
||||
if (escape) {
|
||||
str.push_back(escapedChar((char) c));
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == terminator)
|
||||
return true;
|
||||
else if (c == '\\')
|
||||
escape = true;
|
||||
else
|
||||
str.push_back((char) c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int (READ_CHAR)(void *), void (ADD)(void *, unicode_t), bool (INCLUDE)(void *, const std::string &)>
|
||||
static bool charsetParse(void *userData, bool disableCharLiterals, bool disableInclude) {
|
||||
|
||||
enum {
|
||||
CLEAR,
|
||||
TIGHT,
|
||||
RANGE_BRACKET,
|
||||
RANGE_START,
|
||||
RANGE_SEPARATOR,
|
||||
RANGE_END
|
||||
} state = CLEAR;
|
||||
|
||||
std::string buffer;
|
||||
std::vector<unicode_t> unicodeBuffer;
|
||||
unicode_t rangeStart = 0;
|
||||
for (int c = READ_CHAR(userData), start = true; c >= 0; start = false) {
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // number
|
||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR))
|
||||
return false;
|
||||
buffer.push_back((char) c);
|
||||
c = readWord<READ_CHAR>(userData, buffer);
|
||||
{
|
||||
int cp;
|
||||
if (!parseInt(cp, buffer.c_str()))
|
||||
return false;
|
||||
switch (state) {
|
||||
case CLEAR:
|
||||
if (cp >= 0)
|
||||
ADD(userData, (unicode_t) cp);
|
||||
state = TIGHT;
|
||||
break;
|
||||
case RANGE_BRACKET:
|
||||
rangeStart = (unicode_t) cp;
|
||||
state = RANGE_START;
|
||||
break;
|
||||
case RANGE_SEPARATOR:
|
||||
for (unicode_t u = rangeStart; (int) u <= cp; ++u)
|
||||
ADD(userData, u);
|
||||
state = RANGE_END;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
buffer.clear();
|
||||
continue; // next character already read
|
||||
case '\'': // single UTF-8 character
|
||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals)
|
||||
return false;
|
||||
if (!readString<READ_CHAR>(userData, buffer, '\''))
|
||||
return false;
|
||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||
if (unicodeBuffer.size() == 1) {
|
||||
switch (state) {
|
||||
case CLEAR:
|
||||
if (unicodeBuffer[0] > 0)
|
||||
ADD(userData, unicodeBuffer[0]);
|
||||
state = TIGHT;
|
||||
break;
|
||||
case RANGE_BRACKET:
|
||||
rangeStart = unicodeBuffer[0];
|
||||
state = RANGE_START;
|
||||
break;
|
||||
case RANGE_SEPARATOR:
|
||||
for (unicode_t u = rangeStart; u <= unicodeBuffer[0]; ++u)
|
||||
ADD(userData, u);
|
||||
state = RANGE_END;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
unicodeBuffer.clear();
|
||||
buffer.clear();
|
||||
break;
|
||||
case '"': // string of UTF-8 characters
|
||||
if (state != CLEAR || disableCharLiterals)
|
||||
return false;
|
||||
if (!readString<READ_CHAR>(userData, buffer, '"'))
|
||||
return false;
|
||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||
for (unicode_t cp : unicodeBuffer)
|
||||
ADD(userData, cp);
|
||||
unicodeBuffer.clear();
|
||||
buffer.clear();
|
||||
state = TIGHT;
|
||||
break;
|
||||
case '[': // character range start
|
||||
if (state != CLEAR)
|
||||
return false;
|
||||
state = RANGE_BRACKET;
|
||||
break;
|
||||
case ']': // character range end
|
||||
if (state == RANGE_END)
|
||||
state = TIGHT;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case '@': // annotation
|
||||
if (state != CLEAR)
|
||||
return false;
|
||||
c = readWord<READ_CHAR>(userData, buffer);
|
||||
if (buffer == "include") {
|
||||
while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
c = READ_CHAR(userData);
|
||||
if (c != '"')
|
||||
return false;
|
||||
buffer.clear();
|
||||
if (!readString<READ_CHAR>(userData, buffer, '"'))
|
||||
return false;
|
||||
INCLUDE(userData, buffer);
|
||||
state = TIGHT;
|
||||
} else
|
||||
return false;
|
||||
buffer.clear();
|
||||
break;
|
||||
case ',': case ';': // separator
|
||||
if (!(state == CLEAR || state == TIGHT)) {
|
||||
if (state == RANGE_START)
|
||||
state = RANGE_SEPARATOR;
|
||||
else
|
||||
return false;
|
||||
} // else treat as whitespace
|
||||
// fallthrough
|
||||
case ' ': case '\n': case '\r': case '\t': // whitespace
|
||||
if (state == TIGHT)
|
||||
state = CLEAR;
|
||||
break;
|
||||
case 0xef: // UTF-8 byte order mark
|
||||
if (start) {
|
||||
if (!(READ_CHAR(userData) == 0xbb && READ_CHAR(userData) == 0xbf))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
default: // unexpected character
|
||||
return false;
|
||||
}
|
||||
c = READ_CHAR(userData);
|
||||
}
|
||||
|
||||
return state == CLEAR || state == TIGHT;
|
||||
}
|
||||
|
||||
static std::string combinePath(const char *basePath, const char *relPath) {
|
||||
if (relPath[0] == '/' || (relPath[0] && relPath[1] == ':')) // absolute path?
|
||||
return relPath;
|
||||
|
|
@ -96,155 +241,57 @@ static std::string combinePath(const char *basePath, const char *relPath) {
|
|||
return std::string(basePath, lastSlash+1)+relPath;
|
||||
}
|
||||
|
||||
bool Charset::load(const char *filename, bool disableCharLiterals) {
|
||||
struct CharsetLoadData {
|
||||
Charset *charset;
|
||||
const char *filename;
|
||||
bool disableCharLiterals;
|
||||
FILE *file;
|
||||
|
||||
if (FILE *f = fopen(filename, "rb")) {
|
||||
|
||||
enum {
|
||||
CLEAR,
|
||||
TIGHT,
|
||||
RANGE_BRACKET,
|
||||
RANGE_START,
|
||||
RANGE_SEPARATOR,
|
||||
RANGE_END
|
||||
} state = CLEAR;
|
||||
|
||||
std::string buffer;
|
||||
std::vector<unicode_t> unicodeBuffer;
|
||||
unicode_t rangeStart = 0;
|
||||
for (int c = fgetc(f), start = true; c >= 0; start = false) {
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // number
|
||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR))
|
||||
goto FAIL;
|
||||
buffer.push_back((char) c);
|
||||
c = readWord(buffer, f);
|
||||
{
|
||||
int cp;
|
||||
if (!parseInt(cp, buffer.c_str()))
|
||||
goto FAIL;
|
||||
switch (state) {
|
||||
case CLEAR:
|
||||
if (cp >= 0)
|
||||
add((unicode_t) cp);
|
||||
state = TIGHT;
|
||||
break;
|
||||
case RANGE_BRACKET:
|
||||
rangeStart = (unicode_t) cp;
|
||||
state = RANGE_START;
|
||||
break;
|
||||
case RANGE_SEPARATOR:
|
||||
for (unicode_t u = rangeStart; (int) u <= cp; ++u)
|
||||
add(u);
|
||||
state = RANGE_END;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
buffer.clear();
|
||||
continue; // next character already read
|
||||
case '\'': // single UTF-8 character
|
||||
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals)
|
||||
goto FAIL;
|
||||
if (!readString(buffer, f, '\''))
|
||||
goto FAIL;
|
||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||
if (unicodeBuffer.size() == 1) {
|
||||
switch (state) {
|
||||
case CLEAR:
|
||||
if (unicodeBuffer[0] > 0)
|
||||
add(unicodeBuffer[0]);
|
||||
state = TIGHT;
|
||||
break;
|
||||
case RANGE_BRACKET:
|
||||
rangeStart = unicodeBuffer[0];
|
||||
state = RANGE_START;
|
||||
break;
|
||||
case RANGE_SEPARATOR:
|
||||
for (unicode_t u = rangeStart; u <= unicodeBuffer[0]; ++u)
|
||||
add(u);
|
||||
state = RANGE_END;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
} else
|
||||
goto FAIL;
|
||||
unicodeBuffer.clear();
|
||||
buffer.clear();
|
||||
break;
|
||||
case '"': // string of UTF-8 characters
|
||||
if (state != CLEAR || disableCharLiterals)
|
||||
goto FAIL;
|
||||
if (!readString(buffer, f, '"'))
|
||||
goto FAIL;
|
||||
utf8Decode(unicodeBuffer, buffer.c_str());
|
||||
for (unicode_t cp : unicodeBuffer)
|
||||
add(cp);
|
||||
unicodeBuffer.clear();
|
||||
buffer.clear();
|
||||
state = TIGHT;
|
||||
break;
|
||||
case '[': // character range start
|
||||
if (state != CLEAR)
|
||||
goto FAIL;
|
||||
state = RANGE_BRACKET;
|
||||
break;
|
||||
case ']': // character range end
|
||||
if (state == RANGE_END)
|
||||
state = TIGHT;
|
||||
else
|
||||
goto FAIL;
|
||||
break;
|
||||
case '@': // annotation
|
||||
if (state != CLEAR)
|
||||
goto FAIL;
|
||||
c = readWord(buffer, f);
|
||||
if (buffer == "include") {
|
||||
while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
||||
c = fgetc(f);
|
||||
if (c != '"')
|
||||
goto FAIL;
|
||||
buffer.clear();
|
||||
if (!readString(buffer, f, '"'))
|
||||
goto FAIL;
|
||||
load(combinePath(filename, buffer.c_str()).c_str());
|
||||
state = TIGHT;
|
||||
} else
|
||||
goto FAIL;
|
||||
buffer.clear();
|
||||
break;
|
||||
case ',': case ';': // separator
|
||||
if (!(state == CLEAR || state == TIGHT)) {
|
||||
if (state == RANGE_START)
|
||||
state = RANGE_SEPARATOR;
|
||||
else
|
||||
goto FAIL;
|
||||
} // else treat as whitespace
|
||||
case ' ': case '\n': case '\r': case '\t': // whitespace
|
||||
if (state == TIGHT)
|
||||
state = CLEAR;
|
||||
break;
|
||||
case 0xef: // UTF-8 byte order mark
|
||||
if (start) {
|
||||
if (!(fgetc(f) == 0xbb && fgetc(f) == 0xbf))
|
||||
goto FAIL;
|
||||
break;
|
||||
}
|
||||
default: // unexpected character
|
||||
goto FAIL;
|
||||
}
|
||||
c = fgetc(f);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return state == CLEAR || state == TIGHT;
|
||||
|
||||
FAIL:
|
||||
fclose(f);
|
||||
return false;
|
||||
static int readChar(void *userData) {
|
||||
return fgetc(reinterpret_cast<CharsetLoadData *>(userData)->file);
|
||||
}
|
||||
|
||||
static void add(void *userData, unicode_t cp) {
|
||||
reinterpret_cast<CharsetLoadData *>(userData)->charset->add(cp);
|
||||
}
|
||||
|
||||
static bool include(void *userData, const std::string &path) {
|
||||
const CharsetLoadData &ud = *reinterpret_cast<CharsetLoadData *>(userData);
|
||||
return ud.charset->load(combinePath(ud.filename, path.c_str()).c_str(), ud.disableCharLiterals);
|
||||
}
|
||||
};
|
||||
|
||||
bool Charset::load(const char *filename, bool disableCharLiterals) {
|
||||
if (FILE *f = fopen(filename, "rb")) {
|
||||
CharsetLoadData userData = { this, filename, disableCharLiterals, f };
|
||||
bool success = charsetParse<CharsetLoadData::readChar, CharsetLoadData::add, CharsetLoadData::include>(&userData, disableCharLiterals, false);
|
||||
fclose(f);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct CharsetParseData {
|
||||
Charset *charset;
|
||||
const char *cur, *end;
|
||||
|
||||
static int readChar(void *userData) {
|
||||
CharsetParseData &ud = *reinterpret_cast<CharsetParseData *>(userData);
|
||||
return ud.cur < ud.end ? (int) (unsigned char) *ud.cur++ : -1;
|
||||
}
|
||||
|
||||
static void add(void *userData, unicode_t cp) {
|
||||
reinterpret_cast<CharsetParseData *>(userData)->charset->add(cp);
|
||||
}
|
||||
|
||||
static bool include(void *, const std::string &) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool Charset::parse(const char *str, size_t strLength, bool disableCharLiterals) {
|
||||
CharsetParseData userData = { this, str, str+strLength };
|
||||
return charsetParse<CharsetParseData::readChar, CharsetParseData::add, CharsetParseData::include>(&userData, disableCharLiterals, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,40 @@
|
|||
#include "csv-export.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename) {
|
||||
bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename) {
|
||||
FILE *f = fopen(filename, "w");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
double fsScale = 1/emSize;
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
double l, b, r, t;
|
||||
fprintf(f, "%d,%.17g,", glyphs[i].getIdentifier(glyphIdentifierType), fsScale*glyphs[i].getAdvance());
|
||||
glyphs[i].getQuadPlaneBounds(l, b, r, t);
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", fsScale*l, fsScale*b, fsScale*r, fsScale*t);
|
||||
glyphs[i].getQuadAtlasBounds(l, b, r, t);
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t);
|
||||
for (int i = 0; i < fontCount; ++i) {
|
||||
for (const GlyphGeometry &glyph : fonts[i].getGlyphs()) {
|
||||
double l, b, r, t;
|
||||
if (fontCount > 1)
|
||||
fprintf(f, "%d,", i);
|
||||
fprintf(f, "%d,%.17g,", glyph.getIdentifier(fonts[i].getPreferredIdentifierType()), glyph.getAdvance());
|
||||
glyph.getQuadPlaneBounds(l, b, r, t);
|
||||
switch (yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, b, r, t);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, -t, r, -b);
|
||||
break;
|
||||
}
|
||||
glyph.getQuadAtlasBounds(l, b, r, t);
|
||||
switch (yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, atlasHeight-t, r, atlasHeight-b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "FontGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* Writes the positioning data and atlas layout of the glyphs into a CSV file
|
||||
* The columns are: glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t)
|
||||
* The columns are: font variant index (if fontCount > 1), glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t)
|
||||
*/
|
||||
bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double emSize, const char *filename);
|
||||
bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,32 +8,44 @@ void scanlineGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGe
|
|||
}
|
||||
|
||||
void sdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
|
||||
msdfgen::generateSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.overlapSupport);
|
||||
msdfgen::generateSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), attribs.config);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
}
|
||||
|
||||
void psdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
|
||||
msdfgen::generatePseudoSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.overlapSupport);
|
||||
msdfgen::generatePSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), attribs.config);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
}
|
||||
|
||||
void msdfGenerator(const msdfgen::BitmapRef<float, 3> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
|
||||
msdfgen::generateMSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.errorCorrectionThreshold, attribs.overlapSupport);
|
||||
msdfgen::MSDFGeneratorConfig config = attribs.config;
|
||||
if (attribs.scanlinePass)
|
||||
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
|
||||
msdfgen::generateMSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
|
||||
if (attribs.scanlinePass) {
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
if (attribs.errorCorrectionThreshold > 0)
|
||||
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
|
||||
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
|
||||
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
|
||||
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mtsdfGenerator(const msdfgen::BitmapRef<float, 4> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
|
||||
msdfgen::generateMTSDF(output, glyph.getShape(), glyph.getBoxRange(), glyph.getBoxScale(), glyph.getBoxTranslate(), attribs.errorCorrectionThreshold, attribs.overlapSupport);
|
||||
msdfgen::MSDFGeneratorConfig config = attribs.config;
|
||||
if (attribs.scanlinePass)
|
||||
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
|
||||
msdfgen::generateMTSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
|
||||
if (attribs.scanlinePass) {
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
if (attribs.errorCorrectionThreshold > 0)
|
||||
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
|
||||
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
|
||||
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
|
||||
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace msdf_atlas {
|
|||
void scanlineGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
|
||||
/// Generates a true signed distance field of the glyph
|
||||
void sdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
|
||||
/// Generates a signed pseudo-distance field of the glyph
|
||||
/// Generates a signed perpendicular distance field of the glyph
|
||||
void psdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
|
||||
/// Generates a multi-channel signed distance field of the glyph
|
||||
void msdfGenerator(const msdfgen::BitmapRef<float, 3> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,98 @@
|
|||
|
||||
#include "image-encode.h"
|
||||
|
||||
#include <core/pixel-conversion.hpp>
|
||||
|
||||
#ifdef MSDFGEN_USE_LIBPNG
|
||||
|
||||
#include <png.h>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
class PngGuard {
|
||||
png_structp png;
|
||||
png_infop info;
|
||||
|
||||
public:
|
||||
inline PngGuard(png_structp png, png_infop info) : png(png), info(info) { }
|
||||
inline ~PngGuard() {
|
||||
png_destroy_write_struct(&png, &info);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static void pngIgnoreError(png_structp, png_const_charp) { }
|
||||
|
||||
static void pngWrite(png_structp png, png_bytep data, png_size_t length) {
|
||||
std::vector<byte> &output = *reinterpret_cast<std::vector<byte> *>(png_get_io_ptr(png));
|
||||
output.insert(output.end(), data, data+length);
|
||||
}
|
||||
|
||||
static void pngFlush(png_structp) { }
|
||||
|
||||
static bool pngEncode(std::vector<byte> &output, const byte *pixels, int width, int height, int channels, int colorType) {
|
||||
if (!(pixels && width && height))
|
||||
return false;
|
||||
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngIgnoreError, &pngIgnoreError);
|
||||
if (!png)
|
||||
return false;
|
||||
png_infop info = png_create_info_struct(png);
|
||||
PngGuard guard(png, info);
|
||||
if (!info)
|
||||
return false;
|
||||
std::vector<const byte *> rows(height);
|
||||
for (int y = 0; y < height; ++y)
|
||||
rows[y] = pixels+channels*width*(height-y-1);
|
||||
if (setjmp(png_jmpbuf(png)))
|
||||
return false;
|
||||
png_set_write_fn(png, &output, &pngWrite, &pngFlush);
|
||||
png_set_IHDR(png, info, width, height, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
png_set_compression_level(png, 9);
|
||||
png_set_rows(png, info, const_cast<png_bytepp>(&rows[0]));
|
||||
png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pngEncode(std::vector<byte> &output, const float *pixels, int width, int height, int channels, int colorType) {
|
||||
if (!(pixels && width && height))
|
||||
return false;
|
||||
int subpixels = channels*width*height;
|
||||
std::vector<byte> bytePixels(subpixels);
|
||||
for (int i = 0; i < subpixels; ++i)
|
||||
bytePixels[i] = msdfgen::pixelFloatToByte(pixels[i]);
|
||||
return pngEncode(output, bytePixels.data(), width, height, channels, colorType);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 1> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 1, PNG_COLOR_TYPE_GRAY);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 3> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 3, PNG_COLOR_TYPE_RGB);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 4> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 4, PNG_COLOR_TYPE_RGB_ALPHA);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 1> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 1, PNG_COLOR_TYPE_GRAY);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 3> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 3, PNG_COLOR_TYPE_RGB);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4> &bitmap) {
|
||||
return pngEncode(output, bitmap.pixels, bitmap.width, bitmap.height, 4, PNG_COLOR_TYPE_RGB_ALPHA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MSDFGEN_USE_LODEPNG
|
||||
|
||||
#include <lodepng.h>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
|
@ -61,3 +153,5 @@ bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <msdfgen.h>
|
||||
#include "types.h"
|
||||
|
||||
#ifndef MSDFGEN_DISABLE_PNG
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
// Functions to encode an image as a sequence of bytes in memory
|
||||
|
|
@ -18,3 +20,5 @@ bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 3
|
|||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4> &bitmap);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace msdf_atlas {
|
|||
|
||||
/// Saves the bitmap as an image file with the specified format
|
||||
template <typename T, int N>
|
||||
bool saveImage(const msdfgen::BitmapConstRef<T, N> &bitmap, ImageFormat format, const char *filename);
|
||||
bool saveImage(const msdfgen::BitmapConstRef<T, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,32 +7,38 @@
|
|||
namespace msdf_atlas {
|
||||
|
||||
template <int N>
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename);
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection);
|
||||
template <int N>
|
||||
bool saveImageBinaryLE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
bool saveImageBinaryLE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
|
||||
template <int N>
|
||||
bool saveImageBinaryBE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
bool saveImageBinaryBE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename);
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection);
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
|
||||
|
||||
template <int N>
|
||||
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename) {
|
||||
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP) {
|
||||
switch (format) {
|
||||
#ifndef MSDFGEN_DISABLE_PNG
|
||||
case ImageFormat::PNG:
|
||||
return msdfgen::savePng(bitmap, filename);
|
||||
#endif
|
||||
case ImageFormat::BMP:
|
||||
return msdfgen::saveBmp(bitmap, filename);
|
||||
case ImageFormat::TIFF:
|
||||
return false;
|
||||
case ImageFormat::RGBA:
|
||||
return msdfgen::saveRgba(bitmap, filename);
|
||||
case ImageFormat::FL32:
|
||||
return false;
|
||||
case ImageFormat::TEXT:
|
||||
return saveImageText(bitmap, filename);
|
||||
return saveImageText(bitmap, filename, outputYDirection);
|
||||
case ImageFormat::TEXT_FLOAT:
|
||||
return false;
|
||||
case ImageFormat::BINARY:
|
||||
return saveImageBinary(bitmap, filename);
|
||||
return saveImageBinary(bitmap, filename, outputYDirection);
|
||||
case ImageFormat::BINARY_FLOAT:
|
||||
case ImageFormat::BINARY_FLOAT_BE:
|
||||
return false;
|
||||
|
|
@ -42,34 +48,50 @@ bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat forma
|
|||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImage(const msdfgen::BitmapConstRef<float, N> &bitmap, ImageFormat format, const char *filename) {
|
||||
bool saveImage(const msdfgen::BitmapConstRef<float, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection = YDirection::BOTTOM_UP) {
|
||||
switch (format) {
|
||||
#ifndef MSDFGEN_DISABLE_PNG
|
||||
case ImageFormat::PNG:
|
||||
return msdfgen::savePng(bitmap, filename);
|
||||
#endif
|
||||
case ImageFormat::BMP:
|
||||
return msdfgen::saveBmp(bitmap, filename);
|
||||
case ImageFormat::TIFF:
|
||||
return msdfgen::saveTiff(bitmap, filename);
|
||||
case ImageFormat::RGBA:
|
||||
return msdfgen::saveRgba(bitmap, filename);
|
||||
case ImageFormat::FL32:
|
||||
return msdfgen::saveFl32(bitmap, filename);
|
||||
case ImageFormat::TEXT:
|
||||
return false;
|
||||
case ImageFormat::TEXT_FLOAT:
|
||||
return saveImageText(bitmap, filename);
|
||||
return saveImageText(bitmap, filename, outputYDirection);
|
||||
case ImageFormat::BINARY:
|
||||
return false;
|
||||
case ImageFormat::BINARY_FLOAT:
|
||||
return saveImageBinaryLE(bitmap, filename);
|
||||
return saveImageBinaryLE(bitmap, filename, outputYDirection);
|
||||
case ImageFormat::BINARY_FLOAT_BE:
|
||||
return saveImageBinaryBE(bitmap, filename);
|
||||
return saveImageBinaryBE(bitmap, filename, outputYDirection);
|
||||
default:;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename) {
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
success = fwrite(bitmap.pixels, 1, N*bitmap.width*bitmap.height, f) == N*bitmap.width*bitmap.height;
|
||||
size_t written = 0;
|
||||
switch (outputYDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
written = fwrite(bitmap.pixels, 1, (size_t) N*bitmap.width*bitmap.height, f);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
written += fwrite(bitmap.pixels+(size_t) N*bitmap.width*y, 1, (size_t) N*bitmap.width, f);
|
||||
break;
|
||||
}
|
||||
success = written == (size_t) N*bitmap.width*bitmap.height;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
|
|
@ -82,10 +104,20 @@ bool
|
|||
#else
|
||||
saveImageBinaryLE
|
||||
#endif
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
success = fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f) == N*bitmap.width*bitmap.height;
|
||||
size_t written = 0;
|
||||
switch (outputYDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
written = fwrite(bitmap.pixels, sizeof(float), (size_t) N*bitmap.width*bitmap.height, f);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
written += fwrite(bitmap.pixels+(size_t) N*bitmap.width*y, sizeof(float), (size_t) N*bitmap.width, f);
|
||||
break;
|
||||
}
|
||||
success = written == (size_t) N*bitmap.width*bitmap.height;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
|
|
@ -98,18 +130,19 @@ bool
|
|||
#else
|
||||
saveImageBinaryBE
|
||||
#endif
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
const float *p = bitmap.pixels;
|
||||
int count = N*bitmap.width*bitmap.height;
|
||||
int written = 0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const unsigned char *b = reinterpret_cast<const unsigned char *>(p++);
|
||||
for (int i = sizeof(float)-1; i >= 0; --i)
|
||||
written += fwrite(b+i, 1, 1, f);
|
||||
size_t written = 0;
|
||||
for (int y = 0; y < bitmap.height; ++y) {
|
||||
const float *p = bitmap.pixels+(size_t) N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y);
|
||||
for (int x = 0; x < bitmap.width; ++x) {
|
||||
const unsigned char *b = reinterpret_cast<const unsigned char *>(p++);
|
||||
for (int i = sizeof(float)-1; i >= 0; --i)
|
||||
written += fwrite(b+i, 1, 1, f);
|
||||
}
|
||||
}
|
||||
success = written == sizeof(float)*count;
|
||||
success = written == sizeof(float)*N*bitmap.width*bitmap.height;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
|
|
@ -117,15 +150,15 @@ bool
|
|||
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename) {
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
const byte *p = bitmap.pixels;
|
||||
success = true;
|
||||
for (int y = 0; y < bitmap.height; ++y) {
|
||||
for (int x = 0; x < N*bitmap.width; ++x) {
|
||||
fprintf(f, x ? " %02X" : "%02X", (unsigned) *p++);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
const byte *p = bitmap.pixels+(size_t) N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y);
|
||||
for (int x = 0; x < N*bitmap.width; ++x)
|
||||
success &= fprintf(f, x ? " %02X" : "%02X", (unsigned) *p++) > 0;
|
||||
success &= fprintf(f, "\n") > 0;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
|
@ -133,15 +166,15 @@ bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *f
|
|||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
const float *p = bitmap.pixels;
|
||||
success = true;
|
||||
for (int y = 0; y < bitmap.height; ++y) {
|
||||
for (int x = 0; x < N*bitmap.width; ++x) {
|
||||
fprintf(f, x ? " %g" : "%g", *p++);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
const float *p = bitmap.pixels+(size_t) N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y);
|
||||
for (int x = 0; x < N*bitmap.width; ++x)
|
||||
success &= fprintf(f, x ? " %g" : "%g", *p++) > 0;
|
||||
success &= fprintf(f, "\n") > 0;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,46 @@
|
|||
|
||||
#include "json-export.h"
|
||||
|
||||
#include <string>
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static const char * imageTypeString(ImageType type) {
|
||||
static std::string escapeJsonString(const char *str) {
|
||||
char uval[7] = "\\u0000";
|
||||
std::string outStr;
|
||||
while (*str) {
|
||||
switch (*str) {
|
||||
case '\\':
|
||||
outStr += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
outStr += "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
outStr += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
outStr += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
outStr += "\\t";
|
||||
break;
|
||||
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: /* \\t */ /* \\n */ case 0x0b: case 0x0c: /* \\r */ case 0x0e: case 0x0f:
|
||||
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
uval[4] = '0'+(*str >= 0x10);
|
||||
uval[5] = "0123456789abcdef"[*str&0x0f];
|
||||
outStr += uval;
|
||||
break;
|
||||
default:
|
||||
outStr.push_back(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
return outStr;
|
||||
}
|
||||
|
||||
static const char *imageTypeString(ImageType type) {
|
||||
switch (type) {
|
||||
case ImageType::HARD_MASK:
|
||||
return "hardmask";
|
||||
|
|
@ -21,12 +58,7 @@ static const char * imageTypeString(ImageType type) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename) {
|
||||
msdfgen::FontMetrics fontMetrics;
|
||||
if (!msdfgen::getFontMetrics(fontMetrics, font))
|
||||
return false;
|
||||
double fsScale = 1/fontMetrics.emSize;
|
||||
|
||||
bool exportJSON(const FontGeometry *fonts, int fontCount, ImageType imageType, const JsonAtlasMetrics &metrics, const char *filename, bool kerning) {
|
||||
FILE *f = fopen(filename, "w");
|
||||
if (!f)
|
||||
return false;
|
||||
|
|
@ -35,64 +67,138 @@ bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyp
|
|||
// Atlas properties
|
||||
fputs("\"atlas\":{", f); {
|
||||
fprintf(f, "\"type\":\"%s\",", imageTypeString(imageType));
|
||||
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF)
|
||||
fprintf(f, "\"distanceRange\":%.17g,", pxRange);
|
||||
fprintf(f, "\"size\":%.17g,", fontSize);
|
||||
fprintf(f, "\"width\":%d,", atlasWidth);
|
||||
fprintf(f, "\"height\":%d,", atlasHeight);
|
||||
fputs("\"yOrigin\":\"bottom\"", f);
|
||||
} fputs("},", f);
|
||||
|
||||
// Font metrics
|
||||
fputs("\"metrics\":{", f); {
|
||||
fprintf(f, "\"lineHeight\":%.17g,", fsScale*fontMetrics.lineHeight);
|
||||
fprintf(f, "\"ascender\":%.17g,", fsScale*fontMetrics.ascenderY);
|
||||
fprintf(f, "\"descender\":%.17g,", fsScale*fontMetrics.descenderY);
|
||||
fprintf(f, "\"underlineY\":%.17g,", fsScale*fontMetrics.underlineY);
|
||||
fprintf(f, "\"underlineThickness\":%.17g", fsScale*fontMetrics.underlineThickness);
|
||||
} fputs("},", f);
|
||||
|
||||
// Glyph mapping
|
||||
fputs("\"glyphs\":[", f);
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
fputs(i == 0 ? "{" : ",{", f);
|
||||
switch (glyphIdentifierType) {
|
||||
case GlyphIdentifierType::GLYPH_INDEX:
|
||||
fprintf(f, "\"index\":%d,", glyphs[i].getIndex());
|
||||
break;
|
||||
case GlyphIdentifierType::UNICODE_CODEPOINT:
|
||||
fprintf(f, "\"unicode\":%u,", glyphs[i].getCodepoint());
|
||||
break;
|
||||
if (imageType == ImageType::SDF || imageType == ImageType::PSDF || imageType == ImageType::MSDF || imageType == ImageType::MTSDF) {
|
||||
fprintf(f, "\"distanceRange\":%.17g,", metrics.distanceRange.upper-metrics.distanceRange.lower);
|
||||
fprintf(f, "\"distanceRangeMiddle\":%.17g,", .5*(metrics.distanceRange.lower+metrics.distanceRange.upper));
|
||||
}
|
||||
fprintf(f, "\"advance\":%.17g", fsScale*glyphs[i].getAdvance());
|
||||
double l, b, r, t;
|
||||
glyphs[i].getQuadPlaneBounds(l, b, r, t);
|
||||
if (l || b || r || t)
|
||||
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", fsScale*l, fsScale*b, fsScale*r, fsScale*t);
|
||||
glyphs[i].getQuadAtlasBounds(l, b, r, t);
|
||||
if (l || b || r || t)
|
||||
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
|
||||
fputs("}", f);
|
||||
}
|
||||
fputs("],", f);
|
||||
|
||||
// Kerning pairs
|
||||
fputs("\"kerning\":[", f);
|
||||
bool firstPair = true;
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
for (int j = 0; j < glyphCount; ++j) {
|
||||
double kerning;
|
||||
if (msdfgen::getKerning(kerning, font, glyphs[i].getCodepoint(), glyphs[j].getCodepoint()) && kerning) {
|
||||
fputs(firstPair ? "{" : ",{", f);
|
||||
fprintf(f, "\"unicode1\":%u,", glyphs[i].getCodepoint());
|
||||
fprintf(f, "\"unicode2\":%u,", glyphs[j].getCodepoint());
|
||||
fprintf(f, "\"advance\":%.17g", fsScale*kerning);
|
||||
fputs("}", f);
|
||||
firstPair = false;
|
||||
fprintf(f, "\"size\":%.17g,", metrics.size);
|
||||
fprintf(f, "\"width\":%d,", metrics.width);
|
||||
fprintf(f, "\"height\":%d,", metrics.height);
|
||||
fprintf(f, "\"yOrigin\":\"%s\"", metrics.yDirection == YDirection::TOP_DOWN ? "top" : "bottom");
|
||||
if (metrics.grid) {
|
||||
fputs(",\"grid\":{", f);
|
||||
fprintf(f, "\"cellWidth\":%d,", metrics.grid->cellWidth);
|
||||
fprintf(f, "\"cellHeight\":%d,", metrics.grid->cellHeight);
|
||||
fprintf(f, "\"columns\":%d,", metrics.grid->columns);
|
||||
fprintf(f, "\"rows\":%d", metrics.grid->rows);
|
||||
if (metrics.grid->originX)
|
||||
fprintf(f, ",\"originX\":%.17g", *metrics.grid->originX);
|
||||
if (metrics.grid->originY) {
|
||||
switch (metrics.yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
fprintf(f, ",\"originY\":%.17g", *metrics.grid->originY);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
fprintf(f, ",\"originY\":%.17g", (metrics.grid->cellHeight-metrics.grid->spacing-1)/metrics.size-*metrics.grid->originY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputs("}", f);
|
||||
}
|
||||
} fputs("},", f);
|
||||
|
||||
if (fontCount > 1)
|
||||
fputs("\"variants\":[", f);
|
||||
for (int i = 0; i < fontCount; ++i) {
|
||||
const FontGeometry &font = fonts[i];
|
||||
if (fontCount > 1)
|
||||
fputs(i == 0 ? "{" : ",{", f);
|
||||
|
||||
// Font name
|
||||
const char *name = font.getName();
|
||||
if (name)
|
||||
fprintf(f, "\"name\":\"%s\",", escapeJsonString(name).c_str());
|
||||
|
||||
// Font metrics
|
||||
fputs("\"metrics\":{", f); {
|
||||
double yFactor = metrics.yDirection == YDirection::TOP_DOWN ? -1 : 1;
|
||||
const msdfgen::FontMetrics &fontMetrics = font.getMetrics();
|
||||
fprintf(f, "\"emSize\":%.17g,", fontMetrics.emSize);
|
||||
fprintf(f, "\"lineHeight\":%.17g,", fontMetrics.lineHeight);
|
||||
fprintf(f, "\"ascender\":%.17g,", yFactor*fontMetrics.ascenderY);
|
||||
fprintf(f, "\"descender\":%.17g,", yFactor*fontMetrics.descenderY);
|
||||
fprintf(f, "\"underlineY\":%.17g,", yFactor*fontMetrics.underlineY);
|
||||
fprintf(f, "\"underlineThickness\":%.17g", fontMetrics.underlineThickness);
|
||||
} fputs("},", f);
|
||||
|
||||
// Glyph mapping
|
||||
fputs("\"glyphs\":[", f);
|
||||
bool firstGlyph = true;
|
||||
for (const GlyphGeometry &glyph : font.getGlyphs()) {
|
||||
fputs(firstGlyph ? "{" : ",{", f);
|
||||
switch (font.getPreferredIdentifierType()) {
|
||||
case GlyphIdentifierType::GLYPH_INDEX:
|
||||
fprintf(f, "\"index\":%d,", glyph.getIndex());
|
||||
break;
|
||||
case GlyphIdentifierType::UNICODE_CODEPOINT:
|
||||
fprintf(f, "\"unicode\":%u,", glyph.getCodepoint());
|
||||
break;
|
||||
}
|
||||
fprintf(f, "\"advance\":%.17g", glyph.getAdvance());
|
||||
double l, b, r, t;
|
||||
glyph.getQuadPlaneBounds(l, b, r, t);
|
||||
if (l || b || r || t) {
|
||||
switch (metrics.yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, -t, r, -b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
glyph.getQuadAtlasBounds(l, b, r, t);
|
||||
if (l || b || r || t) {
|
||||
switch (metrics.yDirection) {
|
||||
case YDirection::BOTTOM_UP:
|
||||
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
|
||||
break;
|
||||
case YDirection::TOP_DOWN:
|
||||
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, metrics.height-t, r, metrics.height-b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputs("}", f);
|
||||
firstGlyph = false;
|
||||
} fputs("]", f);
|
||||
|
||||
// Kerning pairs
|
||||
if (kerning) {
|
||||
fputs(",\"kerning\":[", f);
|
||||
bool firstPair = true;
|
||||
switch (font.getPreferredIdentifierType()) {
|
||||
case GlyphIdentifierType::GLYPH_INDEX:
|
||||
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
|
||||
fputs(firstPair ? "{" : ",{", f);
|
||||
fprintf(f, "\"index1\":%d,", kernPair.first.first);
|
||||
fprintf(f, "\"index2\":%d,", kernPair.first.second);
|
||||
fprintf(f, "\"advance\":%.17g", kernPair.second);
|
||||
fputs("}", f);
|
||||
firstPair = false;
|
||||
}
|
||||
break;
|
||||
case GlyphIdentifierType::UNICODE_CODEPOINT:
|
||||
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
|
||||
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.first));
|
||||
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.second));
|
||||
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
|
||||
fputs(firstPair ? "{" : ",{", f);
|
||||
fprintf(f, "\"unicode1\":%u,", glyph1->getCodepoint());
|
||||
fprintf(f, "\"unicode2\":%u,", glyph2->getCodepoint());
|
||||
fprintf(f, "\"advance\":%.17g", kernPair.second);
|
||||
fputs("}", f);
|
||||
firstPair = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} fputs("]", f);
|
||||
}
|
||||
|
||||
if (fontCount > 1)
|
||||
fputs("}", f);
|
||||
}
|
||||
fputs("]", f);
|
||||
if (fontCount > 1)
|
||||
fputs("]", f);
|
||||
|
||||
fputs("}\n", f);
|
||||
fclose(f);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,25 @@
|
|||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "FontGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
struct JsonAtlasMetrics {
|
||||
struct GridMetrics {
|
||||
int cellWidth, cellHeight;
|
||||
int columns, rows;
|
||||
const double *originX, *originY;
|
||||
int spacing;
|
||||
};
|
||||
msdfgen::Range distanceRange;
|
||||
double size;
|
||||
int width, height;
|
||||
YDirection yDirection;
|
||||
const GridMetrics *grid;
|
||||
};
|
||||
|
||||
/// Writes the font and glyph metrics and atlas layout data into a comprehensive JSON file
|
||||
bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, GlyphIdentifierType glyphIdentifierType, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename);
|
||||
bool exportJSON(const FontGeometry *fonts, int fontCount, ImageType imageType, const JsonAtlasMetrics &metrics, const char *filename, bool kerning);
|
||||
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,12 +2,10 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR v1.1 (2020-10-18)
|
||||
* ---------------------------------------------------------------------
|
||||
* A utility by Viktor Chlumsky, (c) 2020
|
||||
*
|
||||
* Generates compact bitmap font atlases using MSDFGEN.
|
||||
*
|
||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR
|
||||
* ---------------------------------------------------
|
||||
* A utility by Viktor Chlumsky, (c) 2020 - 2025
|
||||
* Generates compact bitmap font atlases using MSDFgen
|
||||
*/
|
||||
|
||||
#include <msdfgen.h>
|
||||
|
|
@ -16,9 +14,11 @@
|
|||
#include "types.h"
|
||||
#include "utf8.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Padding.h"
|
||||
#include "Charset.h"
|
||||
#include "GlyphBox.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "FontGeometry.h"
|
||||
#include "RectanglePacker.h"
|
||||
#include "rectangle-packing.h"
|
||||
#include "Workload.h"
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include "AtlasStorage.h"
|
||||
#include "BitmapAtlasStorage.h"
|
||||
#include "TightAtlasPacker.h"
|
||||
#include "GridAtlasPacker.h"
|
||||
#include "AtlasGenerator.h"
|
||||
#include "ImmediateAtlasGenerator.h"
|
||||
#include "DynamicAtlas.h"
|
||||
|
|
@ -37,5 +38,3 @@
|
|||
#include "csv-export.h"
|
||||
#include "json-export.h"
|
||||
#include "shadron-preview-generator.h"
|
||||
|
||||
#define MSDF_ATLAS_VERSION "1.1"
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ namespace msdf_atlas {
|
|||
|
||||
/// Packs the rectangle array into an atlas with fixed dimensions, returns how many didn't fit (0 on success)
|
||||
template <typename RectangleType>
|
||||
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding = 0);
|
||||
int packRectangles(RectangleType *rectangles, int count, int width, int height, int spacing = 0);
|
||||
|
||||
/// Packs the rectangle array into an atlas of unknown size, returns the minimum required dimensions constrained by SizeSelector
|
||||
template <class SizeSelector, typename RectangleType>
|
||||
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding = 0);
|
||||
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int spacing = 0);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,35 +18,35 @@ static void copyRectanglePlacement(OrientedRectangle &dst, const OrientedRectang
|
|||
}
|
||||
|
||||
template <typename RectangleType>
|
||||
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding) {
|
||||
if (padding)
|
||||
int packRectangles(RectangleType *rectangles, int count, int width, int height, int spacing) {
|
||||
if (spacing)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
rectangles[i].w += padding;
|
||||
rectangles[i].h += padding;
|
||||
rectangles[i].w += spacing;
|
||||
rectangles[i].h += spacing;
|
||||
}
|
||||
int result = RectanglePacker(width+padding, height+padding).pack(rectangles, count);
|
||||
if (padding)
|
||||
int result = RectanglePacker(width+spacing, height+spacing).pack(rectangles, count);
|
||||
if (spacing)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
rectangles[i].w -= padding;
|
||||
rectangles[i].h -= padding;
|
||||
rectangles[i].w -= spacing;
|
||||
rectangles[i].h -= spacing;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class SizeSelector, typename RectangleType>
|
||||
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding) {
|
||||
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int spacing) {
|
||||
std::vector<RectangleType> rectanglesCopy(count);
|
||||
int totalArea = 0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
rectanglesCopy[i].w = rectangles[i].w+padding;
|
||||
rectanglesCopy[i].h = rectangles[i].h+padding;
|
||||
rectanglesCopy[i].w = rectangles[i].w+spacing;
|
||||
rectanglesCopy[i].h = rectangles[i].h+spacing;
|
||||
totalArea += rectangles[i].w*rectangles[i].h;
|
||||
}
|
||||
std::pair<int, int> dimensions;
|
||||
SizeSelector sizeSelector(totalArea);
|
||||
int width, height;
|
||||
while (sizeSelector(width, height)) {
|
||||
if (!RectanglePacker(width+padding, height+padding).pack(rectanglesCopy.data(), count)) {
|
||||
if (!RectanglePacker(width+spacing, height+spacing).pack(rectanglesCopy.data(), count)) {
|
||||
dimensions.first = width;
|
||||
dimensions.second = height;
|
||||
for (int i = 0; i < count; ++i)
|
||||
|
|
|
|||
|
|
@ -6,25 +6,25 @@
|
|||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static const char * const shadronFillGlyphMask = R"(
|
||||
template <ATLAS, RANGE, COLOR>
|
||||
static const char *const shadronFillGlyphMask = R"(
|
||||
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||
float fill = texture((ATLAS), texCoord).r;
|
||||
return vec4(vec3(COLOR), fill);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char * const shadronFillGlyphSdf = R"(
|
||||
template <ATLAS, RANGE, COLOR>
|
||||
static const char *const shadronFillGlyphSdf = R"(
|
||||
template <ATLAS, RANGE, ZERO_DIST, COLOR>
|
||||
glsl vec4 fillGlyph(vec2 texCoord) {
|
||||
vec3 s = texture((ATLAS), texCoord).rgb;
|
||||
float sd = dot(vec2(RANGE), 0.5/fwidth(texCoord))*(median(s.r, s.g, s.b)-0.5);
|
||||
float sd = dot(vec2(RANGE), 0.5/fwidth(texCoord))*(median(s.r, s.g, s.b)-ZERO_DIST);
|
||||
float fill = clamp(sd+0.5, 0.0, 1.0);
|
||||
return vec4(vec3(COLOR), fill);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char * const shadronPreviewPreamble = R"(
|
||||
static const char *const shadronPreviewPreamble = R"(
|
||||
#include <median>
|
||||
|
||||
glsl struct GlyphVertex {
|
||||
|
|
@ -43,11 +43,11 @@ glsl vec4 projectVertex(out vec2 texCoord, in GlyphVertex vertex) {
|
|||
return vec4(coord, 0.0, 1.0);
|
||||
}
|
||||
%s
|
||||
#define PREVIEW_IMAGE(NAME, ATLAS, RANGE, COLOR, VERTEX_LIST, TEXT_SIZE, DIMENSIONS) model image NAME : \
|
||||
#define PREVIEW_IMAGE(NAME, ATLAS, RANGE, ZERO_DIST, COLOR, VERTEX_LIST, TEXT_SIZE, DIMENSIONS) model image NAME : \
|
||||
vertex_data(GlyphVertex), \
|
||||
fragment_data(vec2), \
|
||||
vertex(projectVertex<TEXT_SIZE>, triangles, VERTEX_LIST), \
|
||||
fragment(fillGlyph<ATLAS, RANGE, COLOR>), \
|
||||
fragment(fillGlyph<ATLAS, RANGE, ZERO_DIST, COLOR>), \
|
||||
depth(false), \
|
||||
blend(transparency), \
|
||||
background(vec4(vec3(COLOR), 0.0)), \
|
||||
|
|
@ -77,23 +77,48 @@ static std::string relativizePath(const char *base, const char *target) {
|
|||
return output;
|
||||
}
|
||||
|
||||
bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, const char *outputFilename) {
|
||||
static std::string escapeString(const std::string &str) {
|
||||
std::string output;
|
||||
for (int i = 0; i < (int) str.size(); ++i) {
|
||||
switch (str[i]) {
|
||||
case '"':
|
||||
output += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
output += "\\\\";
|
||||
break;
|
||||
default:
|
||||
output.push_back(str[i]);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, msdfgen::Range pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename) {
|
||||
if (fontCount <= 0)
|
||||
return false;
|
||||
double texelWidth = 1./atlasWidth;
|
||||
double texelHeight = 1./atlasHeight;
|
||||
bool anyGlyphs = false;
|
||||
FILE *file = fopen(outputFilename, "w");
|
||||
if (!file)
|
||||
return false;
|
||||
fprintf(file, shadronPreviewPreamble, atlasType == ImageType::HARD_MASK || atlasType == ImageType::SOFT_MASK ? shadronFillGlyphMask : shadronFillGlyphSdf);
|
||||
if (imageFilename)
|
||||
fprintf(file, "image Atlas = file(\"%s\")", relativizePath(outputFilename, imageFilename).c_str());
|
||||
fprintf(file, "image Atlas = file(\"%s\")", escapeString(relativizePath(outputFilename, imageFilename)).c_str());
|
||||
else
|
||||
fprintf(file, "image Atlas = file()");
|
||||
fprintf(file, " : filter(%s), map(repeat);\n", atlasType == ImageType::HARD_MASK ? "nearest" : "linear");
|
||||
fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n\n", pxRange*texelWidth, pxRange*texelHeight);
|
||||
fprintf(file, " : %sfilter(%s), map(repeat);\n", fullRange ? "full_range(true), " : "", atlasType == ImageType::HARD_MASK ? "nearest" : "linear");
|
||||
double pxRangeWidth = pxRange.upper-pxRange.lower;
|
||||
fprintf(file, "const vec2 txRange = vec2(%.9g, %.9g);\n", pxRangeWidth*texelWidth, pxRangeWidth*texelHeight);
|
||||
fprintf(file, "const float zeroDistanceValue = %.9g;\n\n", -pxRange.lower/(pxRange.upper-pxRange.lower));
|
||||
{
|
||||
msdfgen::FontMetrics fontMetrics;
|
||||
if (!msdfgen::getFontMetrics(fontMetrics, font))
|
||||
return false;
|
||||
msdfgen::FontMetrics fontMetrics = fonts->getMetrics();
|
||||
for (int i = 1; i < fontCount; ++i) {
|
||||
fontMetrics.lineHeight = std::max(fontMetrics.lineHeight, fonts[i].getMetrics().lineHeight);
|
||||
fontMetrics.ascenderY = std::max(fontMetrics.ascenderY, fonts[i].getMetrics().ascenderY);
|
||||
fontMetrics.descenderY = std::min(fontMetrics.descenderY, fonts[i].getMetrics().descenderY);
|
||||
}
|
||||
double fsScale = 1/(fontMetrics.ascenderY-fontMetrics.descenderY);
|
||||
fputs("vertex_list GlyphVertex textQuadVertices = {\n", file);
|
||||
double x = 0, y = -fsScale*fontMetrics.ascenderY;
|
||||
|
|
@ -107,13 +132,14 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
|
|||
y -= fsScale*fontMetrics.lineHeight;
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
if (glyphs[i].getCodepoint() == *cp) {
|
||||
if (!glyphs[i].isWhitespace()) {
|
||||
for (int i = 0; i < fontCount; ++i) {
|
||||
const GlyphGeometry *glyph = fonts[i].getGlyph(*cp);
|
||||
if (glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
double pl, pb, pr, pt;
|
||||
double il, ib, ir, it;
|
||||
glyphs[i].getQuadPlaneBounds(pl, pb, pr, pt);
|
||||
glyphs[i].getQuadAtlasBounds(il, ib, ir, it);
|
||||
glyph->getQuadPlaneBounds(pl, pb, pr, pt);
|
||||
glyph->getQuadAtlasBounds(il, ib, ir, it);
|
||||
pl *= fsScale, pb *= fsScale, pr *= fsScale, pt *= fsScale;
|
||||
pl += x, pb += y, pr += x, pt += y;
|
||||
il *= texelWidth, ib *= texelHeight, ir *= texelWidth, it *= texelHeight;
|
||||
|
|
@ -126,10 +152,10 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
|
|||
pr, pb, ir, ib
|
||||
);
|
||||
}
|
||||
x += fsScale*glyphs[i].getAdvance();
|
||||
double kerning;
|
||||
if (msdfgen::getKerning(kerning, font, cp[0], cp[1]))
|
||||
x += fsScale*kerning;
|
||||
double advance = glyph->getAdvance();
|
||||
fonts[i].getAdvance(advance, cp[0], cp[1]);
|
||||
x += fsScale*advance;
|
||||
anyGlyphs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -139,10 +165,10 @@ bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyp
|
|||
fputs("};\n", file);
|
||||
fprintf(file, "const vec2 textSize = vec2(%.9g, %.9g);\n\n", textWidth, -y);
|
||||
}
|
||||
fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file);
|
||||
fputs("PREVIEW_IMAGE(Preview, Atlas, txRange, zeroDistanceValue, vec3(1.0), textQuadVertices, textSize, ivec2(1200, 400));\n", file);
|
||||
fputs("export png(Preview, \"preview.png\");\n", file);
|
||||
fclose(file);
|
||||
return true;
|
||||
return anyGlyphs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "FontGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Generates a Shadron script that displays a string using the generated atlas
|
||||
bool generateShadronPreview(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, ImageType atlasType, int atlasWidth, int atlasHeight, double pxRange, const unicode_t *text, const char *imageFilename, const char *outputFilename);
|
||||
bool generateShadronPreview(const FontGeometry *fonts, int fontCount, ImageType atlasType, int atlasWidth, int atlasHeight, msdfgen::Range pxRange, const unicode_t *text, const char *imageFilename, bool fullRange, const char *outputFilename);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ SquareSizeSelector<MULTIPLE>::SquareSizeSelector(int minArea) : lowerBound(0), u
|
|||
template <int MULTIPLE>
|
||||
void SquareSizeSelector<MULTIPLE>::updateCurrent() {
|
||||
if (upperBound < 0)
|
||||
current = 5*lowerBound/4+16/MULTIPLE;
|
||||
current = 5*lowerBound/4+16/MULTIPLE+1;
|
||||
else
|
||||
current = lowerBound+(upperBound-lowerBound)/2;
|
||||
}
|
||||
|
|
@ -27,14 +27,14 @@ bool SquareSizeSelector<MULTIPLE>::operator()(int &width, int &height) const {
|
|||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
SquareSizeSelector<MULTIPLE> & SquareSizeSelector<MULTIPLE>::operator++() {
|
||||
SquareSizeSelector<MULTIPLE> &SquareSizeSelector<MULTIPLE>::operator++() {
|
||||
lowerBound = current+1;
|
||||
updateCurrent();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
SquareSizeSelector<MULTIPLE> & SquareSizeSelector<MULTIPLE>::operator--() {
|
||||
SquareSizeSelector<MULTIPLE> &SquareSizeSelector<MULTIPLE>::operator--() {
|
||||
upperBound = current;
|
||||
updateCurrent();
|
||||
return *this;
|
||||
|
|
@ -54,12 +54,12 @@ bool SquarePowerOfTwoSizeSelector::operator()(int &width, int &height) const {
|
|||
return side > 0;
|
||||
}
|
||||
|
||||
SquarePowerOfTwoSizeSelector & SquarePowerOfTwoSizeSelector::operator++() {
|
||||
SquarePowerOfTwoSizeSelector &SquarePowerOfTwoSizeSelector::operator++() {
|
||||
side <<= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SquarePowerOfTwoSizeSelector & SquarePowerOfTwoSizeSelector::operator--() {
|
||||
SquarePowerOfTwoSizeSelector &SquarePowerOfTwoSizeSelector::operator--() {
|
||||
side = 0;
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ bool PowerOfTwoSizeSelector::operator()(int &width, int &height) const {
|
|||
return w > 0 && h > 0;
|
||||
}
|
||||
|
||||
PowerOfTwoSizeSelector & PowerOfTwoSizeSelector::operator++() {
|
||||
PowerOfTwoSizeSelector &PowerOfTwoSizeSelector::operator++() {
|
||||
if (w == h)
|
||||
w <<= 1;
|
||||
else
|
||||
|
|
@ -82,7 +82,7 @@ PowerOfTwoSizeSelector & PowerOfTwoSizeSelector::operator++() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
PowerOfTwoSizeSelector & PowerOfTwoSizeSelector::operator--() {
|
||||
PowerOfTwoSizeSelector &PowerOfTwoSizeSelector::operator--() {
|
||||
w = 0, h = 0;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class SquareSizeSelector {
|
|||
public:
|
||||
explicit SquareSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
SquareSizeSelector<MULTIPLE> & operator++();
|
||||
SquareSizeSelector<MULTIPLE> & operator--();
|
||||
SquareSizeSelector<MULTIPLE> &operator++();
|
||||
SquareSizeSelector<MULTIPLE> &operator--();
|
||||
|
||||
private:
|
||||
int lowerBound, upperBound;
|
||||
|
|
@ -29,8 +29,8 @@ class SquarePowerOfTwoSizeSelector {
|
|||
public:
|
||||
explicit SquarePowerOfTwoSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
SquarePowerOfTwoSizeSelector & operator++();
|
||||
SquarePowerOfTwoSizeSelector & operator--();
|
||||
SquarePowerOfTwoSizeSelector &operator++();
|
||||
SquarePowerOfTwoSizeSelector &operator--();
|
||||
|
||||
private:
|
||||
int side;
|
||||
|
|
@ -43,8 +43,8 @@ class PowerOfTwoSizeSelector {
|
|||
public:
|
||||
explicit PowerOfTwoSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
PowerOfTwoSizeSelector & operator++();
|
||||
PowerOfTwoSizeSelector & operator--();
|
||||
PowerOfTwoSizeSelector &operator++();
|
||||
PowerOfTwoSizeSelector &operator--();
|
||||
|
||||
private:
|
||||
int w, h;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ enum class ImageType {
|
|||
SOFT_MASK,
|
||||
/// Signed (true) distance field
|
||||
SDF,
|
||||
/// Signed pseudo-distance field
|
||||
/// Signed perpendicular distance field
|
||||
PSDF,
|
||||
/// Multi-channel signed distance field
|
||||
MSDF,
|
||||
|
|
@ -30,6 +30,8 @@ enum class ImageFormat {
|
|||
PNG,
|
||||
BMP,
|
||||
TIFF,
|
||||
RGBA,
|
||||
FL32,
|
||||
TEXT,
|
||||
TEXT_FLOAT,
|
||||
BINARY,
|
||||
|
|
@ -43,4 +45,26 @@ enum class GlyphIdentifierType {
|
|||
UNICODE_CODEPOINT
|
||||
};
|
||||
|
||||
/// Direction of the Y-axis
|
||||
enum class YDirection {
|
||||
BOTTOM_UP,
|
||||
TOP_DOWN
|
||||
};
|
||||
|
||||
/// The method of computing the layout of the atlas
|
||||
enum class PackingStyle {
|
||||
TIGHT,
|
||||
GRID
|
||||
};
|
||||
|
||||
/// Constraints for the atlas's dimensions - see size selectors for more info
|
||||
enum class DimensionsConstraint {
|
||||
NONE,
|
||||
SQUARE,
|
||||
EVEN_SQUARE,
|
||||
MULTIPLE_OF_FOUR_SQUARE,
|
||||
POWER_OF_TWO_RECTANGLE,
|
||||
POWER_OF_TWO_SQUARE
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Floor positive integer to nearest lower or equal power of two
|
||||
inline int floorToPOT(int x) {
|
||||
int y = 1;
|
||||
while (x >= y && y<<1)
|
||||
y <<= 1;
|
||||
return y>>1;
|
||||
}
|
||||
|
||||
/// Ceil positive integer to nearest higher or equal power of two
|
||||
inline int ceilToPOT(int x) {
|
||||
int y = 1;
|
||||
while (x > y && y<<1)
|
||||
y <<= 1;
|
||||
return y;
|
||||
}
|
||||
|
||||
}
|
||||
2
msdfgen
2
msdfgen
|
|
@ -1 +1 @@
|
|||
Subproject commit 9d22335ea093422d7bf212d31bfaa6adcb9b89f0
|
||||
Subproject commit 03889564a50452fa2e0b0a60973b5057001b391b
|
||||
BIN
resource.h
BIN
resource.h
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/master/docs/vcpkg.schema.json",
|
||||
"name": "msdf-atlas-gen",
|
||||
"version": "1.3.0",
|
||||
"description": "Multi-channel signed distance field atlas generator",
|
||||
"homepage": "https://github.com/Chlumsky/msdf-atlas-gen",
|
||||
"license": "MIT",
|
||||
"dependencies": [
|
||||
"freetype",
|
||||
"libpng"
|
||||
],
|
||||
"default-features": [
|
||||
"geometry-preprocessing"
|
||||
],
|
||||
"features": {
|
||||
"geometry-preprocessing": {
|
||||
"description": "Preprocessing of non-compliant vector geometry via the Skia library",
|
||||
"dependencies": [ {
|
||||
"name": "skia",
|
||||
"default-features": false
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue