Version 1.0
This commit is contained in:
parent
d3a54faff6
commit
cad221afcd
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
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
|
||||
Binary file not shown.
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
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
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
<?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;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win32;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;%(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>
|
||||
</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;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win64;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;%(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>
|
||||
</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;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win32;msdfgen\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</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;%(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>
|
||||
</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;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>msdfgen\freetype\win64;msdfgen\$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</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;%(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>
|
||||
</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>
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<?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>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include "Remap.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
namespace {
|
||||
|
||||
/** Prototype of an atlas generator class.
|
||||
* An atlas generator maintains the atlas bitmap (AtlasStorage) and its layout and facilitates
|
||||
* generation of bitmap representation of glyphs. The layout of the atlas is given by the caller.
|
||||
*/
|
||||
class AtlasGenerator {
|
||||
|
||||
public:
|
||||
AtlasGenerator();
|
||||
AtlasGenerator(int width, int height);
|
||||
/// Generates bitmap representation for the supplied array of glyphs
|
||||
void generate(const GlyphGeometry *glyphs, int count);
|
||||
/// Resizes the atlas and rearranges the generated pixels according to the remapping array
|
||||
void rearrange(int width, int height, const Remap *remapping, int count);
|
||||
/// Resizes the atlas and keeps the generated pixels in place
|
||||
void resize(int width, int height);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// Configuration of signed distance field generator
|
||||
struct GeneratorAttributes {
|
||||
bool overlapSupport = true;
|
||||
bool scanlinePass = true;
|
||||
double errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
|
||||
};
|
||||
|
||||
/// A function that generates the bitmap for a single glyph
|
||||
template <typename T, int N>
|
||||
using GeneratorFunction = void (*)(const msdfgen::BitmapRef<T, N> &, const GlyphGeometry &, const GeneratorAttributes &);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include "Remap.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
namespace {
|
||||
|
||||
/** Prototype of an atlas storage class.
|
||||
* An atlas storage physically holds the pixels of the atlas
|
||||
* and allows to read and write subsections represented as bitmaps.
|
||||
* Can be implemented using a simple bitmap (BitmapAtlasStorage),
|
||||
* as texture memory, or any other way.
|
||||
*/
|
||||
class AtlasStorage {
|
||||
|
||||
public:
|
||||
AtlasStorage();
|
||||
AtlasStorage(int width, int height);
|
||||
/// Creates a copy with different dimensions
|
||||
AtlasStorage(const AtlasStorage &orig, int width, int height);
|
||||
/// Creates a copy with different dimensions and rearranges the pixels according to the remapping array
|
||||
AtlasStorage(const AtlasStorage &orig, int width, int height, const Remap *remapping, int count);
|
||||
/// Stores a subsection at x, y into the atlas storage. May be implemented for only some T, N
|
||||
template <typename T, int N>
|
||||
void put(int x, int y, const msdfgen::BitmapConstRef<T, N> &subBitmap);
|
||||
/// Retrieves a subsection at x, y from the atlas storage. May be implemented for only some T, N
|
||||
template <typename T, int N>
|
||||
void get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "AtlasStorage.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// An implementation of AtlasStorage represented by a bitmap in memory (msdfgen::Bitmap)
|
||||
template <typename T, int N>
|
||||
class BitmapAtlasStorage {
|
||||
|
||||
public:
|
||||
BitmapAtlasStorage();
|
||||
BitmapAtlasStorage(int width, int height);
|
||||
explicit BitmapAtlasStorage(const msdfgen::BitmapConstRef<T, N> &bitmap);
|
||||
explicit BitmapAtlasStorage(msdfgen::Bitmap<T, N> &&bitmap);
|
||||
BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height);
|
||||
BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height, const Remap *remapping, int count);
|
||||
operator msdfgen::BitmapConstRef<T, N>() const;
|
||||
operator msdfgen::BitmapRef<T, N>();
|
||||
operator msdfgen::Bitmap<T, N>() &&;
|
||||
template <typename S>
|
||||
void put(int x, int y, const msdfgen::BitmapConstRef<S, N> &subBitmap);
|
||||
void get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const;
|
||||
|
||||
private:
|
||||
msdfgen::Bitmap<T, N> bitmap;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "BitmapAtlasStorage.hpp"
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
#include "BitmapAtlasStorage.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "bitmap-blit.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage() { }
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(int width, int height) : bitmap(width, height) {
|
||||
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const msdfgen::BitmapConstRef<T, N> &bitmap) : bitmap(bitmap) { }
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(msdfgen::Bitmap<T, N> &&bitmap) : bitmap((msdfgen::Bitmap<T, N> &&) bitmap) { }
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height) : bitmap(width, height) {
|
||||
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
|
||||
blit(bitmap, orig.bitmap, 0, 0, 0, 0, std::min(width, orig.bitmap.width()), std::min(height, orig.bitmap.height()));
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::BitmapAtlasStorage(const BitmapAtlasStorage<T, N> &orig, int width, int height, const Remap *remapping, int count) : bitmap(width, height) {
|
||||
memset((T *) bitmap, 0, sizeof(T)*N*width*height);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const Remap &remap = remapping[i];
|
||||
blit(bitmap, orig.bitmap, remap.target.x, remap.target.y, remap.source.x, remap.source.y, remap.width, remap.height);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::operator msdfgen::BitmapConstRef<T, N>() const {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::operator msdfgen::BitmapRef<T, N>() {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
BitmapAtlasStorage<T, N>::operator msdfgen::Bitmap<T, N>() && {
|
||||
return (msdfgen::Bitmap<T, N>() &&) bitmap;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename S>
|
||||
void BitmapAtlasStorage<T, N>::put(int x, int y, const msdfgen::BitmapConstRef<S, N> &subBitmap) {
|
||||
blit(bitmap, subBitmap, x, y, 0, 0, subBitmap.width, subBitmap.height);
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
void BitmapAtlasStorage<T, N>::get(int x, int y, const msdfgen::BitmapRef<T, N> &subBitmap) const {
|
||||
blit(subBitmap, bitmap, 0, 0, x, y, subBitmap.width, subBitmap.height);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
#include "Charset.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static Charset createAsciiCharset() {
|
||||
Charset ascii;
|
||||
for (unicode_t cp = 0x20; cp < 0x7f; ++cp)
|
||||
ascii.add(cp);
|
||||
return ascii;
|
||||
}
|
||||
|
||||
const Charset Charset::ASCII = createAsciiCharset();
|
||||
|
||||
void Charset::add(unicode_t cp) {
|
||||
codepoints.insert(cp);
|
||||
}
|
||||
|
||||
void Charset::remove(unicode_t cp) {
|
||||
codepoints.erase(cp);
|
||||
}
|
||||
|
||||
size_t Charset::size() const {
|
||||
return codepoints.size();
|
||||
}
|
||||
|
||||
bool Charset::empty() const {
|
||||
return codepoints.empty();
|
||||
}
|
||||
|
||||
std::set<unicode_t>::const_iterator Charset::begin() const {
|
||||
return codepoints.begin();
|
||||
}
|
||||
|
||||
std::set<unicode_t>::const_iterator Charset::end() const {
|
||||
return codepoints.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <set>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represents a set of Unicode codepoints (characters)
|
||||
class Charset {
|
||||
|
||||
public:
|
||||
/// The set of the 95 printable ASCII characters
|
||||
static const Charset ASCII;
|
||||
|
||||
/// Adds a codepoint
|
||||
void add(unicode_t cp);
|
||||
/// Removes a codepoint
|
||||
void remove(unicode_t cp);
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
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
|
||||
bool load(const char *filename);
|
||||
|
||||
private:
|
||||
std::set<unicode_t> codepoints;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "RectanglePacker.h"
|
||||
#include "AtlasGenerator.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* This class can be used to produce a dynamic atlas to which more glyphs are added over time.
|
||||
* It takes care of laying out and enlarging the atlas as necessary and delegates the actual work
|
||||
* to the specified AtlasGenerator, which may e.g. do the work asynchronously.
|
||||
*/
|
||||
template <class AtlasGenerator>
|
||||
class DynamicAtlas {
|
||||
|
||||
public:
|
||||
DynamicAtlas();
|
||||
/// 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);
|
||||
/// Allows access to generator. Do not add glyphs to the generator directly!
|
||||
AtlasGenerator & atlasGenerator();
|
||||
const AtlasGenerator & atlasGenerator() const;
|
||||
|
||||
private:
|
||||
AtlasGenerator generator;
|
||||
RectanglePacker packer;
|
||||
int glyphCount;
|
||||
int side;
|
||||
std::vector<Rectangle> rectangles;
|
||||
std::vector<Remap> remapBuffer;
|
||||
int totalArea;
|
||||
GeneratorAttributes genAttribs;
|
||||
int padding;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "DynamicAtlas.hpp"
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
#include "DynamicAtlas.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <class AtlasGenerator>
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas() : glyphCount(0), side(0), totalArea(0), padding(0) { }
|
||||
|
||||
template <class AtlasGenerator>
|
||||
DynamicAtlas<AtlasGenerator>::DynamicAtlas(AtlasGenerator &&generator) : generator((AtlasGenerator &&) generator), glyphCount(0), side(0), totalArea(0), padding(0) { }
|
||||
|
||||
template <class AtlasGenerator>
|
||||
void DynamicAtlas<AtlasGenerator>::add(GlyphGeometry *glyphs, int count) {
|
||||
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 };
|
||||
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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
while (side*side < totalArea)
|
||||
side <<= 1;
|
||||
packer = RectanglePacker(side+padding, side+padding);
|
||||
packerStart = 0;
|
||||
}
|
||||
if (packerStart < start) {
|
||||
for (int i = 0; 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)
|
||||
generator.resize(side, side);
|
||||
for (int i = start; i < (int) rectangles.size(); ++i) {
|
||||
remapBuffer[i].target.x = rectangles[i].x;
|
||||
remapBuffer[i].target.y = rectangles[i].y;
|
||||
glyphs[remapBuffer[i].index-glyphCount].placeBox(rectangles[i].x, rectangles[i].y);
|
||||
}
|
||||
}
|
||||
generator.generate(glyphs, count, genAttribs);
|
||||
glyphCount += count;
|
||||
}
|
||||
|
||||
template <class AtlasGenerator>
|
||||
AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() {
|
||||
return generator;
|
||||
}
|
||||
|
||||
template <class AtlasGenerator>
|
||||
const AtlasGenerator & DynamicAtlas<AtlasGenerator>::atlasGenerator() const {
|
||||
return generator;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// The glyph box - its bounds in plane and atlas
|
||||
struct GlyphBox {
|
||||
unicode_t codepoint;
|
||||
double advance;
|
||||
struct {
|
||||
double l, b, r, t;
|
||||
} bounds;
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
} rect;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
GlyphGeometry::GlyphGeometry() : codepoint(), bounds(), reverseWinding(), advance(), box() { }
|
||||
|
||||
double GlyphGeometry::simpleSignedDistance(const msdfgen::Point2 &p) const {
|
||||
double dummy;
|
||||
msdfgen::SignedDistance minDistance;
|
||||
for (const msdfgen::Contour &contour : shape.contours)
|
||||
for (const msdfgen::EdgeHolder &edge : contour.edges) {
|
||||
msdfgen::SignedDistance distance = edge->signedDistance(p, dummy);
|
||||
if (distance < minDistance)
|
||||
minDistance = distance;
|
||||
}
|
||||
return minDistance.distance;
|
||||
}
|
||||
|
||||
bool GlyphGeometry::load(msdfgen::FontHandle *font, unicode_t codepoint) {
|
||||
if (font && msdfgen::loadGlyph(shape, font, codepoint, &advance) && shape.validate()) {
|
||||
this->codepoint = codepoint;
|
||||
shape.normalize();
|
||||
bounds = shape.getBounds();
|
||||
msdfgen::Point2 outerPoint(bounds.l-(bounds.r-bounds.l)-1, bounds.b-(bounds.t-bounds.b)-1);
|
||||
reverseWinding = simpleSignedDistance(outerPoint) > 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GlyphGeometry::edgeColoring(double angleThreshold, unsigned long long seed) {
|
||||
msdfgen::edgeColoringInkTrap(shape, angleThreshold, seed);
|
||||
}
|
||||
|
||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
|
||||
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, reverseWinding ? -1 : +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;
|
||||
} else {
|
||||
box.rect.w = 0, box.rect.h = 0;
|
||||
box.translate = msdfgen::Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
void GlyphGeometry::placeBox(int x, int y) {
|
||||
box.rect.x = x, box.rect.y = y;
|
||||
}
|
||||
|
||||
unicode_t GlyphGeometry::getCodepoint() const {
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
const msdfgen::Shape & GlyphGeometry::getShape() const {
|
||||
return shape;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getAdvance() const {
|
||||
return advance;
|
||||
}
|
||||
|
||||
bool GlyphGeometry::isWindingReverse() const {
|
||||
return reverseWinding;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void GlyphGeometry::getBoxSize(int &w, int &h) const {
|
||||
w = box.rect.w, h = box.rect.h;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getBoxRange() const {
|
||||
return box.range;
|
||||
}
|
||||
|
||||
double GlyphGeometry::getBoxScale() const {
|
||||
return box.scale;
|
||||
}
|
||||
|
||||
msdfgen::Vector2 GlyphGeometry::getBoxTranslate() const {
|
||||
return box.translate;
|
||||
}
|
||||
|
||||
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;
|
||||
} 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;
|
||||
} else
|
||||
l = 0, b = 0, r = 0, t = 0;
|
||||
}
|
||||
|
||||
bool GlyphGeometry::isWhitespace() const {
|
||||
return shape.contours.empty();
|
||||
}
|
||||
|
||||
GlyphGeometry::operator GlyphBox() const {
|
||||
GlyphBox box;
|
||||
box.codepoint = codepoint;
|
||||
box.advance = advance;
|
||||
getQuadPlaneBounds(box.bounds.l, box.bounds.b, box.bounds.r, box.bounds.t);
|
||||
box.rect.x = this->box.rect.x, box.rect.y = this->box.rect.y, box.rect.w = this->box.rect.w, box.rect.h = this->box.rect.h;
|
||||
return box;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphBox.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represent's the shape geometry of a single glyph as well as its configuration
|
||||
class GlyphGeometry {
|
||||
|
||||
public:
|
||||
GlyphGeometry();
|
||||
/// Loads glyph geometry from font
|
||||
bool load(msdfgen::FontHandle *font, unicode_t codepoint);
|
||||
/// Applies edge coloring to glyph shape
|
||||
void edgeColoring(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);
|
||||
/// Sets the glyph's box's position in the atlas
|
||||
void placeBox(int x, int y);
|
||||
/// Returns the glyph's Unicode index
|
||||
unicode_t getCodepoint() const;
|
||||
/// Returns the glyph's shape
|
||||
const msdfgen::Shape & getShape() const;
|
||||
/// Returns the glyph's advance
|
||||
double getAdvance() const;
|
||||
/// Returns true if the shape has reverse winding
|
||||
bool isWindingReverse() 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;
|
||||
/// Returns the scale needed to generate the glyph's bitmap
|
||||
double getBoxScale() const;
|
||||
/// Returns the translation vector needed to generate the glyph's bitmap
|
||||
msdfgen::Vector2 getBoxTranslate() const;
|
||||
/// Outputs the bounding box of the glyph as it should be placed on the baseline
|
||||
void getQuadPlaneBounds(double &l, double &b, double &r, double &t) const;
|
||||
/// Outputs the bounding box of the glyph in the atlas
|
||||
void getQuadAtlasBounds(double &l, double &b, double &r, double &t) const;
|
||||
/// Returns true if the glyph is a whitespace and has no geometry
|
||||
bool isWhitespace() const;
|
||||
/// Simplifies to GlyphBox
|
||||
operator GlyphBox() const;
|
||||
|
||||
private:
|
||||
unicode_t codepoint;
|
||||
msdfgen::Shape shape;
|
||||
msdfgen::Shape::Bounds bounds;
|
||||
bool reverseWinding;
|
||||
double advance;
|
||||
struct {
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
} rect;
|
||||
double range;
|
||||
double scale;
|
||||
msdfgen::Vector2 translate;
|
||||
} box;
|
||||
|
||||
/// Computes the signed distance from point p in a naive way
|
||||
double simpleSignedDistance(const msdfgen::Point2 &p) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "GlyphBox.h"
|
||||
#include "Workload.h"
|
||||
#include "AtlasGenerator.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* An implementation of AtlasGenerator that uses the specified generator function
|
||||
* and AtlasStorage class and generates glyph bitmaps immediately
|
||||
* (does not return until all submitted work is finished),
|
||||
* but may use multiple threads (setThreadCount).
|
||||
*/
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
class ImmediateAtlasGenerator {
|
||||
|
||||
public:
|
||||
ImmediateAtlasGenerator();
|
||||
ImmediateAtlasGenerator(int width, int height);
|
||||
void generate(const GlyphGeometry *glyphs, int count);
|
||||
void rearrange(int width, int height, const Remap *remapping, int count);
|
||||
void resize(int width, int height);
|
||||
/// Sets attributes for the generator function
|
||||
void setAttributes(const GeneratorAttributes &attributes);
|
||||
/// Sets the number of threads to be run by generate
|
||||
void setThreadCount(int threadCount);
|
||||
/// Allows access to the underlying AtlasStorage
|
||||
const AtlasStorage & atlasStorage() const;
|
||||
|
||||
private:
|
||||
AtlasStorage storage;
|
||||
std::vector<GlyphBox> layout;
|
||||
std::vector<T> glyphBuffer;
|
||||
GeneratorAttributes attributes;
|
||||
int threadCount;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "ImmediateAtlasGenerator.hpp"
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
|
||||
#include "ImmediateAtlasGenerator.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::ImmediateAtlasGenerator() : threadCount(1) { }
|
||||
|
||||
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>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::generate(const GlyphGeometry *glyphs, int count) {
|
||||
int maxBoxArea = 0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
GlyphBox box = glyphs[i];
|
||||
maxBoxArea = std::max(maxBoxArea, box.rect.w*box.rect.h);
|
||||
layout.push_back((GlyphBox &&) box);
|
||||
}
|
||||
int threadBufferSize = N*maxBoxArea;
|
||||
if (threadCount*threadBufferSize > (int) glyphBuffer.size())
|
||||
glyphBuffer.resize(threadCount*threadBufferSize);
|
||||
|
||||
Workload([this, &glyphs, 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);
|
||||
storage.put(l, b, msdfgen::BitmapConstRef<T, N>(glyphBitmap));
|
||||
}
|
||||
return true;
|
||||
}, count).finish(threadCount);
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::rearrange(int width, int height, const Remap *remapping, int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
layout[remapping[i].index].rect.x = remapping[i].target.x;
|
||||
layout[remapping[i].index].rect.y = remapping[i].target.y;
|
||||
}
|
||||
AtlasStorage newStorage((AtlasStorage &&) storage, width, height, remapping, count);
|
||||
storage = (AtlasStorage &&) newStorage;
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::resize(int width, int height) {
|
||||
AtlasStorage newStorage((AtlasStorage &&) storage, width, height);
|
||||
storage = (AtlasStorage &&) newStorage;
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::setAttributes(const GeneratorAttributes &attributes) {
|
||||
this->attributes = attributes;
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
void ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::setThreadCount(int threadCount) {
|
||||
this->threadCount = threadCount;
|
||||
}
|
||||
|
||||
template <typename T, int N, GeneratorFunction<T, N> GEN_FN, class AtlasStorage>
|
||||
const AtlasStorage & ImmediateAtlasGenerator<T, N, GEN_FN, AtlasStorage>::atlasStorage() const {
|
||||
return storage;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
struct Rectangle {
|
||||
int x, y, w, h;
|
||||
};
|
||||
|
||||
struct OrientedRectangle : Rectangle {
|
||||
bool rotated;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
#include "RectanglePacker.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
#define WORST_FIT 0x7fffffff
|
||||
|
||||
template <typename T>
|
||||
static void removeFromUnorderedVector(std::vector<T> &vector, size_t index) {
|
||||
if (index != vector.size()-1)
|
||||
std::swap(vector[index], vector.back());
|
||||
vector.pop_back();
|
||||
}
|
||||
|
||||
int RectanglePacker::rateFit(int w, int h, int sw, int sh) {
|
||||
return std::min(sw-w, sh-h);
|
||||
}
|
||||
|
||||
RectanglePacker::RectanglePacker() : RectanglePacker(0, 0) { }
|
||||
|
||||
RectanglePacker::RectanglePacker(int width, int height) {
|
||||
if (width > 0 && height > 0)
|
||||
spaces.push_back(Rectangle { 0, 0, width, height });
|
||||
}
|
||||
|
||||
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))
|
||||
a.w = space.w;
|
||||
else
|
||||
b.h = space.h;
|
||||
if (a.w > 0 && a.h > 0)
|
||||
spaces.push_back(a);
|
||||
if (b.w > 0 && b.h > 0)
|
||||
spaces.push_back(b);
|
||||
}
|
||||
|
||||
int RectanglePacker::pack(Rectangle *rectangles, int count) {
|
||||
std::vector<int> remainingRects(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
remainingRects[i] = i;
|
||||
while (!remainingRects.empty()) {
|
||||
int bestFit = WORST_FIT;
|
||||
int bestSpace = -1;
|
||||
int bestRect = -1;
|
||||
for (size_t i = 0; i < spaces.size(); ++i) {
|
||||
const Rectangle &space = spaces[i];
|
||||
for (size_t j = 0; j < remainingRects.size(); ++j) {
|
||||
const Rectangle &rect = rectangles[remainingRects[j]];
|
||||
if (rect.w == space.w && rect.h == space.h) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
goto BEST_FIT_FOUND;
|
||||
}
|
||||
if (rect.w <= space.w && rect.h <= space.h) {
|
||||
int fit = rateFit(rect.w, rect.h, space.w, space.h);
|
||||
if (fit < bestFit) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
bestFit = fit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestSpace < 0 || bestRect < 0)
|
||||
break;
|
||||
BEST_FIT_FOUND:
|
||||
Rectangle &rect = rectangles[remainingRects[bestRect]];
|
||||
rect.x = spaces[bestSpace].x;
|
||||
rect.y = spaces[bestSpace].y;
|
||||
splitSpace(bestSpace, rect.w, rect.h);
|
||||
removeFromUnorderedVector(remainingRects, bestRect);
|
||||
}
|
||||
return (int) remainingRects.size();
|
||||
}
|
||||
|
||||
int RectanglePacker::pack(OrientedRectangle *rectangles, int count) {
|
||||
std::vector<int> remainingRects(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
remainingRects[i] = i;
|
||||
while (!remainingRects.empty()) {
|
||||
int bestFit = WORST_FIT;
|
||||
int bestSpace = -1;
|
||||
int bestRect = -1;
|
||||
bool bestRotated = false;
|
||||
for (size_t i = 0; i < spaces.size(); ++i) {
|
||||
const Rectangle &space = spaces[i];
|
||||
for (size_t j = 0; j < remainingRects.size(); ++j) {
|
||||
const OrientedRectangle &rect = rectangles[remainingRects[j]];
|
||||
if (rect.w == space.w && rect.h == space.h) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
bestRotated = false;
|
||||
goto BEST_FIT_FOUND;
|
||||
}
|
||||
if (rect.h == space.w && rect.w == space.h) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
bestRotated = true;
|
||||
goto BEST_FIT_FOUND;
|
||||
}
|
||||
if (rect.w <= space.w && rect.h <= space.h) {
|
||||
int fit = rateFit(rect.w, rect.h, space.w, space.h);
|
||||
if (fit < bestFit) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
bestRotated = false;
|
||||
bestFit = fit;
|
||||
}
|
||||
}
|
||||
if (rect.h <= space.w && rect.w <= space.h) {
|
||||
int fit = rateFit(rect.h, rect.w, space.w, space.h);
|
||||
if (fit < bestFit) {
|
||||
bestSpace = i;
|
||||
bestRect = j;
|
||||
bestRotated = true;
|
||||
bestFit = fit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestSpace < 0 || bestRect < 0)
|
||||
break;
|
||||
BEST_FIT_FOUND:
|
||||
OrientedRectangle &rect = rectangles[remainingRects[bestRect]];
|
||||
rect.x = spaces[bestSpace].x;
|
||||
rect.y = spaces[bestSpace].y;
|
||||
rect.rotated = bestRotated;
|
||||
if (bestRotated)
|
||||
splitSpace(bestSpace, rect.h, rect.w);
|
||||
else
|
||||
splitSpace(bestSpace, rect.w, rect.h);
|
||||
removeFromUnorderedVector(remainingRects, bestRect);
|
||||
}
|
||||
return (int) remainingRects.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "Rectangle.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Guillotine 2D single bin packer
|
||||
class RectanglePacker {
|
||||
|
||||
public:
|
||||
RectanglePacker();
|
||||
RectanglePacker(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);
|
||||
|
||||
private:
|
||||
std::vector<Rectangle> spaces;
|
||||
|
||||
static int rateFit(int w, int h, int sw, int sh);
|
||||
|
||||
void splitSpace(int index, int w, int h);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Represents the repositioning of a subsection of the atlas
|
||||
struct Remap {
|
||||
int index;
|
||||
struct {
|
||||
int x, y;
|
||||
} source, target;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
|
||||
#include "TightAtlasPacker.h"
|
||||
|
||||
#include <vector>
|
||||
#include "Rectangle.h"
|
||||
#include "rectangle-packing.h"
|
||||
#include "size-selectors.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
int TightAtlasPacker::tryPack(GlyphGeometry *glyphs, int count, DimensionsConstraint dimensionsConstraint, int &width, int &height, int padding, double scale, double range, double miterLimit) {
|
||||
// Wrap glyphs into boxes
|
||||
std::vector<Rectangle> rectangles;
|
||||
std::vector<GlyphGeometry *> rectangleGlyphs;
|
||||
rectangles.reserve(count);
|
||||
rectangleGlyphs.reserve(count);
|
||||
for (GlyphGeometry *glyph = glyphs, *end = glyphs+count; glyph < end; ++glyph) {
|
||||
if (!glyph->isWhitespace()) {
|
||||
Rectangle rect = { };
|
||||
glyph->wrapBox(scale, range, miterLimit);
|
||||
glyph->getBoxSize(rect.w, rect.h);
|
||||
if (rect.w > 0 && rect.h > 0) {
|
||||
rectangles.push_back(rect);
|
||||
rectangleGlyphs.push_back(glyph);
|
||||
}
|
||||
}
|
||||
}
|
||||
// No non-zero size boxes?
|
||||
if (rectangles.empty()) {
|
||||
if (width < 0 || height < 0)
|
||||
width = 0, height = 0;
|
||||
return 0;
|
||||
}
|
||||
// Box rectangle packing
|
||||
if (width < 0 || height < 0) {
|
||||
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);
|
||||
break;
|
||||
case DimensionsConstraint::POWER_OF_TWO_RECTANGLE:
|
||||
dimensions = packRectangles<PowerOfTwoSizeSelector>(rectangles.data(), rectangles.size(), padding);
|
||||
break;
|
||||
case DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<4> >(rectangles.data(), rectangles.size(), padding);
|
||||
break;
|
||||
case DimensionsConstraint::EVEN_SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<2> >(rectangles.data(), rectangles.size(), padding);
|
||||
break;
|
||||
case DimensionsConstraint::SQUARE:
|
||||
dimensions = packRectangles<SquareSizeSelector<> >(rectangles.data(), rectangles.size(), padding);
|
||||
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))
|
||||
return result;
|
||||
}
|
||||
// Set glyph box placement
|
||||
for (size_t i = 0; i < rectangles.size(); ++i)
|
||||
rectangleGlyphs[i]->placeBox(rectangles[i].x, height-(rectangles[i].y+rectangles[i].h));
|
||||
return 0;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::packAndScale(GlyphGeometry *glyphs, int count, int width, int height, int padding, double unitRange, double pxRange, double miterLimit, double tolerance) {
|
||||
bool lastResult = false;
|
||||
#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, DimensionsConstraint(), width, height, padding, (scale), unitRange+pxRange/(scale), miterLimit))
|
||||
double minScale = 1, maxScale = 1;
|
||||
if (TRY_PACK(1)) {
|
||||
while (maxScale < 1e+32 && TRY_PACK(maxScale = 2*minScale))
|
||||
minScale = maxScale;
|
||||
} else {
|
||||
while (minScale > 1e-32 && !TRY_PACK(minScale = .5*maxScale))
|
||||
maxScale = minScale;
|
||||
}
|
||||
if (minScale == maxScale)
|
||||
return 0;
|
||||
while (minScale/maxScale < 1-tolerance) {
|
||||
double midScale = .5*(minScale+maxScale);
|
||||
if (TRY_PACK(midScale))
|
||||
minScale = midScale;
|
||||
else
|
||||
maxScale = midScale;
|
||||
}
|
||||
if (!lastResult)
|
||||
TRY_PACK(minScale);
|
||||
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))
|
||||
return remaining;
|
||||
} else if (width < 0 || height < 0)
|
||||
return -1;
|
||||
if (scale <= 0)
|
||||
scale = packAndScale(glyphs, count, width, height, padding, unitRange, pxRange, miterLimit, scaleMaximizationTolerance);
|
||||
if (scale <= 0)
|
||||
return -1;
|
||||
pxRange += scale*unitRange;
|
||||
unitRange = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setDimensions(int width, int height) {
|
||||
this->width = width, this->height = height;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::unsetDimensions() {
|
||||
width = -1, height = -1;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setDimensionsConstraint(DimensionsConstraint dimensionsConstraint) {
|
||||
this->dimensionsConstraint = dimensionsConstraint;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setPadding(int padding) {
|
||||
this->padding = padding;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setScale(double scale) {
|
||||
this->scale = scale;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setMinimumScale(double minScale) {
|
||||
this->minScale = minScale;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setUnitRange(double unitRange) {
|
||||
this->unitRange = unitRange;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setPixelRange(double pxRange) {
|
||||
this->pxRange = pxRange;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::setMiterLimit(double miterLimit) {
|
||||
this->miterLimit = miterLimit;
|
||||
}
|
||||
|
||||
void TightAtlasPacker::getDimensions(int &width, int &height) const {
|
||||
width = this->width, height = this->height;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::getScale() const {
|
||||
return scale;
|
||||
}
|
||||
|
||||
double TightAtlasPacker::getPixelRange() const {
|
||||
return pxRange;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* This class computes the layout of a static 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
|
||||
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 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);
|
||||
/// Sets the pixel component of the total distance range
|
||||
void setPixelRange(double pxRange);
|
||||
/// Sets the miter limit for bounds computation
|
||||
void setMiterLimit(double miterLimit);
|
||||
|
||||
/// 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;
|
||||
|
||||
private:
|
||||
int width, height;
|
||||
int padding;
|
||||
DimensionsConstraint dimensionsConstraint;
|
||||
double scale;
|
||||
double minScale;
|
||||
double unitRange;
|
||||
double pxRange;
|
||||
double miterLimit;
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
#include "Workload.h"
|
||||
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
Workload::Workload() : chunks(0) { }
|
||||
|
||||
Workload::Workload(const std::function<bool(int, int)> &workerFunction, int chunks) : workerFunction(workerFunction), chunks(chunks) { }
|
||||
|
||||
bool Workload::finishSequential() {
|
||||
for (int i = 0; i < chunks; ++i)
|
||||
if (!workerFunction(i, 0))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Workload::finishParallel(int threadCount) {
|
||||
bool result = true;
|
||||
std::atomic<int> next(0);
|
||||
std::function<void(int)> threadWorker = [this, &result, &next](int threadNo) {
|
||||
for (int i = next++; result && i < chunks; i = next++) {
|
||||
if (!workerFunction(i, threadNo))
|
||||
result = false;
|
||||
}
|
||||
};
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(threadCount);
|
||||
for (int i = 0; i < threadCount; ++i)
|
||||
threads.emplace_back(threadWorker, i);
|
||||
for (std::thread &thread : threads)
|
||||
thread.join();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Workload::finish(int threadCount) {
|
||||
if (!chunks)
|
||||
return true;
|
||||
if (threadCount == 1 || chunks == 1)
|
||||
return finishSequential();
|
||||
if (threadCount > 1)
|
||||
return finishParallel(threadCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* This function allows to split a workload into multiple threads.
|
||||
* The worker function:
|
||||
* bool FN(int chunk, int threadNo);
|
||||
* should process the given chunk (out of chunks) and return true.
|
||||
* If false is returned, the process is interrupted.
|
||||
*/
|
||||
class Workload {
|
||||
|
||||
public:
|
||||
Workload();
|
||||
Workload(const std::function<bool(int, int)> &workerFunction, int chunks);
|
||||
/// Runs the process and returns true if all chunks have been processed
|
||||
bool finish(int threadCount);
|
||||
|
||||
private:
|
||||
std::function<bool(int, int)> workerFunction;
|
||||
int chunks;
|
||||
|
||||
bool finishSequential();
|
||||
bool finishParallel(int threadCount);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
#include "artery-font-export.h"
|
||||
|
||||
#include <artery-font/std-artery-font.h>
|
||||
#include <artery-font/stdio-serialization.h>
|
||||
#include "image-encode.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static artery_font::ImageType convertImageType(ImageType imageType) {
|
||||
switch (imageType) {
|
||||
case ImageType::HARD_MASK:
|
||||
case ImageType::SOFT_MASK:
|
||||
return artery_font::IMAGE_LINEAR_MASK;
|
||||
case ImageType::SDF:
|
||||
return artery_font::IMAGE_SDF;
|
||||
case ImageType::PSDF:
|
||||
return artery_font::IMAGE_PSDF;
|
||||
case ImageType::MSDF:
|
||||
return artery_font::IMAGE_MSDF;
|
||||
case ImageType::MTSDF:
|
||||
return artery_font::IMAGE_MTSDF;
|
||||
}
|
||||
return artery_font::IMAGE_NONE;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
static bool encodeTiff(std::vector<byte> &output, const msdfgen::BitmapConstRef<T, N> &atlas) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static artery_font::PixelFormat getPixelFormat();
|
||||
|
||||
template <>
|
||||
artery_font::PixelFormat getPixelFormat<byte>() {
|
||||
return artery_font::PIXEL_UNSIGNED8;
|
||||
}
|
||||
template <>
|
||||
artery_font::PixelFormat getPixelFormat<float>() {
|
||||
return artery_font::PIXEL_FLOAT32;
|
||||
}
|
||||
|
||||
template <typename REAL, typename T, int N>
|
||||
bool exportArteryFont(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<T, N> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename) {
|
||||
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 = artery_font::CP_UNICODE;
|
||||
fontVariant.imageType = convertImageType(imageType);
|
||||
fontVariant.metrics.fontSize = REAL(fontSize);
|
||||
if (imageType != ImageType::HARD_MASK)
|
||||
fontVariant.metrics.distanceRange = REAL(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].getCodepoint();
|
||||
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);
|
||||
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.v = REAL(0);
|
||||
for (int j = 0; j < glyphCount; ++j) {
|
||||
double kerning;
|
||||
if (msdfgen::getKerning(kerning, font, glyphs[i].getCodepoint(), glyphs[j].getCodepoint()) && kerning) {
|
||||
artery_font::KernPair<REAL> kernPair = { };
|
||||
kernPair.codepoint1 = glyphs[i].getCodepoint();
|
||||
kernPair.codepoint2 = glyphs[j].getCodepoint();
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
artery_font::StdImage image = { };
|
||||
image.width = atlas.width;
|
||||
image.height = atlas.height;
|
||||
image.channels = N;
|
||||
image.imageType = convertImageType(imageType);
|
||||
switch (imageFormat) {
|
||||
case ImageFormat::PNG:
|
||||
image.encoding = artery_font::IMAGE_PNG;
|
||||
image.pixelFormat = artery_font::PIXEL_UNSIGNED8;
|
||||
if (!encodePng(image.data.vector, atlas))
|
||||
return false;
|
||||
break;
|
||||
case ImageFormat::TIFF:
|
||||
image.encoding = artery_font::IMAGE_TIFF;
|
||||
image.pixelFormat = artery_font::PIXEL_FLOAT32;
|
||||
if (!encodeTiff(image.data.vector, atlas))
|
||||
return false;
|
||||
break;
|
||||
case ImageFormat::BINARY:
|
||||
image.pixelFormat = artery_font::PIXEL_UNSIGNED8;
|
||||
goto BINARY_EITHER;
|
||||
case ImageFormat::BINARY_FLOAT:
|
||||
image.pixelFormat = artery_font::PIXEL_FLOAT32;
|
||||
goto BINARY_EITHER;
|
||||
BINARY_EITHER:
|
||||
if (image.pixelFormat != getPixelFormat<T>())
|
||||
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);
|
||||
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, double fontSize, double pxRange, const msdfgen::BitmapConstRef<byte, 1> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<byte, 3> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<byte, 4> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<float, 1> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<float, 3> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
template bool exportArteryFont<float>(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, double fontSize, double pxRange, const msdfgen::BitmapConstRef<float, 4> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// 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, double fontSize, double pxRange, const msdfgen::BitmapConstRef<T, N> &atlas, ImageType imageType, ImageFormat imageFormat, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
#include "bitmap-blit.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
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) {
|
||||
for (int y = 0; y < h; ++y)
|
||||
memcpy(dst(dx, dy+y), src(sx, sy+y), sizeof(T)*N*w);
|
||||
}
|
||||
|
||||
#define BLIT_SAME_TYPE_IMPL(T, N) void blit(const msdfgen::BitmapRef<T, N> &dst, const msdfgen::BitmapConstRef<T, N> &src, int dx, int dy, int sx, int sy, int w, int h) { blitSameType(dst, src, dx, dy, sx, sy, w, h); }
|
||||
|
||||
BLIT_SAME_TYPE_IMPL(byte, 1)
|
||||
BLIT_SAME_TYPE_IMPL(byte, 3)
|
||||
BLIT_SAME_TYPE_IMPL(byte, 4)
|
||||
BLIT_SAME_TYPE_IMPL(float, 1)
|
||||
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) {
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
const float *srcPixel = src(sx+x, sy+y);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(*srcPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
const float *srcPixel = src(sx+x, sy+y);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[0]);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[1]);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
for (int y = 0; y < h; ++y) {
|
||||
byte *dstPixel = dst(dx, dy+y);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
const float *srcPixel = src(sx+x, sy+y);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[0]);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[1]);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[2]);
|
||||
*dstPixel++ = msdfgen::pixelFloatToByte(srcPixel[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include "types.h"
|
||||
|
||||
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);
|
||||
void blit(const msdfgen::BitmapRef<byte, 3> &dst, const msdfgen::BitmapConstRef<byte, 3> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
void blit(const msdfgen::BitmapRef<byte, 4> &dst, const msdfgen::BitmapConstRef<byte, 4> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
|
||||
void blit(const msdfgen::BitmapRef<float, 1> &dst, const msdfgen::BitmapConstRef<float, 1> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
void blit(const msdfgen::BitmapRef<float, 3> &dst, const msdfgen::BitmapConstRef<float, 3> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
void blit(const msdfgen::BitmapRef<float, 4> &dst, const msdfgen::BitmapConstRef<float, 4> &src, int dx, int dy, int sx, int sy, int w, int h);
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
|
||||
#include "Charset.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "utf8.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static char escapedChar(char c) {
|
||||
switch (c) {
|
||||
case '0':
|
||||
return '\0';
|
||||
case 'n': case 'N':
|
||||
return '\n';
|
||||
case 'r': case 'R':
|
||||
return '\r';
|
||||
case 's': case 'S':
|
||||
return ' ';
|
||||
case 't': case 'T':
|
||||
return '\t';
|
||||
case '\\': case '"': case '\'':
|
||||
default:
|
||||
return 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
|
||||
str += 2;
|
||||
for (; *str; ++str) {
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
i <<= 4;
|
||||
i += *str-'0';
|
||||
} else if (*str >= 'A' && *str <= 'F') {
|
||||
i <<= 4;
|
||||
i += *str-'A'+10;
|
||||
} else if (*str >= 'a' && *str <= 'f') {
|
||||
i <<= 4;
|
||||
i += *str-'a'+10;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
} else { // dec
|
||||
for (; *str; ++str) {
|
||||
if (*str >= '0' && *str <= '9') {
|
||||
i *= 10;
|
||||
i += *str-'0';
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string combinePath(const char *basePath, const char *relPath) {
|
||||
if (relPath[0] == '/' || (relPath[0] && relPath[1] == ':')) // absolute path?
|
||||
return relPath;
|
||||
int lastSlash = -1;
|
||||
for (int i = 0; basePath[i]; ++i)
|
||||
if (basePath[i] == '/' || basePath[i] == '\\')
|
||||
lastSlash = i;
|
||||
if (lastSlash < 0)
|
||||
return relPath;
|
||||
return std::string(basePath, lastSlash+1)+relPath;
|
||||
}
|
||||
|
||||
bool Charset::load(const char *filename) {
|
||||
|
||||
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))
|
||||
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();
|
||||
state = TIGHT;
|
||||
break;
|
||||
case '"': // string of UTF-8 characters
|
||||
if (state != CLEAR)
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
#include "csv-export.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, double emSize, 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, "%u,%.17g,", glyphs[i].getCodepoint(), 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);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/**
|
||||
* Writes the positioning data and atlas layout of the glyphs into a CSV file
|
||||
* The columns are: Unicode index, horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t)
|
||||
*/
|
||||
bool exportCSV(const GlyphGeometry *glyphs, int glyphCount, double emSize, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
#include "glyph-generators.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <int N>
|
||||
static void invertColor(const msdfgen::BitmapRef<float, N> &bitmap) {
|
||||
const float *end = bitmap.pixels+N*bitmap.width*bitmap.height;
|
||||
for (float *p = bitmap.pixels; p < end; ++p)
|
||||
*p = 1.f-*p;
|
||||
}
|
||||
|
||||
void scanlineGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
|
||||
msdfgen::rasterize(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
}
|
||||
|
||||
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);
|
||||
if (glyph.isWindingReverse())
|
||||
invertColor(output);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), 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);
|
||||
if (glyph.isWindingReverse())
|
||||
invertColor(output);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), 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(), 0, attribs.overlapSupport);
|
||||
if (glyph.isWindingReverse())
|
||||
invertColor(output);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
|
||||
}
|
||||
|
||||
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(), 0, attribs.overlapSupport);
|
||||
if (glyph.isWindingReverse())
|
||||
invertColor(output);
|
||||
if (attribs.scanlinePass)
|
||||
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxScale(), glyph.getBoxTranslate(), MSDF_ATLAS_GLYPH_FILL_RULE);
|
||||
msdfgen::msdfErrorCorrection(output, attribs.errorCorrectionThreshold/(glyph.getBoxScale()*glyph.getBoxRange()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include "GlyphGeometry.h"
|
||||
#include "AtlasGenerator.h"
|
||||
|
||||
#define MSDF_ATLAS_GLYPH_FILL_RULE msdfgen::FILL_NONZERO
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
// Glyph bitmap generator functions
|
||||
|
||||
/// Generates non-anti-aliased binary image of the glyph using scanline rasterization
|
||||
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
|
||||
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);
|
||||
/// Generates a multi-channel and alpha-encoded true signed distance field of the glyph
|
||||
void mtsdfGenerator(const msdfgen::BitmapRef<float, 4> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
#include "image-encode.h"
|
||||
|
||||
#include <lodepng.h>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 1> &bitmap) {
|
||||
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
||||
for (int y = 0; y < bitmap.height; ++y)
|
||||
memcpy(&pixels[bitmap.width*y], bitmap(0, bitmap.height-y-1), bitmap.width);
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_GREY);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 3> &bitmap) {
|
||||
std::vector<byte> pixels(3*bitmap.width*bitmap.height);
|
||||
for (int y = 0; y < bitmap.height; ++y)
|
||||
memcpy(&pixels[3*bitmap.width*y], bitmap(0, bitmap.height-y-1), 3*bitmap.width);
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 4> &bitmap) {
|
||||
std::vector<byte> pixels(4*bitmap.width*bitmap.height);
|
||||
for (int y = 0; y < bitmap.height; ++y)
|
||||
memcpy(&pixels[4*bitmap.width*y], bitmap(0, bitmap.height-y-1), 4*bitmap.width);
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_RGBA);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 1> &bitmap) {
|
||||
std::vector<byte> pixels(bitmap.width*bitmap.height);
|
||||
std::vector<byte>::iterator it = pixels.begin();
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
for (int x = 0; x < bitmap.width; ++x)
|
||||
*it++ = msdfgen::pixelFloatToByte(*bitmap(x, y));
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_GREY);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 3> &bitmap) {
|
||||
std::vector<byte> pixels(3*bitmap.width*bitmap.height);
|
||||
std::vector<byte>::iterator it = pixels.begin();
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
for (int x = 0; x < bitmap.width; ++x) {
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[0]);
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[1]);
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[2]);
|
||||
}
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_RGB);
|
||||
}
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4> &bitmap) {
|
||||
std::vector<byte> pixels(4*bitmap.width*bitmap.height);
|
||||
std::vector<byte>::iterator it = pixels.begin();
|
||||
for (int y = bitmap.height-1; y >= 0; --y)
|
||||
for (int x = 0; x < bitmap.width; ++x) {
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[0]);
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[1]);
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[2]);
|
||||
*it++ = msdfgen::pixelFloatToByte(bitmap(x, y)[3]);
|
||||
}
|
||||
return !lodepng::encode(output, pixels, bitmap.width, bitmap.height, LCT_RGBA);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <msdfgen.h>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
// Functions to encode an image as a sequence of bytes in memory
|
||||
// Only PNG format available currently
|
||||
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 1> &bitmap);
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 3> &bitmap);
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<msdfgen::byte, 4> &bitmap);
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 1> &bitmap);
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 3> &bitmap);
|
||||
bool encodePng(std::vector<byte> &output, const msdfgen::BitmapConstRef<float, 4> &bitmap);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include "types.h"
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
#include "image-save.hpp"
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
#include "image-save.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <msdfgen-ext.h>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <int N>
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename);
|
||||
template <int N>
|
||||
bool saveImageBinaryLE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
template <int N>
|
||||
bool saveImageBinaryBE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename);
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
|
||||
template <int N>
|
||||
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename) {
|
||||
switch (format) {
|
||||
case ImageFormat::PNG:
|
||||
return msdfgen::savePng(bitmap, filename);
|
||||
case ImageFormat::BMP:
|
||||
return msdfgen::saveBmp(bitmap, filename);
|
||||
case ImageFormat::TIFF:
|
||||
return false;
|
||||
case ImageFormat::TEXT:
|
||||
return saveImageText(bitmap, filename);
|
||||
case ImageFormat::TEXT_FLOAT:
|
||||
return false;
|
||||
case ImageFormat::BINARY:
|
||||
return saveImageBinary(bitmap, filename);
|
||||
case ImageFormat::BINARY_FLOAT:
|
||||
case ImageFormat::BINARY_FLOAT_BE:
|
||||
return false;
|
||||
default:;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImage(const msdfgen::BitmapConstRef<float, N> &bitmap, ImageFormat format, const char *filename) {
|
||||
switch (format) {
|
||||
case ImageFormat::PNG:
|
||||
return msdfgen::savePng(bitmap, filename);
|
||||
case ImageFormat::BMP:
|
||||
return msdfgen::saveBmp(bitmap, filename);
|
||||
case ImageFormat::TIFF:
|
||||
return msdfgen::saveTiff(bitmap, filename);
|
||||
case ImageFormat::TEXT:
|
||||
return false;
|
||||
case ImageFormat::TEXT_FLOAT:
|
||||
return saveImageText(bitmap, filename);
|
||||
case ImageFormat::BINARY:
|
||||
return false;
|
||||
case ImageFormat::BINARY_FLOAT:
|
||||
return saveImageBinaryLE(bitmap, filename);
|
||||
case ImageFormat::BINARY_FLOAT_BE:
|
||||
return saveImageBinaryBE(bitmap, filename);
|
||||
default:;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename) {
|
||||
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;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool
|
||||
#ifdef __BIG_ENDIAN__
|
||||
saveImageBinaryBE
|
||||
#else
|
||||
saveImageBinaryLE
|
||||
#endif
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
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;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool
|
||||
#ifdef __BIG_ENDIAN__
|
||||
saveImageBinaryLE
|
||||
#else
|
||||
saveImageBinaryBE
|
||||
#endif
|
||||
(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
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);
|
||||
}
|
||||
success = written == sizeof(float)*count;
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
const byte *p = bitmap.pixels;
|
||||
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");
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
template <int N>
|
||||
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
bool success = false;
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
const float *p = bitmap.pixels;
|
||||
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");
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
|
||||
#include "json-export.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static const char * imageTypeString(ImageType type) {
|
||||
switch (type) {
|
||||
case ImageType::HARD_MASK:
|
||||
return "hardmask";
|
||||
case ImageType::SOFT_MASK:
|
||||
return "softmask";
|
||||
case ImageType::SDF:
|
||||
return "sdf";
|
||||
case ImageType::PSDF:
|
||||
return "psdf";
|
||||
case ImageType::MSDF:
|
||||
return "msdf";
|
||||
case ImageType::MTSDF:
|
||||
return "mtsdf";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool exportJSON(msdfgen::FontHandle *font, const GlyphGeometry *glyphs, int glyphCount, 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;
|
||||
|
||||
FILE *f = fopen(filename, "w");
|
||||
if (!f)
|
||||
return false;
|
||||
fputs("{", f);
|
||||
|
||||
// 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);
|
||||
fprintf(f, "\"unicode\":%u,", glyphs[i].getCodepoint());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
fputs("]", f);
|
||||
|
||||
fputs("}\n", f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// 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, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,688 @@
|
|||
|
||||
/*
|
||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR v1.0 (2020-03-08) - standalone console program
|
||||
* --------------------------------------------------------------------------------------------------
|
||||
* A utility by Viktor Chlumsky, (c) 2020
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef MSDF_ATLAS_STANDALONE
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include "msdf-atlas-gen.h"
|
||||
|
||||
using namespace msdf_atlas;
|
||||
|
||||
#define DEFAULT_ANGLE_THRESHOLD 3.0
|
||||
#define DEFAULT_MITER_LIMIT 1.0
|
||||
#define DEFAULT_EM_SIZE 32.0
|
||||
#define DEFAULT_PIXEL_RANGE 2.0
|
||||
#define SDF_ERROR_ESTIMATE_PRECISION 19
|
||||
#define GLYPH_FILL_RULE msdfgen::FILL_NONZERO
|
||||
#define MCG_MULTIPLIER 6364136223846793005ull
|
||||
|
||||
static const char * const helpText = R"(
|
||||
MSDF Atlas Generator by Viktor Chlumsky v)" MSDF_ATLAS_VERSION R"( (with MSDFGEN v)" MSDFGEN_VERSION R"()
|
||||
----------------------------------------------------------------
|
||||
|
||||
INPUT SPECIFICATION
|
||||
-font <filename.ttf/otf>
|
||||
Specifies the input TrueType / OpenType font file. This is required.
|
||||
-charset <filename>
|
||||
Specifies the input character set. Refer to the documentation for format of charset specification. Defaults to ASCII.
|
||||
|
||||
ATLAS CONFIGURATION
|
||||
-type <hardmask / softmask / sdf / psdf / msdf / mtsdf>
|
||||
Selects the type of atlas to be generated.
|
||||
-format <png / bmp / tiff / text / textfloat / bin / binfloat / binfloatbe>
|
||||
Selects the format for the atlas image output. Some image formats may be incompatible with embedded output formats.
|
||||
-dimensions <width> <height>
|
||||
Sets the atlas to have fixed dimensions (width x height).
|
||||
-pots / -potr / -square / -square2 / -square4
|
||||
Picks the minimum atlas dimensions that fit all glyphs and satisfy the selected constraint:
|
||||
power of two square / ... rectangle / any square / square with side divisible by 2 / ... 4
|
||||
|
||||
OUTPUT SPECIFICATION - one or more can be specified
|
||||
-imageout <filename.*>
|
||||
Saves the atlas as an image file with the specified format. Layout data must be stored separately.
|
||||
-json <filename.json>
|
||||
Writes the atlas's layout data, as well as other metrics into a structured JSON file.
|
||||
-csv <filename.csv>
|
||||
Writes the layout data of the glyphs into a simple CSV file.
|
||||
-arfont <filename.arfont>
|
||||
Stores the atlas and its layout data as an Artery Font file. Supported formats: png, bin, binfloat.
|
||||
-shadronpreview <filename.shadron> <sample text>
|
||||
Generates a Shadron script that uses the generated atlas to draw a sample text as a preview.
|
||||
|
||||
GLYPH CONFIGURATION
|
||||
-size <EM size>
|
||||
Specified the size of the glyphs in the atlas bitmap in pixels per EM.
|
||||
-minsize <EM size>
|
||||
Specifies the minimum size. The largest possible size that fits the same atlas dimensions will be used.
|
||||
-emrange <EM range>
|
||||
Specifies the SDF distance range in EM's.
|
||||
-pxrange <pixel range>
|
||||
Specifies the SDF distance range in output pixels. The default value is 2.
|
||||
|
||||
DISTANCE FIELD GENERATOR SETTINGS
|
||||
-angle <angle>
|
||||
Specifies the minimum angle between adjacent edges to be considered a corner. Append D for degrees. (msdf / mtsdf only)
|
||||
-errorcorrection <threshold>
|
||||
Changes the threshold used to detect and correct potential artifacts. 0 disables error correction. (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)
|
||||
-nooverlap
|
||||
Disables resolution of overlapping contours.
|
||||
-noscanline
|
||||
Disables the scanline pass, which corrects the distance field's signs according to the non-zero fill rule.
|
||||
-seed <N>
|
||||
Sets the initial seed for the edge coloring heuristic.
|
||||
)";
|
||||
|
||||
static char toupper(char c) {
|
||||
return c >= 'a' && c <= 'z' ? c-'a'+'A' : c;
|
||||
}
|
||||
|
||||
static bool parseUnsigned(unsigned &value, const char *arg) {
|
||||
static char c;
|
||||
return sscanf(arg, "%u%c", &value, &c) == 1;
|
||||
}
|
||||
|
||||
static bool parseUnsignedLL(unsigned long long &value, const char *arg) {
|
||||
static char c;
|
||||
return sscanf(arg, "%llu%c", &value, &c) == 1;
|
||||
}
|
||||
|
||||
static bool parseDouble(double &value, const char *arg) {
|
||||
static char c;
|
||||
return sscanf(arg, "%lf%c", &value, &c) == 1;
|
||||
}
|
||||
|
||||
static bool parseAngle(double &value, const char *arg) {
|
||||
char c1, c2;
|
||||
int result = sscanf(arg, "%lf%c%c", &value, &c1, &c2);
|
||||
if (result == 1)
|
||||
return true;
|
||||
if (result == 2 && (c1 == 'd' || c1 == 'D')) {
|
||||
value *= M_PI/180;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cmpExtension(const char *path, const char *ext) {
|
||||
for (const char *a = path+strlen(path)-1, *b = ext+strlen(ext)-1; b >= ext; --a, --b)
|
||||
if (a < path || toupper(*a) != toupper(*b))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void loadGlyphs(std::vector<GlyphGeometry> &glyphs, msdfgen::FontHandle *font, const Charset &charset) {
|
||||
glyphs.clear();
|
||||
glyphs.reserve(charset.size());
|
||||
for (unicode_t cp : charset) {
|
||||
GlyphGeometry glyph;
|
||||
if (glyph.load(font, cp))
|
||||
glyphs.push_back((GlyphGeometry &&) glyph);
|
||||
else
|
||||
printf("Glyph for codepoint 0x%X missing\n", cp);
|
||||
}
|
||||
}
|
||||
|
||||
struct Configuration {
|
||||
ImageType imageType;
|
||||
ImageFormat imageFormat;
|
||||
int width, height;
|
||||
double emSize;
|
||||
double pxRange;
|
||||
double angleThreshold;
|
||||
double miterLimit;
|
||||
unsigned long long coloringSeed;
|
||||
GeneratorAttributes generatorAttributes;
|
||||
int threadCount;
|
||||
const char *arteryFontFilename;
|
||||
const char *imageFilename;
|
||||
const char *jsonFilename;
|
||||
const char *csvFilename;
|
||||
const char *shadronPreviewFilename;
|
||||
const char *shadronPreviewText;
|
||||
};
|
||||
|
||||
template <typename T, typename S, int N, GeneratorFunction<S, N> GEN_FN>
|
||||
static bool makeAtlas(const std::vector<GlyphGeometry> &glyphs, msdfgen::FontHandle *font, const Configuration &config) {
|
||||
ImmediateAtlasGenerator<S, N, GEN_FN, BitmapAtlasStorage<T, N> > generator(config.width, config.height);
|
||||
generator.setAttributes(config.generatorAttributes);
|
||||
generator.setThreadCount(config.threadCount);
|
||||
generator.generate(glyphs.data(), glyphs.size());
|
||||
msdfgen::BitmapConstRef<T, N> bitmap = (msdfgen::BitmapConstRef<T, N>) generator.atlasStorage();
|
||||
|
||||
bool success = true;
|
||||
|
||||
if (config.imageFilename) {
|
||||
if (saveImage(bitmap, config.imageFormat, config.imageFilename))
|
||||
puts("Atlas image file saved.");
|
||||
else {
|
||||
success = false;
|
||||
puts("Failed to save the atlas as an image file.");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.arteryFontFilename) {
|
||||
if (exportArteryFont<float>(font, glyphs.data(), glyphs.size(), config.emSize, config.pxRange, bitmap, config.imageType, config.imageFormat, config.arteryFontFilename))
|
||||
puts("Artery Font file generated.");
|
||||
else {
|
||||
success = false;
|
||||
puts("Failed to generate Artery Font file.");
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int main(int argc, const char * const *argv) {
|
||||
#define ABORT(msg) { puts(msg); return 1; }
|
||||
|
||||
int result = 0;
|
||||
Configuration config = { };
|
||||
const char *fontFilename = nullptr;
|
||||
const char *charsetFilename = nullptr;
|
||||
config.imageType = ImageType::MSDF;
|
||||
config.imageFormat = ImageFormat::UNSPECIFIED;
|
||||
const char *imageFormatName = nullptr;
|
||||
int fixedWidth = -1, fixedHeight = -1;
|
||||
config.generatorAttributes.overlapSupport = true;
|
||||
config.generatorAttributes.scanlinePass = true;
|
||||
config.generatorAttributes.errorCorrectionThreshold = MSDFGEN_DEFAULT_ERROR_CORRECTION_THRESHOLD;
|
||||
double minEmSize = 0;
|
||||
enum {
|
||||
/// Range specified in EMs
|
||||
RANGE_EM,
|
||||
/// Range specified in output pixels
|
||||
RANGE_PIXEL,
|
||||
} rangeMode = RANGE_PIXEL;
|
||||
double rangeValue = 0;
|
||||
TightAtlasPacker::DimensionsConstraint atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE;
|
||||
config.angleThreshold = DEFAULT_ANGLE_THRESHOLD;
|
||||
config.miterLimit = DEFAULT_MITER_LIMIT;
|
||||
config.threadCount = std::max((int) std::thread::hardware_concurrency(), 1);
|
||||
|
||||
// Parse command line
|
||||
int argPos = 1;
|
||||
bool suggestHelp = false;
|
||||
while (argPos < argc) {
|
||||
const char *arg = argv[argPos];
|
||||
#define ARG_CASE(s, p) if (!strcmp(arg, s) && argPos+(p) < argc)
|
||||
|
||||
ARG_CASE("-type", 1) {
|
||||
arg = argv[++argPos];
|
||||
if (!strcmp(arg, "hardmask"))
|
||||
config.imageType = ImageType::HARD_MASK;
|
||||
else if (!strcmp(arg, "softmask"))
|
||||
config.imageType = ImageType::SOFT_MASK;
|
||||
else if (!strcmp(arg, "sdf"))
|
||||
config.imageType = ImageType::SDF;
|
||||
else if (!strcmp(arg, "psdf"))
|
||||
config.imageType = ImageType::PSDF;
|
||||
else if (!strcmp(arg, "msdf"))
|
||||
config.imageType = ImageType::MSDF;
|
||||
else if (!strcmp(arg, "mtsdf"))
|
||||
config.imageType = ImageType::MTSDF;
|
||||
else
|
||||
ABORT("Invalid atlas type. Valid types are: hardmask, softmask, sdf, psdf, msdf, mtsdf");
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-format", 1) {
|
||||
arg = argv[++argPos];
|
||||
if (!strcmp(arg, "png"))
|
||||
config.imageFormat = ImageFormat::PNG;
|
||||
else if (!strcmp(arg, "bmp"))
|
||||
config.imageFormat = ImageFormat::BMP;
|
||||
else if (!strcmp(arg, "tiff"))
|
||||
config.imageFormat = ImageFormat::TIFF;
|
||||
else if (!strcmp(arg, "text"))
|
||||
config.imageFormat = ImageFormat::TEXT;
|
||||
else if (!strcmp(arg, "textfloat"))
|
||||
config.imageFormat = ImageFormat::TEXT_FLOAT;
|
||||
else if (!strcmp(arg, "bin"))
|
||||
config.imageFormat = ImageFormat::BINARY;
|
||||
else if (!strcmp(arg, "binfloat"))
|
||||
config.imageFormat = ImageFormat::BINARY_FLOAT;
|
||||
else if (!strcmp(arg, "binfloatbe"))
|
||||
config.imageFormat = ImageFormat::BINARY_FLOAT_BE;
|
||||
else
|
||||
ABORT("Invalid image format. Valid formats are: png, bmp, tiff, text, textfloat, bin, binfloat");
|
||||
imageFormatName = arg;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-font", 1) {
|
||||
fontFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-charset", 1) {
|
||||
charsetFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-arfont", 1) {
|
||||
config.arteryFontFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-imageout", 1) {
|
||||
config.imageFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-json", 1) {
|
||||
config.jsonFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-csv", 1) {
|
||||
config.csvFilename = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-shadronpreview", 2) {
|
||||
config.shadronPreviewFilename = argv[++argPos];
|
||||
config.shadronPreviewText = argv[++argPos];
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-dimensions", 2) {
|
||||
unsigned w, h;
|
||||
if (!(parseUnsigned(w, argv[argPos+1]) && parseUnsigned(h, argv[argPos+2]) && w && h))
|
||||
ABORT("Invalid atlas dimensions. Use -dimensions <width> <height> with two positive integers.");
|
||||
fixedWidth = w, fixedHeight = h;
|
||||
argPos += 3;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-pots", 0) {
|
||||
atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::POWER_OF_TWO_SQUARE;
|
||||
fixedWidth = -1, fixedHeight = -1;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-potr", 0) {
|
||||
atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::POWER_OF_TWO_RECTANGLE;
|
||||
fixedWidth = -1, fixedHeight = -1;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-square", 0) {
|
||||
atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::SQUARE;
|
||||
fixedWidth = -1, fixedHeight = -1;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-square2", 0) {
|
||||
atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::EVEN_SQUARE;
|
||||
fixedWidth = -1, fixedHeight = -1;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-square4", 0) {
|
||||
atlasSizeConstraint = TightAtlasPacker::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE;
|
||||
fixedWidth = -1, fixedHeight = -1;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-size", 1) {
|
||||
double s;
|
||||
if (!(parseDouble(s, argv[++argPos]) && s > 0))
|
||||
ABORT("Invalid EM size argument. Use -size <EM size> with a positive real number.");
|
||||
config.emSize = s;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-minsize", 1) {
|
||||
double s;
|
||||
if (!(parseDouble(s, argv[++argPos]) && s > 0))
|
||||
ABORT("Invalid minimum EM size argument. Use -minsize <EM size> with a positive real number.");
|
||||
minEmSize = s;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-emrange", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[++argPos]) && r >= 0))
|
||||
ABORT("Invalid range argument. Use -emrange <EM range> with a positive real number.");
|
||||
rangeMode = RANGE_EM;
|
||||
rangeValue = r;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-pxrange", 1) {
|
||||
double r;
|
||||
if (!(parseDouble(r, argv[++argPos]) && r >= 0))
|
||||
ABORT("Invalid range argument. Use -pxrange <pixel range> with a positive real number.");
|
||||
rangeMode = RANGE_PIXEL;
|
||||
rangeValue = r;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-angle", 1) {
|
||||
double at;
|
||||
if (!parseAngle(at, argv[argPos+1]))
|
||||
ABORT("Invalid angle threshold. Use -angle <min angle> with a positive real number less than PI or a value in degrees followed by 'd' below 180d.");
|
||||
config.angleThreshold = at;
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-errorcorrection", 1) {
|
||||
double ect;
|
||||
if (!parseDouble(ect, argv[argPos+1]) || ect < 0)
|
||||
ABORT("Invalid error correction threshold. Use -errorcorrection <threshold> with a real number larger or equal to 1.");
|
||||
config.generatorAttributes.errorCorrectionThreshold = ect;
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-miterlimit", 1) {
|
||||
double m;
|
||||
if (!(parseDouble(m, argv[++argPos]) && m >= 0))
|
||||
ABORT("Invalid miter limit argument. Use -miterlimit <limit> with a positive real number.");
|
||||
config.miterLimit = m;
|
||||
++argPos;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-nooverlap", 0) {
|
||||
config.generatorAttributes.overlapSupport = false;
|
||||
argPos += 1;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-noscanline", 0) {
|
||||
config.generatorAttributes.scanlinePass = false;
|
||||
argPos += 1;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-scanline", 0) {
|
||||
config.generatorAttributes.scanlinePass = true;
|
||||
argPos += 1;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-seed", 1) {
|
||||
if (!parseUnsignedLL(config.coloringSeed, argv[argPos+1]))
|
||||
ABORT("Invalid seed. Use -seed <N> with N being a non-negative integer.");
|
||||
argPos += 2;
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-help", 0) {
|
||||
puts(helpText);
|
||||
return 0;
|
||||
}
|
||||
printf("Unknown setting or insufficient parameters: %s\n", arg);
|
||||
suggestHelp = true;
|
||||
++argPos;
|
||||
}
|
||||
if (suggestHelp)
|
||||
printf("Use -help for more information.\n");
|
||||
|
||||
// Nothing to do?
|
||||
if (argc == 1) {
|
||||
printf(
|
||||
"Usage: msdf-atlas-gen"
|
||||
#ifdef _WIN32
|
||||
".exe"
|
||||
#endif
|
||||
" -font <filename.ttf/otf> -charset <charset> <output specification> <options>\n"
|
||||
"Use -help for more information.\n"
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
if (!fontFilename)
|
||||
ABORT("No font specified.");
|
||||
if (!(config.arteryFontFilename || config.imageFilename || config.jsonFilename || config.csvFilename || config.shadronPreviewFilename)) {
|
||||
puts("No output specified.");
|
||||
return 0;
|
||||
}
|
||||
bool layoutOnly = !(config.arteryFontFilename || config.imageFilename);
|
||||
|
||||
// Fix up configuration based on related values
|
||||
if (!(config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF))
|
||||
config.miterLimit = 0;
|
||||
if (config.emSize > minEmSize)
|
||||
minEmSize = config.emSize;
|
||||
if (!(fixedWidth > 0 && fixedHeight > 0) && !(minEmSize > 0)) {
|
||||
puts("Neither atlas size nor glyph size selected, using default...");
|
||||
minEmSize = DEFAULT_EM_SIZE;
|
||||
}
|
||||
if (!(config.imageType == ImageType::SDF || config.imageType == ImageType::PSDF || config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF)) {
|
||||
rangeMode = RANGE_PIXEL;
|
||||
rangeValue = (double) (config.imageType == ImageType::SOFT_MASK);
|
||||
} else if (rangeValue <= 0) {
|
||||
rangeMode = RANGE_PIXEL;
|
||||
rangeValue = DEFAULT_PIXEL_RANGE;
|
||||
}
|
||||
|
||||
// Finalize image format
|
||||
ImageFormat imageExtension = ImageFormat::UNSPECIFIED;
|
||||
if (config.imageFilename) {
|
||||
if (cmpExtension(config.imageFilename, ".png")) imageExtension = ImageFormat::PNG;
|
||||
else if (cmpExtension(config.imageFilename, ".bmp")) imageExtension = ImageFormat::BMP;
|
||||
else if (cmpExtension(config.imageFilename, ".tif") || cmpExtension(config.imageFilename, ".tiff")) imageExtension = ImageFormat::TIFF;
|
||||
else if (cmpExtension(config.imageFilename, ".txt")) imageExtension = ImageFormat::TEXT;
|
||||
else if (cmpExtension(config.imageFilename, ".bin")) imageExtension = ImageFormat::BINARY;
|
||||
}
|
||||
if (config.imageFormat == ImageFormat::UNSPECIFIED) {
|
||||
config.imageFormat = ImageFormat::PNG;
|
||||
imageFormatName = "png";
|
||||
// If image format is not specified and -imageout is the only image output, infer format from its extension
|
||||
if (imageExtension != ImageFormat::UNSPECIFIED && !config.arteryFontFilename)
|
||||
config.imageFormat = imageExtension;
|
||||
}
|
||||
if (config.imageType == ImageType::MTSDF && config.imageFormat == ImageFormat::BMP)
|
||||
ABORT("Atlas type not compatible with image format. MTSDF requires a format with alpha channel.");
|
||||
if (config.arteryFontFilename && !(config.imageFormat == ImageFormat::PNG || config.imageFormat == ImageFormat::BINARY || config.imageFormat == ImageFormat::BINARY_FLOAT)) {
|
||||
config.arteryFontFilename = nullptr;
|
||||
result = 1;
|
||||
puts("Error: Unable to create an Artery Font file with the specified image format!");
|
||||
// Recheck whether there is anything else to do
|
||||
if (!(config.arteryFontFilename || config.imageFilename || config.jsonFilename || config.csvFilename || config.shadronPreviewFilename))
|
||||
return result;
|
||||
layoutOnly = !(config.arteryFontFilename || config.imageFilename);
|
||||
}
|
||||
if (imageExtension != ImageFormat::UNSPECIFIED) {
|
||||
// Warn if image format mismatches -imageout extension
|
||||
bool mismatch = false;
|
||||
switch (config.imageFormat) {
|
||||
case ImageFormat::TEXT: case ImageFormat::TEXT_FLOAT:
|
||||
mismatch = imageExtension != ImageFormat::TEXT;
|
||||
break;
|
||||
case ImageFormat::BINARY: case ImageFormat::BINARY_FLOAT: case ImageFormat::BINARY_FLOAT_BE:
|
||||
mismatch = imageExtension != ImageFormat::BINARY;
|
||||
break;
|
||||
default:
|
||||
mismatch = imageExtension != config.imageFormat;
|
||||
}
|
||||
if (mismatch)
|
||||
printf("Warning: Output image file extension does not match the image's actual format (%s)!\n", imageFormatName);
|
||||
}
|
||||
imageFormatName = nullptr; // No longer consistent with imageFormat
|
||||
|
||||
// Load font
|
||||
class FontHolder {
|
||||
msdfgen::FreetypeHandle *ft;
|
||||
msdfgen::FontHandle *font;
|
||||
public:
|
||||
explicit FontHolder(const char *fontFilename) : ft(nullptr), font(nullptr) {
|
||||
if ((ft = msdfgen::initializeFreetype()))
|
||||
font = msdfgen::loadFont(ft, fontFilename);
|
||||
}
|
||||
~FontHolder() {
|
||||
if (ft) {
|
||||
if (font)
|
||||
msdfgen::destroyFont(font);
|
||||
msdfgen::deinitializeFreetype(ft);
|
||||
}
|
||||
}
|
||||
operator msdfgen::FontHandle *() const {
|
||||
return font;
|
||||
}
|
||||
} font(fontFilename);
|
||||
if (!font)
|
||||
ABORT("Failed to load specified font file.");
|
||||
msdfgen::FontMetrics fontMetrics = { };
|
||||
msdfgen::getFontMetrics(fontMetrics, font);
|
||||
if (fontMetrics.emSize <= 0)
|
||||
fontMetrics.emSize = DEFAULT_EM_SIZE;
|
||||
|
||||
// Load character set
|
||||
Charset charset;
|
||||
if (charsetFilename) {
|
||||
if (!charset.load(charsetFilename))
|
||||
ABORT("Failed to load character set specification.");
|
||||
} else
|
||||
charset = Charset::ASCII;
|
||||
if (charset.empty())
|
||||
ABORT("No character set loaded.");
|
||||
|
||||
// Load glyphs
|
||||
std::vector<GlyphGeometry> glyphs;
|
||||
loadGlyphs(glyphs, font, charset);
|
||||
printf("Loaded geometry of %d out of %d characters.\n", (int) glyphs.size(), (int) charset.size());
|
||||
|
||||
// Determine final atlas dimensions, scale and range, pack glyphs
|
||||
{
|
||||
double unitRange = 0, pxRange = 0;
|
||||
switch (rangeMode) {
|
||||
case RANGE_EM:
|
||||
unitRange = rangeValue*fontMetrics.emSize;
|
||||
break;
|
||||
case RANGE_PIXEL:
|
||||
pxRange = rangeValue;
|
||||
break;
|
||||
}
|
||||
bool fixedDimensions = fixedWidth >= 0 && fixedHeight >= 0;
|
||||
bool fixedScale = config.emSize > 0;
|
||||
TightAtlasPacker atlasPacker;
|
||||
if (fixedDimensions)
|
||||
atlasPacker.setDimensions(fixedWidth, fixedHeight);
|
||||
else
|
||||
atlasPacker.setDimensionsConstraint(atlasSizeConstraint);
|
||||
atlasPacker.setPadding(config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF ? 0 : -1);
|
||||
// TODO: In this case (if padding is -1), the border pixels of each glyph are black, but still computed. For floating-point output, this may play a role.
|
||||
if (fixedScale)
|
||||
atlasPacker.setScale(config.emSize/fontMetrics.emSize);
|
||||
else
|
||||
atlasPacker.setMinimumScale(minEmSize/fontMetrics.emSize);
|
||||
atlasPacker.setPixelRange(pxRange);
|
||||
atlasPacker.setUnitRange(unitRange);
|
||||
atlasPacker.setMiterLimit(config.miterLimit);
|
||||
if (int remaining = atlasPacker.pack(glyphs.data(), glyphs.size())) {
|
||||
if (remaining < 0) {
|
||||
ABORT("Failed to pack glyphs into atlas.");
|
||||
} else {
|
||||
printf("Error: Could not fit %d out of %d glyphs into the atlas.\n", remaining, (int) glyphs.size());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
atlasPacker.getDimensions(config.width, config.height);
|
||||
if (!(config.width > 0 && config.height > 0))
|
||||
ABORT("Unable to determine atlas size.");
|
||||
config.emSize = atlasPacker.getScale()*fontMetrics.emSize;
|
||||
config.pxRange = atlasPacker.getPixelRange();
|
||||
if (!fixedScale)
|
||||
printf("Glyph size: %.9g pixels/EM\n", config.emSize);
|
||||
if (!fixedDimensions)
|
||||
printf("Atlas dimensions: %d x %d\n", config.width, config.height);
|
||||
}
|
||||
|
||||
// Generate atlas bitmap
|
||||
if (!layoutOnly) {
|
||||
|
||||
// Edge coloring
|
||||
if (config.imageType == ImageType::MSDF || config.imageType == ImageType::MTSDF) {
|
||||
unsigned long long glyphSeed = config.coloringSeed;
|
||||
for (GlyphGeometry &glyph : glyphs) {
|
||||
glyphSeed *= MCG_MULTIPLIER;
|
||||
glyph.edgeColoring(config.angleThreshold, glyphSeed);
|
||||
}
|
||||
}
|
||||
|
||||
bool floatingPoint = (
|
||||
config.imageFormat == ImageFormat::TIFF ||
|
||||
config.imageFormat == ImageFormat::TEXT_FLOAT ||
|
||||
config.imageFormat == ImageFormat::BINARY_FLOAT ||
|
||||
config.imageFormat == ImageFormat::BINARY_FLOAT_BE
|
||||
);
|
||||
|
||||
bool success = false;
|
||||
switch (config.imageType) {
|
||||
case ImageType::HARD_MASK:
|
||||
if (floatingPoint)
|
||||
success = makeAtlas<float, float, 1, scanlineGenerator>(glyphs, font, config);
|
||||
else
|
||||
success = makeAtlas<byte, float, 1, scanlineGenerator>(glyphs, font, config);
|
||||
break;
|
||||
case ImageType::SOFT_MASK:
|
||||
case ImageType::SDF:
|
||||
if (floatingPoint)
|
||||
success = makeAtlas<float, float, 1, sdfGenerator>(glyphs, font, config);
|
||||
else
|
||||
success = makeAtlas<byte, float, 1, sdfGenerator>(glyphs, font, config);
|
||||
break;
|
||||
case ImageType::PSDF:
|
||||
if (floatingPoint)
|
||||
success = makeAtlas<float, float, 1, psdfGenerator>(glyphs, font, config);
|
||||
else
|
||||
success = makeAtlas<byte, float, 1, psdfGenerator>(glyphs, font, config);
|
||||
break;
|
||||
case ImageType::MSDF:
|
||||
if (floatingPoint)
|
||||
success = makeAtlas<float, float, 3, msdfGenerator>(glyphs, font, config);
|
||||
else
|
||||
success = makeAtlas<byte, float, 3, msdfGenerator>(glyphs, font, config);
|
||||
break;
|
||||
case ImageType::MTSDF:
|
||||
if (floatingPoint)
|
||||
success = makeAtlas<float, float, 4, mtsdfGenerator>(glyphs, font, config);
|
||||
else
|
||||
success = makeAtlas<byte, float, 4, mtsdfGenerator>(glyphs, font, config);
|
||||
break;
|
||||
}
|
||||
if (!success)
|
||||
result = 1;
|
||||
}
|
||||
|
||||
if (config.csvFilename) {
|
||||
if (exportCSV(glyphs.data(), glyphs.size(), fontMetrics.emSize, config.csvFilename))
|
||||
puts("Glyph layout written into CSV file.");
|
||||
else {
|
||||
result = 1;
|
||||
puts("Failed to write CSV output file.");
|
||||
}
|
||||
}
|
||||
if (config.jsonFilename) {
|
||||
if (exportJSON(font, glyphs.data(), glyphs.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.jsonFilename))
|
||||
puts("Glyph layout and metadata written into JSON file.");
|
||||
else {
|
||||
result = 1;
|
||||
puts("Failed to write JSON output file.");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.shadronPreviewFilename && config.shadronPreviewText) {
|
||||
std::vector<unicode_t> previewText;
|
||||
utf8Decode(previewText, config.shadronPreviewText);
|
||||
previewText.push_back(0);
|
||||
if (generateShadronPreview(font, glyphs.data(), glyphs.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, config.shadronPreviewFilename))
|
||||
puts("Shadron preview script generated.");
|
||||
else {
|
||||
result = 1;
|
||||
puts("Failed to generate Shadron preview file.");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR v1.0 (2020-03-08)
|
||||
* ---------------------------------------------------------------------
|
||||
* A utility by Viktor Chlumsky, (c) 2020
|
||||
*
|
||||
* Generates compact bitmap font atlases using MSDFGEN.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utf8.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Charset.h"
|
||||
#include "GlyphBox.h"
|
||||
#include "GlyphGeometry.h"
|
||||
#include "RectanglePacker.h"
|
||||
#include "rectangle-packing.h"
|
||||
#include "Workload.h"
|
||||
#include "size-selectors.h"
|
||||
#include "bitmap-blit.h"
|
||||
#include "AtlasStorage.h"
|
||||
#include "BitmapAtlasStorage.h"
|
||||
#include "TightAtlasPacker.h"
|
||||
#include "AtlasGenerator.h"
|
||||
#include "ImmediateAtlasGenerator.h"
|
||||
#include "DynamicAtlas.h"
|
||||
#include "glyph-generators.h"
|
||||
#include "image-encode.h"
|
||||
#include "image-save.h"
|
||||
#include "artery-font-export.h"
|
||||
#include "csv-export.h"
|
||||
#include "json-export.h"
|
||||
#include "shadron-preview-generator.h"
|
||||
|
||||
#define MSDF_ATLAS_VERSION "1.0"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include "Rectangle.h"
|
||||
|
||||
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);
|
||||
|
||||
/// 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);
|
||||
|
||||
}
|
||||
|
||||
#include "rectangle-packing.hpp"
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
#include "rectangle-packing.h"
|
||||
|
||||
#include <vector>
|
||||
#include "RectanglePacker.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static void copyRectanglePlacement(Rectangle &dst, const Rectangle &src) {
|
||||
dst.x = src.x;
|
||||
dst.y = src.y;
|
||||
}
|
||||
|
||||
static void copyRectanglePlacement(OrientedRectangle &dst, const OrientedRectangle &src) {
|
||||
dst.x = src.x;
|
||||
dst.y = src.y;
|
||||
dst.rotated = src.rotated;
|
||||
}
|
||||
|
||||
template <typename RectangleType>
|
||||
int packRectangles(RectangleType *rectangles, int count, int width, int height, int padding) {
|
||||
if (padding)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
rectangles[i].w += padding;
|
||||
rectangles[i].h += padding;
|
||||
}
|
||||
int result = RectanglePacker(width+padding, height+padding).pack(rectangles, count);
|
||||
if (padding)
|
||||
for (int i = 0; i < count; ++i) {
|
||||
rectangles[i].w -= padding;
|
||||
rectangles[i].h -= padding;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class SizeSelector, typename RectangleType>
|
||||
std::pair<int, int> packRectangles(RectangleType *rectangles, int count, int padding) {
|
||||
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;
|
||||
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)) {
|
||||
dimensions.first = width;
|
||||
dimensions.second = height;
|
||||
for (int i = 0; i < count; ++i)
|
||||
copyRectanglePlacement(rectangles[i], rectanglesCopy[i]);
|
||||
--sizeSelector;
|
||||
} else
|
||||
++sizeSelector;
|
||||
}
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
|
||||
#include "shadron-preview-generator.h"
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
static const char * const shadronFillGlyphMask = R"(
|
||||
template <ATLAS, RANGE, 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>
|
||||
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 fill = clamp(sd+0.5, 0.0, 1.0);
|
||||
return vec4(vec3(COLOR), fill);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char * const shadronPreviewPreamble = R"(
|
||||
#include <median>
|
||||
|
||||
glsl struct GlyphVertex {
|
||||
vec2 coord;
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
template <TEXT_SIZE>
|
||||
glsl vec4 projectVertex(out vec2 texCoord, in GlyphVertex vertex) {
|
||||
vec2 coord = vertex.coord;
|
||||
float scale = 2.0/max((TEXT_SIZE).x, shadron_Aspect*(TEXT_SIZE).y);
|
||||
scale *= exp(0.0625*shadron_Mouse.z);
|
||||
coord += vec2(-0.5, 0.5)*vec2(TEXT_SIZE);
|
||||
coord *= scale*vec2(1.0, shadron_Aspect);
|
||||
texCoord = vertex.texCoord;
|
||||
return vec4(coord, 0.0, 1.0);
|
||||
}
|
||||
%s
|
||||
#define PREVIEW_IMAGE(NAME, ATLAS, RANGE, 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>), \
|
||||
depth(false), \
|
||||
blend(transparency), \
|
||||
background(vec4(vec3(COLOR), 0.0)), \
|
||||
dimensions(DIMENSIONS), \
|
||||
resizable(true)
|
||||
|
||||
)";
|
||||
|
||||
static std::string relativizePath(const char *base, const char *target) {
|
||||
if (target[0] == '/' || (target[0] && target[1] == ':')) // absolute path?
|
||||
return target;
|
||||
int commonPrefix = 0;
|
||||
for (int i = 0; base[i] && target[i] && base[i] == target[i]; ++i) {
|
||||
if (base[i] == '/' || base[i] == '\\')
|
||||
commonPrefix = i+1;
|
||||
}
|
||||
base += commonPrefix;
|
||||
target += commonPrefix;
|
||||
int baseNesting = 0;
|
||||
for (int i = 0; base[i]; ++i)
|
||||
if (base[i] == '/' || base[i] == '\\')
|
||||
++baseNesting;
|
||||
std::string output;
|
||||
for (int i = 0; i < baseNesting; ++i)
|
||||
output += "../";
|
||||
output += 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) {
|
||||
double texelWidth = 1./atlasWidth;
|
||||
double texelHeight = 1./atlasHeight;
|
||||
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());
|
||||
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);
|
||||
{
|
||||
msdfgen::FontMetrics fontMetrics;
|
||||
if (!msdfgen::getFontMetrics(fontMetrics, font))
|
||||
return false;
|
||||
double fsScale = 1/(fontMetrics.ascenderY-fontMetrics.descenderY);
|
||||
fputs("vertex_list GlyphVertex textQuadVertices = {\n", file);
|
||||
double x = 0, y = -fsScale*fontMetrics.ascenderY;
|
||||
double textWidth = 0;
|
||||
for (const unicode_t *cp = text; *cp; ++cp) {
|
||||
if (*cp == '\r')
|
||||
continue;
|
||||
if (*cp == '\n') {
|
||||
textWidth = std::max(textWidth, x);
|
||||
x = 0;
|
||||
y -= fsScale*fontMetrics.lineHeight;
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
if (glyphs[i].getCodepoint() == *cp) {
|
||||
if (!glyphs[i].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);
|
||||
pl *= fsScale, pb *= fsScale, pr *= fsScale, pt *= fsScale;
|
||||
pl += x, pb += y, pr += x, pt += y;
|
||||
il *= texelWidth, ib *= texelHeight, ir *= texelWidth, it *= texelHeight;
|
||||
fprintf(file, " %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g, %.9g,\n",
|
||||
pl, pb, il, ib,
|
||||
pr, pb, ir, ib,
|
||||
pl, pt, il, it,
|
||||
pr, pt, ir, it,
|
||||
pl, pt, il, it,
|
||||
pr, pb, ir, ib
|
||||
);
|
||||
}
|
||||
x += fsScale*glyphs[i].getAdvance();
|
||||
double kerning;
|
||||
if (msdfgen::getKerning(kerning, font, cp[0], cp[1]))
|
||||
x += fsScale*kerning;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
textWidth = std::max(textWidth, x);
|
||||
y += fsScale*fontMetrics.descenderY;
|
||||
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("export png(Preview, \"preview.png\");\n", file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include "types.h"
|
||||
#include "GlyphGeometry.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);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
#include "size-selectors.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
template <int MULTIPLE>
|
||||
SquareSizeSelector<MULTIPLE>::SquareSizeSelector(int minArea) : lowerBound(0), upperBound(-1) {
|
||||
if (minArea > 0)
|
||||
lowerBound = int(sqrt(minArea-1))/MULTIPLE+1;
|
||||
updateCurrent();
|
||||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
void SquareSizeSelector<MULTIPLE>::updateCurrent() {
|
||||
if (upperBound < 0)
|
||||
current = 5*lowerBound/4+16/MULTIPLE;
|
||||
else
|
||||
current = lowerBound+(upperBound-lowerBound)/2;
|
||||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
bool SquareSizeSelector<MULTIPLE>::operator()(int &width, int &height) const {
|
||||
width = MULTIPLE*current, height = MULTIPLE*current;
|
||||
return lowerBound < upperBound || upperBound < 0;
|
||||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
SquareSizeSelector<MULTIPLE> & SquareSizeSelector<MULTIPLE>::operator++() {
|
||||
lowerBound = current+1;
|
||||
updateCurrent();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <int MULTIPLE>
|
||||
SquareSizeSelector<MULTIPLE> & SquareSizeSelector<MULTIPLE>::operator--() {
|
||||
upperBound = current;
|
||||
updateCurrent();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template class SquareSizeSelector<1>;
|
||||
template class SquareSizeSelector<2>;
|
||||
template class SquareSizeSelector<4>;
|
||||
|
||||
SquarePowerOfTwoSizeSelector::SquarePowerOfTwoSizeSelector(int minArea) : side(1) {
|
||||
while (side*side < minArea)
|
||||
side <<= 1;
|
||||
}
|
||||
|
||||
bool SquarePowerOfTwoSizeSelector::operator()(int &width, int &height) const {
|
||||
width = side, height = side;
|
||||
return side > 0;
|
||||
}
|
||||
|
||||
SquarePowerOfTwoSizeSelector & SquarePowerOfTwoSizeSelector::operator++() {
|
||||
side <<= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SquarePowerOfTwoSizeSelector & SquarePowerOfTwoSizeSelector::operator--() {
|
||||
side = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PowerOfTwoSizeSelector::PowerOfTwoSizeSelector(int minArea) : w(1), h(1) {
|
||||
while (w*h < minArea)
|
||||
++*this;
|
||||
}
|
||||
|
||||
bool PowerOfTwoSizeSelector::operator()(int &width, int &height) const {
|
||||
width = w, height = h;
|
||||
return w > 0 && h > 0;
|
||||
}
|
||||
|
||||
PowerOfTwoSizeSelector & PowerOfTwoSizeSelector::operator++() {
|
||||
if (w == h)
|
||||
w <<= 1;
|
||||
else
|
||||
h = w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PowerOfTwoSizeSelector & PowerOfTwoSizeSelector::operator--() {
|
||||
w = 0, h = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
// The size selector classes are used to select the minimum dimensions of the atlas fitting a given constraint.
|
||||
|
||||
/// Selects square dimensions which are also a multiple of MULTIPLE
|
||||
template <int MULTIPLE = 1>
|
||||
class SquareSizeSelector {
|
||||
|
||||
public:
|
||||
explicit SquareSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
SquareSizeSelector<MULTIPLE> & operator++();
|
||||
SquareSizeSelector<MULTIPLE> & operator--();
|
||||
|
||||
private:
|
||||
int lowerBound, upperBound;
|
||||
int current;
|
||||
|
||||
void updateCurrent();
|
||||
|
||||
};
|
||||
|
||||
/// Selects square power-of-two dimensions
|
||||
class SquarePowerOfTwoSizeSelector {
|
||||
|
||||
public:
|
||||
explicit SquarePowerOfTwoSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
SquarePowerOfTwoSizeSelector & operator++();
|
||||
SquarePowerOfTwoSizeSelector & operator--();
|
||||
|
||||
private:
|
||||
int side;
|
||||
|
||||
};
|
||||
|
||||
/// Selects square or rectangular (2:1) power-of-two dimensions
|
||||
class PowerOfTwoSizeSelector {
|
||||
|
||||
public:
|
||||
explicit PowerOfTwoSizeSelector(int minArea = 0);
|
||||
bool operator()(int &width, int &height) const;
|
||||
PowerOfTwoSizeSelector & operator++();
|
||||
PowerOfTwoSizeSelector & operator--();
|
||||
|
||||
private:
|
||||
int w, h;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef uint32_t unicode_t;
|
||||
|
||||
/// Type of atlas image contents
|
||||
enum class ImageType {
|
||||
/// Rendered glyphs without anti-aliasing (two colors only)
|
||||
HARD_MASK,
|
||||
/// Rendered glyphs with anti-aliasing
|
||||
SOFT_MASK,
|
||||
/// Signed (true) distance field
|
||||
SDF,
|
||||
/// Signed pseudo-distance field
|
||||
PSDF,
|
||||
/// Multi-channel signed distance field
|
||||
MSDF,
|
||||
/// Multi-channel & true signed distance field
|
||||
MTSDF
|
||||
};
|
||||
|
||||
/// Atlas image encoding
|
||||
enum class ImageFormat {
|
||||
UNSPECIFIED,
|
||||
PNG,
|
||||
BMP,
|
||||
TIFF,
|
||||
TEXT,
|
||||
TEXT_FLOAT,
|
||||
BINARY,
|
||||
BINARY_FLOAT,
|
||||
BINARY_FLOAT_BE
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
#include "utf8.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
void utf8Decode(std::vector<unicode_t> &codepoints, const char *utf8String) {
|
||||
bool start = true;
|
||||
int rBytes = 0;
|
||||
unicode_t cp = 0;
|
||||
|
||||
for (const char *c = utf8String; *c; ++c) {
|
||||
if (rBytes > 0) {
|
||||
--rBytes;
|
||||
if ((*c&0xc0) == 0x80)
|
||||
cp |= (*c&0x3f)<<(6*rBytes);
|
||||
// else error
|
||||
} else if (!(*c&0x80)) {
|
||||
cp = *c;
|
||||
rBytes = 0;
|
||||
} else if (*c&0x40) {
|
||||
int block;
|
||||
for (block = 0; (*c<<block)&0x40 && block < 4; ++block);
|
||||
if (block < 4) {
|
||||
cp = (*c&(0x3f>>block))<<(6*block);
|
||||
rBytes = block;
|
||||
} else
|
||||
continue; // error
|
||||
} else
|
||||
continue; // error
|
||||
if (!rBytes) {
|
||||
if (!(start && cp == 0xfeff)) // BOM
|
||||
codepoints.push_back(cp);
|
||||
start = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
|
||||
namespace msdf_atlas {
|
||||
|
||||
/// Decodes the UTF-8 string into an array of Unicode codepoints
|
||||
void utf8Decode(std::vector<unicode_t> &codepoints, const char *utf8String);
|
||||
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue