Merge pull request #2 from antopilo/develop

Merge current scripting progress to master.
This commit is contained in:
Antoine Pilote
2021-06-12 15:32:33 -04:00
committed by GitHub
114 changed files with 5972 additions and 25234 deletions

4
.gitignore vendored
View File

@@ -826,4 +826,6 @@ FodyWeavers.xsd
# JetBrains Rider
.idea/
*.sln.iml
*.sln.iml
!freetype.lib

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "Nuake/dependencies/glfw"]
path = Nuake/dependencies/glfw
url = https://github.com/glfw/glfw/tree/master

Binary file not shown.

View File

@@ -1,26 +1,10 @@
import "Scripts/Math" for Vector3
class Engine {
foreign static Log(msg)
}
class Scene {
foreign static GetEntityID(name)
static GetEntity(name) {
var entId = Scene.GetEntityID(name)
var ent = Entity.new(entId)
return ent
}
foreign static EntityHasComponent(id, name)
}
class Entity {
construct new(id) {
_entityId = id
}
HasComponent(component) {
return Scene.EntityHasComponent(_entityId, component)
}
}

View File

@@ -0,0 +1,70 @@
import "Scripts/Math" for Vector3
import "Scripts/Engine" for Engine
class Input {
foreign static GetMouseX()
foreign static GetMouseY()
// Gets the mouse position of X & Y and returns a Vector3
static GetMousePos() {
var result = Vector3.new(this.GetMouseX_(), this.GetMouseY_(), 0)
return result
}
// Keys
foreign static IsKeyDown_(key)
static IsKeyDown(key) {
if(key is Num) {
return this.IsKeyDown_(key)
}
Engine.Log("IsKeyDown expects a number. Got: %(key.type)")
}
foreign static IsKeyPressed_(key)
static IsKeyPressed(key) {
if(key is Num){
return this.IsKeyPressed_(key)
}
Engine.Log("IsKeyPressed expects a number. Got: %(key.type)")
}
foreign static IsKeyReleased_(key)
static IsKeyReleased(key) {
if(key is Num) {
return this.IsKeyReleased_(key)
}
Engine.Log("IsKeyReleased expects a number. Got: %(key.type)")
}
// Mouse
foreign static IsMouseButtonDown_(button)
static IsMouseButtonDown(button) {
if(button is Num){
return this.IsMouseButtonDown_(button)
}
}
foreign static IsMouseButtonPressed_(button)
static IsMouseButtonPressed(button) {
if(button is Num) {
Engine.Log("IsmouseButtonPressed: %(button)")
return this.IsMouseButtonPressed_(button)
}
}
foreign static IsMouseButtonReleased_(button)
static IsMouseButtonReleased(button) {
if(button is Num){
return this.IsMouseButtonReleased_(button)
}
}
foreign static HideMouse()
foreign static ShowMouse()
foreign static IsMouseHidden()
}

View File

@@ -0,0 +1,69 @@
class Math {
foreign static Sqrt_(x, y, z)
}
class Vector3 {
x {_x}
y {_y}
z {_z}
x=(value) {
_x = value
}
y=(value) {
_y = value
}
z=(value) {
_z = value
}
mul(other) {
if(other is Vector3) {
return Vector3.new(_x * other.x,
_y * other.y,
_z * other.z)
} else {
return Vector3.new(_x * other, _y * other, _z * other)
}
}
*(other) {
if(other is Vector3) {
return Vector3.new(_x * other.x,
_y * other.y,
_z * other.z)
} else if(other is Num) {
return Vector3.new(_x * other, _y * other, _z * other)
}
}
+(other) {
if(other is Vector3) {
return Vector3.new(_x + other.x,
_y + other.y,
_z + other.z)
} else if(other is Num) {
return Vector3.new(_x + other,
_y + other,
_z + other)
}
}
construct new(x, y, z) {
_x = x
_y = y
_z = z
}
Sqrt() {
return Math.Sqrt_(_x, _y, _z)
}
Normalize() {
var length = this.Sqrt()
var x = _x / length
var y = _y / length
var z = _z / length
return Vector3.new(x, y, z)
}
}

View File

@@ -0,0 +1,155 @@
import "Scripts/Engine" for Engine
import "Scripts/Math" for Vector3
class Scene {
foreign static GetEntityID(name)
static GetEntity(name) {
var entId = Scene.GetEntityID(name)
var ent = Entity.new(entId)
return ent
}
foreign static EntityHasComponent(id, name)
static EntityGetComponent(id, component) {
if(this.EntityHasComponent(id, component) == false) {
Engine.Log("Tried getting a non-existent component of type: %(component) on entity with id: %(id)")
return
}
if (component == "Light") {
return Light.new(id)
} else if (component == "CharacterController") {
return CharacterController.new(id)
} else if (component == "Camera") {
return Camera.new(id)
}
}
//
// Components
//
// Transform
//foreign static SetTranslation_(e, x, y, z)
//foreign static SetRotation_(e, x, y, z)
//foreign static SetScale_(e, x, y, z)
// Light
foreign static GetLightIntensity_(e) // returns a float
foreign static SetLightIntensity_(e, intensity)
//foreign static SetLightIsVolumetric_(e, bool)
//foreign static SetLightSyncDirectionWithSky_(e, bool)
//foreign static SetLightColor_(e, r, g, b)
//foreign static GetLightColor_(e)
// Camera
foreign static SetCameraDirection_(e, x, y, z)
foreign static GetCameraDirection_(e) // returns a list x,y,z
foreign static GetCameraRight_(e) // returns a list x,y,z
//foreign static SetcameraFov(e, fov)
//foreign static GetCameraFov(e) // returns a float
// Character controller
foreign static MoveAndSlide_(e, x, y, z)
//foreign static IsOnGround_(e)
}
class Entity {
construct new(id) {
_entityId = id
}
HasComponent(component) {
return Scene.EntityHasComponent(_entityId, component)
}
GetComponent(component) {
return Scene.EntityGetComponent(_entityId, component)
}
// Foreign engine functions
/*
// Transform
foreign static SetTranslation_(e, x, y, z)
foreign static SetRotation_(e, x, y, z)
foreign static SetScale_(e, x, y, z)
// Character controller
foreign static SetVelocity(e, x, y, z)
foreign static SetStepHeight(e, x, y, z)
foreign static IsOnGround(e)
*/
// Light
//
/*
foreign static SetLightIsVolumetric_(e, bool)
foreign static SetLightSyncDirectionWithSky_(e, bool)
foreign static SetLightColor_(e, r, g, b)
// Camera
foreign static SetCameraFov_(e, fov)
foreign static SetCameraType(e, type)
foreign static SetCameraDirection_(e, x, y, z)
*/
}
class Light {
construct new(id) {
_entityId = id
}
SetIntensity(intensity) {
Scene.SetLightIntensity_(_entityId, intensity)
}
GetIntensity() {
return Scene.GetLightIntensity_(_entityId)
}
/*
SetType(type) {
this.SetType_(_entityId, type)
}
SetColor(color) {
this.SetColor_(_entityId, color.r, color.g, color.b, color.a)
}*/
}
class CharacterController {
construct new(id) {
_entityId = id
}
MoveAndSlide(vel) {
Scene.MoveAndSlide_(_entityId, vel.x, vel.y, vel.z)
}
}
class Camera {
construct new(id) {
_entityId = id
}
SetDirection(dir) {
Scene.SetCameraDirection_(_entityId, dir.x, dir.y, dir.z)
}
GetDirection() {
var dir = Scene.GetCameraDirection_(_entityId)
return Vector3.new(dir[0], dir[1], dir[2])
}
GetRight() {
var dir = Scene.GetCameraRight_(_entityId)
return Vector3.new(dir[0], dir[1], dir[2])
}
}

View File

@@ -0,0 +1,15 @@
import "Scripts/Scene" for Scene
class ScriptableEntity {
SetEntityId(id) {
_EntityID = id
}
GetComponent(component) {
return Scene.EntityGetComponent(_EntityID, component)
}
HasComponent(component) {
return Scene.EntityHasComponent(_EntityID, component)
}
}

View File

@@ -1,4 +1,7 @@
import "Scripts/Engine" for Engine, Scene, Entity
import "Scripts/Engine" for Engine
import "Scripts/Input" for Input
import "Scripts/Scene" for Scene, Entity
class Test {
init() {
System.print("hello init")
@@ -13,10 +16,12 @@ class Test {
}
static hello() {
var entity = Scene.GetEntity("Trenchbroom map")
var hasTransform = entity.HasComponent("Transform")
var hasLight = entity.HasComponent("Light")
var entity = Scene.GetEntity("Light")
var light = entity.GetComponent("Light")
light.SetIntensity(1.0)
Engine.Log("trasnform: %(hasTransform) light:%(hasLight)")
if(Input.IsMouseButtonPressed(2) == true) {
Engine.Log("RIGHT CLICK!!!!!!")
}
}
}

View File

@@ -19,6 +19,7 @@
#include "src/Resource/Project.h"
#include <src/Scene/Entities/Components/LuaScriptComponent.h>
#include <src/Core/Logger.h>
#include <src/Scene/Entities/Components/WrenScriptComponent.h>
Ref<UI::UserInterface> userInterface;
ImFont* normalFont;
ImFont* EditorInterface::bigIconFont;
@@ -80,7 +81,7 @@ void EditorInterface::DrawViewport()
glm::vec2 viewportPanelSize = glm::vec2(regionAvail.x, regionAvail.y);
if(Engine::GetCurrentWindow()->GetFrameBuffer()->GetSize() != viewportPanelSize)
Engine::GetCurrentWindow()->GetFrameBuffer()->UpdateSize(viewportPanelSize);
Engine::GetCurrentWindow()->GetFrameBuffer()->QueueResize(viewportPanelSize);
Ref<Texture> texture = Engine::GetCurrentWindow()->GetFrameBuffer()->GetTexture();
ImGui::Image((void*)texture->GetID(), regionAvail, ImVec2(0, 1), ImVec2(1, 0));
@@ -321,16 +322,22 @@ void EditorInterface::DrawEntityPropreties()
}
if (ImGui::BeginPopup("add_component_popup"))
{
if (ImGui::MenuItem("Lua script") && !m_SelectedEntity.HasComponent<LuaScriptComponent>())
m_SelectedEntity.AddComponent<LuaScriptComponent>();
if (ImGui::MenuItem("Light Component") && !m_SelectedEntity.HasComponent<LightComponent>())
m_SelectedEntity.AddComponent<LightComponent>();
if (ImGui::MenuItem("Mesh Component") && !m_SelectedEntity.HasComponent<MeshComponent>())
m_SelectedEntity.AddComponent<MeshComponent>();
if (ImGui::MenuItem("Wren Script") && !m_SelectedEntity.HasComponent<WrenScriptComponent>())
m_SelectedEntity.AddComponent<WrenScriptComponent>();
ImGui::Separator();
if (ImGui::MenuItem("Camera Component") && !m_SelectedEntity.HasComponent<CameraComponent>())
m_SelectedEntity.AddComponent<CameraComponent>();
ImGui::Separator();
if (ImGui::MenuItem("Light Component") && !m_SelectedEntity.HasComponent<LightComponent>())
m_SelectedEntity.AddComponent<LightComponent>();
ImGui::Separator();
if (ImGui::MenuItem("Mesh Component") && !m_SelectedEntity.HasComponent<MeshComponent>())
m_SelectedEntity.AddComponent<MeshComponent>();
if (ImGui::MenuItem("Quake map Component") && !m_SelectedEntity.HasComponent<QuakeMapComponent>())
m_SelectedEntity.AddComponent<QuakeMapComponent>();
ImGui::Separator();
if (ImGui::MenuItem("Character controller") && !m_SelectedEntity.HasComponent<CharacterControllerComponent>())
m_SelectedEntity.AddComponent<CharacterControllerComponent>();
if (ImGui::MenuItem("Rigidbody Component") && !m_SelectedEntity.HasComponent<RigidBodyComponent>())
{
m_SelectedEntity.AddComponent<RigidBodyComponent>();
@@ -377,32 +384,43 @@ void EditorInterface::DrawEntityPropreties()
}
if (m_SelectedEntity.HasComponent<LuaScriptComponent>()) {
if (m_SelectedEntity.HasComponent<WrenScriptComponent>()) {
std::string icon = ICON_FA_FILE;
if (ImGui::CollapsingHeader((icon + " " + "Lua script").c_str(), ImGuiTreeNodeFlags_DefaultOpen))
if (ImGui::CollapsingHeader((icon + " " + "Wren Script").c_str(), ImGuiTreeNodeFlags_DefaultOpen))
{
auto& component = m_SelectedEntity.GetComponent<LuaScriptComponent>();
auto& component = m_SelectedEntity.GetComponent<WrenScriptComponent>();
// Path
std::string path = component.Script;
char pathBuffer[256];
memset(pathBuffer, 0, sizeof(pathBuffer));
std::strncpy(pathBuffer, path.c_str(), sizeof(pathBuffer));
if (ImGui::InputText("##ScriptPath", pathBuffer, sizeof(pathBuffer)))
{
path = std::string(pathBuffer);
}
path = FileSystem::AbsoluteToRelative(std::string(pathBuffer));
ImGui::SameLine();
if (ImGui::Button("Browse"))
{
path = FileDialog::OpenFile(".map");
}
path = FileSystem::AbsoluteToRelative(FileDialog::OpenFile(".wren"));
component.Script = path;
// Class
std::string module = component.Class;
char classBuffer[256];
memset(classBuffer, 0, sizeof(classBuffer));
std::strncpy(classBuffer, module.c_str(), sizeof(classBuffer));
if (ImGui::InputText("##ScriptModule", classBuffer, sizeof(classBuffer)))
module = std::string(classBuffer);
component.Class = module;
ImGui::Separator();
}
}
if (m_SelectedEntity.HasComponent<CameraComponent>()) {
@@ -416,6 +434,18 @@ void EditorInterface::DrawEntityPropreties()
}
if (m_SelectedEntity.HasComponent<CharacterControllerComponent>())
{
if (ImGui::CollapsingHeader("Character controller", ImGuiTreeNodeFlags_DefaultOpen))
{
auto& c = m_SelectedEntity.GetComponent<CharacterControllerComponent>();
ImGui::InputFloat("Height", &c.Height);
ImGui::InputFloat("Radius", &c.Radius);
ImGui::InputFloat("Mass", &c.Mass);
ImGui::Separator();
}
}
if (m_SelectedEntity.HasComponent<MeshComponent>()) {
std::string icon = ICON_FA_TREE;
@@ -907,17 +937,26 @@ void OpenProject()
Engine::LoadProject(project);
// Create new interface named test.
userInterface = UI::UserInterface::New("test");
//userInterface = UI::UserInterface::New("test");
// Set current interface running.
Engine::GetCurrentScene()->AddInterface(userInterface);
//Engine::GetCurrentScene()->AddInterface(userInterface);
}
void OpenScene()
{
// Parse the project and load it.
std::string projectPath = FileDialog::OpenFile(".scene");
Ref<Scene> scene = Scene::New();
if (!scene->Deserialize(FileSystem::ReadFile(projectPath, true))) {
Logger::Log("Error failed loading scene: " + projectPath);
return;
}
scene->Path = FileSystem::AbsoluteToRelative(projectPath);
Engine::LoadScene(scene);
}
void EditorInterface::DrawInit()
@@ -981,6 +1020,10 @@ void EditorInterface::Draw()
m_IsEntitySelected = false;
}
ImGui::Separator();
if (ImGui::MenuItem("Set current scene as default")) {
Engine::GetProject()->DefaultScene = Engine::GetCurrentScene();
}
ImGui::Separator();
if (ImGui::MenuItem("Open scene...", "CTRL+O"))
{
OpenScene();

View File

@@ -24,8 +24,6 @@ void Engine::Init()
PhysicsManager::Get()->Init();
Logger::Log("Physics initialized");
ScriptingEngine::Init();
Logger::Log("Scripting engine initialized");
CurrentWindow = Window::Get();
Logger::Log("Window initialized");

File diff suppressed because it is too large Load Diff

View File

@@ -1,525 +0,0 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,525 +0,0 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,525 +0,0 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,525 +0,0 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_ARB_debug_output
// callback) but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

View File

@@ -38,6 +38,18 @@ void Camera::OnWindowResize(int x, int y)
float height = y;
}
void Camera::SetDirection(Vector3 direction)
{
//cam->cameraDirection.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
//cam->cameraDirection.y = sin(glm::radians(Pitch));
//cam->cameraDirection.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
//cam->cameraFront = glm::normalize(cam->cameraDirection);
//cam->cameraRight = glm::normalize(glm::cross(cam->up, cam->cameraFront));
cameraDirection = glm::normalize(direction);
cameraFront = cameraDirection;
cameraRight = glm::normalize(glm::cross(up, cameraFront));
}
glm::vec3 Camera::GetTranslation() {
return Translation;
}

View File

@@ -16,12 +16,13 @@ class Camera : public ISerializable
private:
CAMERA_TYPE m_Type;
float AspectRatio = 16.0f / 9.0f;
Vector3 Rotation = { 0.0f, 0.0f, 0.0f };
Vector3 Scale = { 1.0f, 1.0f, 1.0f };
Matrix4 m_Perspective;
public:
float AspectRatio = 16.0f / 9.0f;
// TODO: remove duplicate direction and have a proper api.
Vector3 up = Vector3(0.0f, 1.0f, 0.0f);
Vector3 cameraFront = Vector3(0.0f, 0.0f, 1.0f);
@@ -42,6 +43,8 @@ public:
void SetType(CAMERA_TYPE type);
void OnWindowResize(int x, int y);
void SetDirection(Vector3 direction);
Vector3 GetTranslation();
Vector3 GetDirection();
Matrix4 GetPerspective();

View File

@@ -55,6 +55,9 @@ void FrameBuffer::SetTexture(Ref<Texture> texture, GLenum attachment)
void FrameBuffer::Bind()
{
if (ResizeQueued)
UpdateSize(m_Size);
glBindFramebuffer(GL_FRAMEBUFFER, m_FramebufferID);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, m_Size.x, m_Size.y);
@@ -65,6 +68,12 @@ void FrameBuffer::Unbind()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::QueueResize(Vector2 size)
{
ResizeQueued = true;
m_Size = size;
}
void FrameBuffer::UpdateSize(Vector2 size)
{
m_Size = size;

View File

@@ -11,7 +11,8 @@ private:
unsigned int m_RenderBuffer;
Vector2 m_Size;
bool ResizeQueued = false;
std::map<int, Ref<Texture>> m_Textures;
Ref<Texture> m_Texture;
@@ -27,6 +28,7 @@ public:
void Bind();
void Unbind();
void QueueResize(Vector2 size);
Vector2 GetSize() const { return m_Size; }
void UpdateSize(Vector2 size);

View File

@@ -125,7 +125,7 @@ int Shader::FindUniformLocation(std::string uniform) {
if (addr == -1)
std::cout << "Warning: uniform '" << uniform << "' doesn't exists!" << std::endl;
else {
std::cout << "Info: uniform '" << uniform << "' registered." << std::endl;
//std::cout << "Info: uniform '" << uniform << "' registered." << std::endl;
UniformCache[uniform] = addr;
}

View File

@@ -33,11 +33,8 @@ void Project::Save()
void Project::SaveAs(const std::string FullPath)
{
// Serialize the scene.
BEGIN_SERIALIZE();
SERIALIZE_VAL(Name);
SERIALIZE_VAL(Description);
json j = Serialize();
// Dump.
std::string serialized_string = j.dump();

View File

@@ -8,13 +8,13 @@ void EditorCamera::Update(Timestep ts)
float x = Input::GetMouseX();
float y = Input::GetMouseY();
if (!controlled && Input::IsMouseButtonPressed(1))
if (!controlled && Input::IsMouseButtonDown(1))
{
mouseLastX = x;
mouseLastY = y;
}
controlled = Input::IsMouseButtonPressed(1);
controlled = Input::IsMouseButtonDown(1);
if (!controlled)
Input::ShowMouse();
@@ -36,30 +36,30 @@ void EditorCamera::Update(Timestep ts)
}
if (m_Type == CAMERA_TYPE::ORTHO) {
if (Input::IsKeyPressed(GLFW_KEY_RIGHT))
if (Input::IsKeyDown(GLFW_KEY_RIGHT))
Translation.x += Speed * ts;
if (Input::IsKeyPressed(GLFW_KEY_LEFT))
if (Input::IsKeyDown(GLFW_KEY_LEFT))
Translation.x -= Speed * ts;
if (Input::IsKeyPressed(GLFW_KEY_UP))
if (Input::IsKeyDown(GLFW_KEY_UP))
Translation.y += Speed * ts;
if (Input::IsKeyPressed(GLFW_KEY_DOWN))
if (Input::IsKeyDown(GLFW_KEY_DOWN))
Translation.y -= Speed * ts;
}
else {
glm::vec3 movement = glm::vec3(0, 0, 0);
if (Input::IsKeyPressed(GLFW_KEY_D))
if (Input::IsKeyDown(GLFW_KEY_D))
movement -= cameraRight * (Speed * ts);
if (Input::IsKeyPressed(GLFW_KEY_A))
if (Input::IsKeyDown(GLFW_KEY_A))
movement += cameraRight * (Speed * ts);
if (Input::IsKeyPressed(GLFW_KEY_W))
if (Input::IsKeyDown(GLFW_KEY_W))
movement += cameraDirection * (Speed * ts);
if (Input::IsKeyPressed(GLFW_KEY_S))
if (Input::IsKeyDown(GLFW_KEY_S))
movement -= cameraDirection * (Speed * ts);
if (Input::IsKeyPressed(GLFW_KEY_LEFT_SHIFT))
if (Input::IsKeyDown(GLFW_KEY_LEFT_SHIFT))
movement -= up * (Speed * ts);
if (Input::IsKeyPressed(GLFW_KEY_SPACE))
if (Input::IsKeyDown(GLFW_KEY_SPACE))
movement += up * (Speed * ts);
Translation += Vector3(movement);

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../Core/Core.h"
#include <src/UI/UserInterface.h>
class InterfaceComponent
{
Ref<UI::UserInterface> Interface;
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "../Scripting/WrenScript.h"
class WrenScriptComponent
{
public:
std::string Script;
std::string Class;
Ref<WrenScript> WrenScript;
json Serialize()
{
BEGIN_SERIALIZE();
SERIALIZE_VAL(Script);
SERIALIZE_VAL(Class);
END_SERIALIZE();
}
bool Deserialize(std::string str)
{
BEGIN_DESERIALIZE();
if (j.contains("Script"))
Script = j["Script"];
if (j.contains("Class"))
Class = j["Class"];
return true;
}
};

View File

@@ -8,6 +8,7 @@
#include "Components/QuakeMap.h"
#include "Components/LightComponent.h"
#include "Components/QuakeMap.h"
#include <src/Scene/Entities/Components/WrenScriptComponent.h>
void Entity::AddChild(Entity ent)
{
if ((int)m_EntityHandle != ent.GetHandle())
@@ -31,6 +32,8 @@ json Entity::Serialize()
SERIALIZE_OBJECT_REF_LBL("QuakeMapComponent", GetComponent<QuakeMapComponent>());
if (HasComponent<LightComponent>())
SERIALIZE_OBJECT_REF_LBL("LightComponent", GetComponent<LightComponent>());
if (HasComponent<WrenScriptComponent>())
SERIALIZE_OBJECT_REF_LBL("WrenScriptComponent", GetComponent<WrenScriptComponent>());
END_SERIALIZE();
}
@@ -43,6 +46,7 @@ bool Entity::Deserialize(const std::string& str)
DESERIALIZE_COMPONENT(CameraComponent);
DESERIALIZE_COMPONENT(QuakeMapComponent);
DESERIALIZE_COMPONENT(LightComponent);
DESERIALIZE_COMPONENT(WrenScriptComponent);
return true;
}

View File

@@ -13,7 +13,7 @@
#include "../Scene/Entities/Components/LuaScriptComponent.h"
#include <fstream>
#include <streambuf>
#include "../Scene/Entities/Components/WrenScriptComponent.h"
Ref<Scene> Scene::New()
{
return CreateRef<Scene>();
@@ -44,6 +44,8 @@ bool Scene::SetName(std::string& newName)
void Scene::OnInit()
{
ScriptingEngine::Init();
// Create physic world.
auto view = m_Registry.view<TransformComponent, RigidBodyComponent>();
for (auto e : view)
@@ -98,10 +100,19 @@ void Scene::OnInit()
// Instanciate scripts.
{
m_Registry.view<LuaScriptComponent>().each([=](auto entity, auto& nsc)
auto entities = m_Registry.view<WrenScriptComponent>();
for (auto& e : entities)
{
WrenScriptComponent& wren = entities.get<WrenScriptComponent>(e);
if (wren.Script != "" && wren.Class != "")
wren.WrenScript = CreateRef<WrenScript>(wren.Script, wren.Class, true);
});
if (wren.WrenScript != nullptr)
{
wren.WrenScript->SetScriptableEntityID((int)e);
wren.WrenScript->CallInit();
}
}
}
}
@@ -110,12 +121,21 @@ void Scene::OnExit()
PhysicsManager::Get()->Reset();
// destroy scripts.
auto entities = m_Registry.view<WrenScriptComponent>();
for (auto& e : entities)
{
m_Registry.view<NativeScriptComponent>().each([=](auto entity, auto& nsc)
WrenScriptComponent& wren = entities.get<WrenScriptComponent>(e);
if (wren.WrenScript != nullptr)
{
nsc.Instance->OnDestroy();
});
wren.WrenScript->CallExit();
}
}
ScriptingEngine::Close();
}
// update entities and some components.
@@ -129,6 +149,16 @@ void Scene::Update(Timestep ts)
});
}
// destroy scripts.
auto entities = m_Registry.view<WrenScriptComponent>();
for (auto& e : entities)
{
WrenScriptComponent& wren = entities.get<WrenScriptComponent>(e);
if (wren.WrenScript != nullptr)
wren.WrenScript->CallUpdate(ts);
}
// Update rigidbodies
PhysicsManager::Get()->Step(ts);
@@ -618,12 +648,14 @@ bool Scene::SaveAs(const std::string& path)
return true;
}
void Scene::ReloadInterfaces()
{
for (auto& i : m_Interfaces)
i->Reload();
}
void Scene::AddInterface(Ref<UI::UserInterface> interface)
{
this->m_Interfaces.push_back(interface);
@@ -643,7 +675,6 @@ json Scene::Serialize()
}
bool Scene::Deserialize(const std::string& str)
{
if (str == "")
@@ -675,5 +706,6 @@ bool Scene::Deserialize(const std::string& str)
}
}
}
return true;
}

View File

@@ -11,10 +11,6 @@ namespace ScriptAPI
class EngineModule : public ScriptModule
{
std::string ModuleName = "Engine";
std::string WrenAPI = "class Engine { \n"
"foreign static Log(msg) \n"
"}"
"";
std::string GetModuleName() override
{
@@ -24,7 +20,6 @@ namespace ScriptAPI
void RegisterModule(WrenVM* vm) override
{
RegisterMethod("Log(_)", (void*)Log);
WrenInterpretResult result = wrenInterpret(vm, "main", WrenAPI.c_str());
}
static void Log(WrenVM* vm)
@@ -33,6 +28,4 @@ namespace ScriptAPI
Logger::Log(msg);
}
};
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include "wren.h"
#include <string>
#include <src/Core/Logger.h>
#include "ScriptModule.h"
#include <iostream>
#include <wren.h>
namespace ScriptAPI
{
class EngineModule : public ScriptModule
{
std::string ModuleName = "Engine";
std::string WrenAPI = "class Engine { \n"
"foreign static Log(msg) \n"
"}"
"";
std::string GetModuleName() override
{
return "Engine";
}
void RegisterModule(WrenVM* vm) override
{
RegisterMethod("Log(_)", (void*)Log);
WrenInterpretResult result = wrenInterpret(vm, "main", WrenAPI.c_str());
}
static void Log(WrenVM* vm)
{
std::string msg = wrenGetSlotString(vm, 1);
Logger::Log(msg);
}
};
}

View File

@@ -0,0 +1,105 @@
#pragma once
#include "wren.h"
#include <string>
#include <src/Core/Logger.h>
#include "ScriptModule.h"
#include <iostream>
#include <wren.h>
#include "../Core/Maths.h"
#include "../Core/Input.h"
namespace ScriptAPI
{
class InputModule : public ScriptModule
{
std::string GetModuleName() override
{
return "Input";
}
void RegisterModule(WrenVM* vm) override
{
RegisterMethod("GetMouseX()", GetMouseX);
RegisterMethod("GetMouseY()", (void*)GetMouseY);
RegisterMethod("IsKeyDown_(_)", (void*)IsKeyDown);
RegisterMethod("IsKeyPressed_(_)", (void*)IsKeyPressed);
RegisterMethod("IsKeyReleased_(_)", (void*)IsKeyReleased);
RegisterMethod("IsMouseButtonDown_(_)", (void*)IsMouseButtonDown);
RegisterMethod("IsMouseButtonPressed_(_)", (void*)IsMouseButtonPressed);
RegisterMethod("IsMouseButtonReleased_(_)", (void*)IsMouseButtonReleased);
RegisterMethod("HideMouse()", (void*)HideMouse);
RegisterMethod("ShowMouse()", (void*)ShowMouse);
RegisterMethod("IsMouseHidden()", (void*)IsMouseHidden);
}
static void GetMouseX(WrenVM* vm)
{
wrenSetSlotDouble(vm, 0, Input::GetMouseX());
}
static void GetMouseY(WrenVM* vm)
{
wrenSetSlotDouble(vm, 0, Input::GetMouseY());
}
static void IsMouseButtonDown(WrenVM* vm)
{
int key = wrenGetSlotDouble(vm, 1);
bool result = Input::IsMouseButtonDown(key);
wrenSetSlotBool(vm, 0, result);
}
static void IsMouseButtonPressed(WrenVM* vm)
{
int key = (int)wrenGetSlotDouble(vm, 1);
bool result = Input::IsMouseButtonPressed(key);
wrenSetSlotBool(vm, 0, result);
}
static void IsMouseButtonReleased(WrenVM* vm)
{
int key = wrenGetSlotDouble(vm, 1);
bool result = Input::IsMouseButtonReleased(key);
wrenSetSlotBool(vm, 0, result);
}
static void IsKeyDown(WrenVM* vm)
{
int key = wrenGetSlotDouble(vm, 1);
bool result = Input::IsKeyDown(key);
wrenSetSlotBool(vm, 0, result);
}
static void IsKeyPressed(WrenVM* vm)
{
int key = wrenGetSlotDouble(vm, 1);
bool result = Input::IsKeyPressed(key);
wrenSetSlotBool(vm, 0, result);
}
static void IsKeyReleased(WrenVM* vm)
{
int key = wrenGetSlotDouble(vm, 1);
bool result = Input::IsKeyReleased(key);
wrenSetSlotBool(vm, 0, result);
}
static void HideMouse(WrenVM* vm)
{
Input::HideMouse();
}
static void ShowMouse(WrenVM* vm)
{
Input::ShowMouse();
}
static void IsMouseHidden(WrenVM* vm)
{
wrenSetSlotBool(vm, 0, Input::IsMouseHidden());
}
};
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include "wren.h"
#include <string>
#include <src/Core/Logger.h>
#include "ScriptModule.h"
#include <iostream>
#include <wren.h>
#include "../Core/Maths.h"
namespace ScriptAPI
{
class MathModule : public ScriptModule
{
std::string ModuleName = "Math";
std::string GetModuleName() override
{
return "Math";
}
void RegisterModule(WrenVM* vm) override
{
RegisterMethod("Sqrt_(_,_,_)", (void*)Sqrt);
}
static void Sqrt(WrenVM* vm)
{
float x = wrenGetSlotDouble(vm, 1);
float y = wrenGetSlotDouble(vm, 2);
float z = wrenGetSlotDouble(vm, 3);
float result = glm::sqrt((x * x) + (y * y) + (z * z));
wrenSetSlotDouble(vm, 0, result);
}
};
}

View File

@@ -12,6 +12,7 @@
#include <src/Scene/Entities/Components/QuakeMap.h>
#include <src/Scene/Entities/Components/CameraComponent.h>
#include <src/Scene/Entities/Components/RigidbodyComponent.h>
#include <src/Scene/Entities/Components/CharacterControllerComponent.h>
namespace ScriptAPI
{
@@ -28,6 +29,12 @@ namespace ScriptAPI
{
RegisterMethod("GetEntityID(_)", (void*)GetEntity);
RegisterMethod("EntityHasComponent(_,_)", (void*)EntityHasComponent);
RegisterMethod("SetLightIntensity_(_,_)", (void*)SetLightIntensity);
RegisterMethod("GetLightIntensity_(_)", (void*)GetLightIntensity);
RegisterMethod("SetCameraDirection_(_,_,_,_)", (void*)SetCameraDirection);
RegisterMethod("GetCameraDirection_(_)", (void*)GetCameraDirection);
RegisterMethod("GetCameraRight_(_)", (void*)GetCameraRight);
RegisterMethod("MoveAndSlide_(_,_,_,_)", (void*)MoveAndSlide);
}
static void GetEntity(WrenVM* vm)
@@ -74,5 +81,96 @@ namespace ScriptAPI
wrenSetSlotBool(vm, 0, result);
}
}
static void SetLightIntensity(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
float intensity = wrenGetSlotDouble(vm, 2);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& light = ent.GetComponent<LightComponent>();
light.Strength = intensity;
}
static void GetLightIntensity(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
float intensity = wrenGetSlotDouble(vm, 2);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& light = ent.GetComponent<LightComponent>();
wrenSetSlotDouble(vm, 0, light.Strength);
}
static void SetCameraDirection(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
float x = wrenGetSlotDouble(vm, 2);
float y = wrenGetSlotDouble(vm, 3);
float z = wrenGetSlotDouble(vm, 4);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& cam = ent.GetComponent<CameraComponent>();
cam.CameraInstance->SetDirection(Vector3(x, y, z));
}
static void GetCameraDirection(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& cam = ent.GetComponent<CameraComponent>();
Vector3 dir = cam.CameraInstance->GetDirection();
wrenEnsureSlots(vm, 4);
// set the slots
// Fill the list
wrenSetSlotNewList(vm, 0);
wrenSetSlotDouble(vm, 1, dir.x);
wrenSetSlotDouble(vm, 2, dir.y);
wrenSetSlotDouble(vm, 3, dir.z);
wrenInsertInList(vm, 0, -1, 1);
wrenInsertInList(vm, 0, -1, 2);
wrenInsertInList(vm, 0, -1, 3);
}
static void GetCameraRight(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& cam = ent.GetComponent<CameraComponent>();
Vector3 right = cam.CameraInstance->cameraRight;
// set the slots
wrenSetSlotDouble(vm, 1, right.x);
wrenSetSlotDouble(vm, 2, right.y);
wrenSetSlotDouble(vm, 3, right.z);
// Fill the list
wrenSetSlotNewList(vm, 0);
wrenInsertInList(vm, 0, 0, 1);
wrenInsertInList(vm, 0, 1, 2);
wrenInsertInList(vm, 0, 2, 3);
}
static void MoveAndSlide(WrenVM* vm)
{
int handle = wrenGetSlotDouble(vm, 1);
float x = wrenGetSlotDouble(vm, 2);
float y = wrenGetSlotDouble(vm, 3);
float z = wrenGetSlotDouble(vm, 4);
Entity ent = Entity((entt::entity)handle, Engine::GetCurrentScene().get());
auto& characterController = ent.GetComponent<CharacterControllerComponent>();
characterController.CharacterController->MoveAndSlide(Vector3(x, y, z));
}
};
}

View File

@@ -4,6 +4,9 @@
#include <src/Scripting/Modules/ScriptModule.h>
#include <src/Scripting/Modules/EngineModule.h>
#include <src/Scripting/Modules/SceneModule.h>
#include <src/Scripting/Modules/MathModule.h>
#include <src/Scripting/Modules/InputModule.h>
WrenVM* ScriptingEngine::m_WrenVM;
@@ -37,11 +40,25 @@ void writeFn(WrenVM* vm, const char* text) {
printf("%s", text);
}
bool hasEnding(std::string const& fullString, std::string const& ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
}
else {
return false;
}
}
WrenLoadModuleResult myLoadModule(WrenVM* vm, const char* name) {
WrenLoadModuleResult result = { 0 };
std::string str = FileSystem::ReadFile("resources/" + std::string(name) + ".wren", true);
std::string path = "resources/" + std::string(name);
if(!hasEnding(path, ".wren"))
path += ".wren";
std::string str = FileSystem::ReadFile(path, true);
char* c = strcpy(new char[str.length() + 1], str.c_str());
result.source = c;
return result;
}
@@ -91,6 +108,10 @@ void ScriptingEngine::Init()
RegisterModule(engineModule);
Ref<ScriptAPI::SceneModule> sceneModule = CreateRef<ScriptAPI::SceneModule>();
RegisterModule(sceneModule);
Ref<ScriptAPI::MathModule> mathModule = CreateRef<ScriptAPI::MathModule>();
RegisterModule(mathModule);
Ref<ScriptAPI::InputModule> inputModule = CreateRef<ScriptAPI::InputModule> ();
RegisterModule(inputModule);
}

View File

@@ -1,20 +1,36 @@
#include "WrenScript.h"
#include "../Core/FileSystem.h"
#include <src/Vendors/wren/src/include/wren.h>
WrenScript::WrenScript(const std::string& path, const std::string& mod)
{
WrenScript::WrenScript(const std::string& path, const std::string& mod, bool isEntity)
{
WrenVM* vm = ScriptingEngine::GetWrenVM();
// Import statement
std::string source = "import \"" + path + "\" for " + mod;
// Import file as module
wrenInterpret(vm, "main", source.c_str());
// Get handle to class
wrenEnsureSlots(vm, 1);
wrenGetVariable(vm, "main", mod.c_str(), 0);
WrenHandle* classHandle = wrenGetSlotHandle(vm, 0);
// Call the constructor
WrenHandle* constructHandle = wrenMakeCallHandle(vm, "new()");
wrenCall(vm, constructHandle);
// Retreive value of constructor
this->m_Instance = wrenGetSlotHandle(vm, 0);
// Create handles to the instance methods.
this->m_OnInitHandle = wrenMakeCallHandle(vm, "init()");
this->m_OnUpdateHandle = wrenMakeCallHandle(vm, "update(_)");
this->m_OnExitHandle = wrenMakeCallHandle(vm, "exit()");
if (isEntity)
this->m_SetEntityIDHandle = wrenMakeCallHandle(vm, "SetEntityId(_)");
}
void WrenScript::CallInit()
@@ -26,7 +42,6 @@ void WrenScript::CallInit()
void WrenScript::CallUpdate(float timestep)
{
WrenVM* vm = ScriptingEngine::GetWrenVM();
wrenEnsureSlots(vm, 2);
wrenSetSlotHandle(vm, 0, this->m_Instance);
@@ -37,6 +52,7 @@ void WrenScript::CallUpdate(float timestep)
void WrenScript::CallExit()
{
WrenVM* vm = ScriptingEngine::GetWrenVM();
wrenEnsureSlots(vm, 1);
wrenSetSlotHandle(vm, 0, this->m_Instance);
WrenInterpretResult result = wrenCall(vm, this->m_OnExitHandle);
}
@@ -56,7 +72,16 @@ void WrenScript::CallMethod(const std::string& signature)
// Not found. maybe try to register it?
if (methods.find(signature) == methods.end())
return;
wrenSetSlotHandle(vm, 0, this->m_Instance);
WrenHandle* handle = methods[signature];
WrenInterpretResult result = wrenCall(vm, handle);
}
void WrenScript::SetScriptableEntityID(int id)
{
WrenVM* vm = ScriptingEngine::GetWrenVM();
wrenSetSlotHandle(vm, 0, this->m_Instance);
wrenSetSlotDouble(vm, 1, id);
WrenInterpretResult result = wrenCall(vm, this->m_SetEntityIDHandle);
}

View File

@@ -12,8 +12,9 @@ public:
WrenHandle* m_OnInitHandle;
WrenHandle* m_OnUpdateHandle;
WrenHandle* m_OnExitHandle;
WrenHandle* m_SetEntityIDHandle;
WrenScript(const std::string& path, const std::string& mod);
WrenScript(const std::string& path, const std::string& mod, bool isEntity = false);
void CallInit();
void CallUpdate(float timestep);
@@ -21,4 +22,6 @@ public:
void RegisterMethod(const std::string& signature);
void CallMethod(const std::string& signature);
void SetScriptableEntityID(int id);
};

View File

@@ -1,5 +1,5 @@
#include "InterfaceParser.h"
#include "Styling/Stylesheet.h"
Ref<Canvas> InterfaceParser::Root = CreateRef<Canvas>();
void InterfaceParser::Iterate(const pugi::xml_node& xml_node, Ref<Node> node, int depth)
@@ -271,6 +271,11 @@ Ref<Canvas> InterfaceParser::CreateCanvas(const pugi::xml_node& xml_node)
std::string module = s[1];
node->Script = ScriptingEngine::RegisterScript(path, module);
}
if (name == "stylesheet")
{
std::string path = a.value();
node->StyleSheet = UI::StyleSheet::New(path);
}
}
return node;

View File

@@ -3,6 +3,8 @@
#include "../Core/Core.h"
#include "../Scripting/WrenScript.h"
#include <map>
#include "../Styling/Stylesheet.h"
// Base container for UI.
class Canvas : public Node
{
@@ -11,5 +13,7 @@ private:
public:
Ref<WrenScript> Script;
Ref<UI::StyleSheet> StyleSheet;
Canvas();
};

View File

@@ -1,18 +1,14 @@
#pragma once
#include "katana-parser/katana.h"
#include "../Core/FileSystem.h"
#include "../Core/Logger.h"
#include "../Core/Core.h"
#include "Style.h"
#include "katana-parser/katana.h"
#include <string>
#include <map>
#include "../Nodes/Node.h"
#include <src/UI/Styling/StyleSheetParser.h>
#include <regex>
#include <regex>
#include <src/UI/InterfaceParser.h>
#include <string>
#include <map>
namespace UI
{
class StyleSheet
@@ -24,7 +20,20 @@ namespace UI
public:
std::string Path;
static Ref<StyleSheet> New(const std::string& path);
std::vector<std::string> Split(std::string const& str, const char delim)
{
std::vector<std::string> result;
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
{
end = str.find(delim, start);
result.push_back(str.substr(start, end - start));
}
return result;
}
void AddStyleGroup(std::string selector, Ref<StyleGroup> group)
{
Styles[selector] = group;
@@ -90,7 +99,7 @@ namespace UI
std::smatch match_value;
Layout::LayoutVec4 result;
std::vector<std::string> splits = InterfaceParser::split(value, ' ');
std::vector<std::string> splits = Split(value, ' ');
int idx = 0;
for (auto& s : splits)
{
@@ -245,7 +254,6 @@ namespace UI
KatanaArray errors = Data->errors;
return false;
}
katana_dump_output(Data);
return true;
}
};

View File

@@ -14,11 +14,8 @@ namespace UI
m_Name = name;
font = FontLoader::LoadFont("resources/Fonts/RobotoMono-Regular.ttf");
m_Stylesheet = StyleSheet::New("/Interface\\Testing.css");
Root = InterfaceParser::Parse("resources/Interface/Testing.interface");
if (!Root)
{
Logger::Log("Failed to generate interface structure");
@@ -38,7 +35,6 @@ namespace UI
void UserInterface::Reload()
{
m_Stylesheet = StyleSheet::New("/Interface\\Testing.css");
Root = InterfaceParser::Parse("resources/Interface/Testing.interface");
if (!Root)
{
@@ -67,8 +63,8 @@ namespace UI
Root->YogaNode = yoga_root;
for (auto& g : Root->GetGroups())
if (m_Stylesheet->HasStyleGroup(g))
Root->ApplyStyle(m_Stylesheet->GetStyleGroup(g));
if (Root->StyleSheet->HasStyleGroup(g))
Root->ApplyStyle(Root->StyleSheet->GetStyleGroup(g));
Root->SetYogaLayout();
CreateYogaLayoutRecursive(Root, yoga_root);
}
@@ -85,8 +81,8 @@ namespace UI
n->YogaNode = newYogaNode;
for (auto& g : n->GetGroups())
if (m_Stylesheet->HasStyleGroup(g))
n->ApplyStyle(m_Stylesheet->GetStyleGroup(g));
if (Root->StyleSheet->HasStyleGroup(g))
n->ApplyStyle(Root->StyleSheet->GetStyleGroup(g));
n->SetYogaLayout();
YGNodeInsertChild(yoga_node, newYogaNode, index);

View File

@@ -16,7 +16,6 @@ namespace UI
Ref<FrameBuffer> m_Framebuffer; // Texture of the interface.
std::string m_Name;
Ref<Canvas> Root;
Ref<StyleSheet> m_Stylesheet;
YGConfigRef yoga_config;
YGNodeRef yoga_root;
public:

View File

@@ -0,0 +1,20 @@
Debug/
Release/
Debug Library/
Release Library/
x86/
x64/
*.exe
*.user
*.sdf
*.pdb
*.ipdb
*.iobj
*.suo
*.VC.opendb
*.VC.db
bin/msdf-atlas-gen
bin/*.lib
output.png
out/
build/

View File

@@ -0,0 +1,6 @@
[submodule "msdfgen"]
path = msdfgen
url = https://github.com/Chlumsky/msdfgen
[submodule "artery-font-format"]
path = artery-font-format
url = https://github.com/Chlumsky/artery-font-format

View File

@@ -0,0 +1,42 @@
#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 {
msdfgen::MSDFGeneratorConfig config;
bool scanlinePass = false;
};
/// 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 &);
}

View File

@@ -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;
};
}
}

View File

@@ -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"

View File

@@ -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);
}
}

View File

@@ -0,0 +1,26 @@
## Version 1.2 (2021-05-29)
- Updated to MSDFgen 1.9.
- Multiple fonts or font sizes can now be compiled into a single atlas.
- Added `-yorigin` option to choose if Y-coordinates increase from bottom to top or from top to bottom.
- Added `-coloringstrategy` option to select MSDF edge coloring heuristic.
- Shadron preview now properly loads floating-point image outputs in full range mode.
## Version 1.1 (2020-10-18)
- Updated to MSDFgen 1.8.
- Glyph geometry is now preprocessed by Skia to resolve irregularities which were previously unsupported and caused artifacts.
- The scanline pass and overlapping contour mode is made obsolete by this step and has been disabled by default. The preprocess step can be disabled by the new `-nopreprocess` switch and the former enabled by `-scanline` and `-overlap` respectively.
- The project can be built without the Skia library, forgoing the geometry preprocessing feature. This is controlled by the macro definition `MSDFGEN_USE_SKIA`.
- Glyphs can now also be loaded by glyph index rather than Unicode values. In the standalone version, a set of glyphs can be passed by `-glyphset` in place of `-charset`.
- Glyphs not present in the font should now be correctly skipped instead of producing a placeholder symbol.
- Added `-threads` argument to set the number of concurrent threads used during distance field generation.
### Version 1.0.1 (2020-03-09)
- Updated to MSDFgen 1.7.1.
## Version 1.0 (2020-03-08)
- Initial release.

View File

@@ -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();
}
}

View File

@@ -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, bool disableCharLiterals = false);
private:
std::set<unicode_t> codepoints;
};
}

View File

@@ -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"

View File

@@ -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;
}
}

View File

@@ -0,0 +1,185 @@
#include "FontGeometry.h"
namespace msdf_atlas {
FontGeometry::GlyphRange::GlyphRange() : glyphs(), rangeStart(), rangeEnd() { }
FontGeometry::GlyphRange::GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd) : glyphs(glyphs), rangeStart(rangeStart), rangeEnd(rangeEnd) { }
size_t FontGeometry::GlyphRange::size() const {
return glyphs->size();
}
bool FontGeometry::GlyphRange::empty() const {
return glyphs->empty();
}
const GlyphGeometry * FontGeometry::GlyphRange::begin() const {
return glyphs->data()+rangeStart;
}
const GlyphGeometry * FontGeometry::GlyphRange::end() const {
return glyphs->data()+rangeEnd;
}
FontGeometry::FontGeometry() : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(&ownGlyphs), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
FontGeometry::FontGeometry(std::vector<GlyphGeometry> *glyphStorage) : geometryScale(1), metrics(), preferredIdentifierType(GlyphIdentifierType::UNICODE_CODEPOINT), glyphs(glyphStorage), rangeStart(glyphs->size()), rangeEnd(glyphs->size()) { }
int FontGeometry::loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry, bool enableKerning) {
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
return -1;
glyphs->reserve(glyphs->size()+glyphset.size());
int loaded = 0;
for (unicode_t index : glyphset) {
GlyphGeometry glyph;
if (glyph.load(font, geometryScale, msdfgen::GlyphIndex(index), preprocessGeometry)) {
addGlyph((GlyphGeometry &&) glyph);
++loaded;
}
}
if (enableKerning)
loadKerning(font);
preferredIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
return loaded;
}
int FontGeometry::loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry, bool enableKerning) {
if (!(glyphs->size() == rangeEnd && loadMetrics(font, fontScale)))
return -1;
glyphs->reserve(glyphs->size()+charset.size());
int loaded = 0;
for (unicode_t cp : charset) {
GlyphGeometry glyph;
if (glyph.load(font, geometryScale, cp, preprocessGeometry)) {
addGlyph((GlyphGeometry &&) glyph);
++loaded;
}
}
if (enableKerning)
loadKerning(font);
preferredIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
return loaded;
}
bool FontGeometry::loadMetrics(msdfgen::FontHandle *font, double fontScale) {
if (!msdfgen::getFontMetrics(metrics, font))
return false;
if (metrics.emSize <= 0)
metrics.emSize = MSDF_ATLAS_DEFAULT_EM_SIZE;
geometryScale = fontScale/metrics.emSize;
metrics.emSize *= geometryScale;
metrics.ascenderY *= geometryScale;
metrics.descenderY *= geometryScale;
metrics.lineHeight *= geometryScale;
metrics.underlineY *= geometryScale;
metrics.underlineThickness *= geometryScale;
return true;
}
bool FontGeometry::addGlyph(const GlyphGeometry &glyph) {
if (glyphs->size() != rangeEnd)
return false;
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
if (glyph.getCodepoint())
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
glyphs->push_back(glyph);
++rangeEnd;
return true;
}
bool FontGeometry::addGlyph(GlyphGeometry &&glyph) {
if (glyphs->size() != rangeEnd)
return false;
glyphsByIndex.insert(std::make_pair(glyph.getIndex(), rangeEnd));
if (glyph.getCodepoint())
glyphsByCodepoint.insert(std::make_pair(glyph.getCodepoint(), rangeEnd));
glyphs->push_back((GlyphGeometry &&) glyph);
++rangeEnd;
return true;
}
int FontGeometry::loadKerning(msdfgen::FontHandle *font) {
int loaded = 0;
for (size_t i = rangeStart; i < rangeEnd; ++i)
for (size_t j = rangeStart; j < rangeEnd; ++j) {
double advance;
if (msdfgen::getKerning(advance, font, (*glyphs)[i].getGlyphIndex(), (*glyphs)[j].getGlyphIndex()) && advance) {
kerning[std::make_pair<int, int>((*glyphs)[i].getIndex(), (*glyphs)[j].getIndex())] = geometryScale*advance;
++loaded;
}
}
return loaded;
}
void FontGeometry::setName(const char *name) {
if (name)
this->name = name;
else
this->name.clear();
}
double FontGeometry::getGeometryScale() const {
return geometryScale;
}
const msdfgen::FontMetrics & FontGeometry::getMetrics() const {
return metrics;
}
GlyphIdentifierType FontGeometry::getPreferredIdentifierType() const {
return preferredIdentifierType;
}
FontGeometry::GlyphRange FontGeometry::getGlyphs() const {
return GlyphRange(glyphs, rangeStart, rangeEnd);
}
const GlyphGeometry * FontGeometry::getGlyph(msdfgen::GlyphIndex index) const {
std::map<int, size_t>::const_iterator it = glyphsByIndex.find(index.getIndex());
if (it != glyphsByIndex.end())
return &(*glyphs)[it->second];
return nullptr;
}
const GlyphGeometry * FontGeometry::getGlyph(unicode_t codepoint) const {
std::map<unicode_t, size_t>::const_iterator it = glyphsByCodepoint.find(codepoint);
if (it != glyphsByCodepoint.end())
return &(*glyphs)[it->second];
return nullptr;
}
bool FontGeometry::getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const {
const GlyphGeometry *glyph1 = getGlyph(index1);
if (!glyph1)
return false;
advance = glyph1->getAdvance();
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(index1.getIndex(), index2.getIndex()));
if (it != kerning.end())
advance += it->second;
return true;
}
bool FontGeometry::getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const {
const GlyphGeometry *glyph1, *glyph2;
if (!((glyph1 = getGlyph(codepoint1)) && (glyph2 = getGlyph(codepoint2))))
return false;
advance = glyph1->getAdvance();
std::map<std::pair<int, int>, double>::const_iterator it = kerning.find(std::make_pair<int, int>(glyph1->getIndex(), glyph2->getIndex()));
if (it != kerning.end())
advance += it->second;
return true;
}
const std::map<std::pair<int, int>, double> & FontGeometry::getKerning() const {
return kerning;
}
const char * FontGeometry::getName() const {
if (name.empty())
return nullptr;
return name.c_str();
}
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <utility>
#include <vector>
#include <string>
#include <map>
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "GlyphGeometry.h"
#include "Charset.h"
#define MSDF_ATLAS_DEFAULT_EM_SIZE 32.0
namespace msdf_atlas {
/// Represents the geometry of all glyphs of a given font or font variant
class FontGeometry {
public:
class GlyphRange {
public:
GlyphRange();
GlyphRange(const std::vector<GlyphGeometry> *glyphs, size_t rangeStart, size_t rangeEnd);
size_t size() const;
bool empty() const;
const GlyphGeometry * begin() const;
const GlyphGeometry * end() const;
private:
const std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
};
FontGeometry();
explicit FontGeometry(std::vector<GlyphGeometry> *glyphStorage);
/// Loads all glyphs in a glyphset (Charset elements are glyph indices), returns the number of successfully loaded glyphs
int loadGlyphset(msdfgen::FontHandle *font, double fontScale, const Charset &glyphset, bool preprocessGeometry = true, bool enableKerning = true);
/// Loads all glyphs in a charset (Charset elements are Unicode codepoints), returns the number of successfully loaded glyphs
int loadCharset(msdfgen::FontHandle *font, double fontScale, const Charset &charset, bool preprocessGeometry = true, bool enableKerning = true);
/// Only loads font metrics and geometry scale from font
bool loadMetrics(msdfgen::FontHandle *font, double fontScale);
/// Adds a loaded glyph
bool addGlyph(const GlyphGeometry &glyph);
bool addGlyph(GlyphGeometry &&glyph);
/// Loads kerning pairs for all glyphs that are currently present, returns the number of loaded kerning pairs
int loadKerning(msdfgen::FontHandle *font);
/// Sets a name to be associated with the font
void setName(const char *name);
/// Returns the geometry scale to be used when loading glyphs
double getGeometryScale() const;
/// Returns the processed font metrics
const msdfgen::FontMetrics & getMetrics() const;
/// Returns the type of identifier that was used to load glyphs
GlyphIdentifierType getPreferredIdentifierType() const;
/// Returns the list of all glyphs
GlyphRange getGlyphs() const;
/// Finds a glyph by glyph index or Unicode codepoint, returns null if not found
const GlyphGeometry * getGlyph(msdfgen::GlyphIndex index) const;
const GlyphGeometry * getGlyph(unicode_t codepoint) const;
/// Outputs the advance between two glyphs with kerning taken into consideration, returns false on failure
bool getAdvance(double &advance, msdfgen::GlyphIndex index1, msdfgen::GlyphIndex index2) const;
bool getAdvance(double &advance, unicode_t codepoint1, unicode_t codepoint2) const;
/// Returns the complete mapping of kerning pairs (by glyph indices) and their respective advance values
const std::map<std::pair<int, int>, double> & getKerning() const;
/// Returns the name associated with the font or null if not set
const char * getName() const;
private:
double geometryScale;
msdfgen::FontMetrics metrics;
GlyphIdentifierType preferredIdentifierType;
std::vector<GlyphGeometry> *glyphs;
size_t rangeStart, rangeEnd;
std::map<int, size_t> glyphsByIndex;
std::map<unicode_t, size_t> glyphsByCodepoint;
std::map<std::pair<int, int>, double> kerning;
std::vector<GlyphGeometry> ownGlyphs;
std::string name;
};
}

View File

@@ -0,0 +1,19 @@
#pragma once
namespace msdf_atlas {
/// The glyph box - its bounds in plane and atlas
struct GlyphBox {
int index;
double advance;
struct {
double l, b, r, t;
} bounds;
struct {
int x, y, w, h;
} rect;
};
}

View File

@@ -0,0 +1,170 @@
#include "GlyphGeometry.h"
#include <cmath>
#include <core/ShapeDistanceFinder.h>
namespace msdf_atlas {
GlyphGeometry::GlyphGeometry() : index(), codepoint(), geometryScale(), bounds(), advance(), box() { }
bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry) {
if (font && msdfgen::loadGlyph(shape, font, index, &advance) && shape.validate()) {
this->index = index.getIndex();
this->geometryScale = geometryScale;
codepoint = 0;
advance *= geometryScale;
#ifdef MSDFGEN_USE_SKIA
if (preprocessGeometry)
msdfgen::resolveShapeGeometry(shape);
#endif
shape.normalize();
bounds = shape.getBounds();
#ifdef MSDFGEN_USE_SKIA
if (!preprocessGeometry)
#endif
{
// Determine if shape is winded incorrectly and reverse it in that case
msdfgen::Point2 outerPoint(bounds.l-(bounds.r-bounds.l)-1, bounds.b-(bounds.t-bounds.b)-1);
if (msdfgen::SimpleTrueShapeDistanceFinder::oneShotDistance(shape, outerPoint) > 0) {
for (msdfgen::Contour &contour : shape.contours)
contour.reverse();
}
}
return true;
}
return false;
}
bool GlyphGeometry::load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry) {
msdfgen::GlyphIndex index;
if (msdfgen::getGlyphIndex(index, font, codepoint)) {
if (load(font, geometryScale, index, preprocessGeometry)) {
this->codepoint = codepoint;
return true;
}
}
return false;
}
void GlyphGeometry::edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed) {
fn(shape, angleThreshold, seed);
}
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
scale *= geometryScale;
range /= geometryScale;
box.range = range;
box.scale = scale;
if (bounds.l < bounds.r && bounds.b < bounds.t) {
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
l -= .5*range, b -= .5*range;
r += .5*range, t += .5*range;
if (miterLimit > 0)
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
double w = scale*(r-l);
double h = scale*(t-b);
box.rect.w = (int) ceil(w)+1;
box.rect.h = (int) ceil(h)+1;
box.translate.x = -l+.5*(box.rect.w-w)/scale;
box.translate.y = -b+.5*(box.rect.h-h)/scale;
} 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;
}
int GlyphGeometry::getIndex() const {
return index;
}
msdfgen::GlyphIndex GlyphGeometry::getGlyphIndex() const {
return msdfgen::GlyphIndex(index);
}
unicode_t GlyphGeometry::getCodepoint() const {
return codepoint;
}
int GlyphGeometry::getIdentifier(GlyphIdentifierType type) const {
switch (type) {
case GlyphIdentifierType::GLYPH_INDEX:
return index;
case GlyphIdentifierType::UNICODE_CODEPOINT:
return (int) codepoint;
}
return 0;
}
const msdfgen::Shape & GlyphGeometry::getShape() const {
return shape;
}
double GlyphGeometry::getAdvance() const {
return advance;
}
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;
}
msdfgen::Projection GlyphGeometry::getBoxProjection() const {
return msdfgen::Projection(msdfgen::Vector2(box.scale), box.translate);
}
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) {
double invBoxScale = 1/box.scale;
l = geometryScale*(-box.translate.x+.5*invBoxScale);
b = geometryScale*(-box.translate.y+.5*invBoxScale);
r = geometryScale*(-box.translate.x+(box.rect.w-.5)*invBoxScale);
t = geometryScale*(-box.translate.y+(box.rect.h-.5)*invBoxScale);
} else
l = 0, b = 0, r = 0, t = 0;
}
void GlyphGeometry::getQuadAtlasBounds(double &l, double &b, double &r, double &t) const {
if (box.rect.w > 0 && box.rect.h > 0) {
l = box.rect.x+.5;
b = box.rect.y+.5;
r = box.rect.x+box.rect.w-.5;
t = box.rect.y+box.rect.h-.5;
} else
l = 0, b = 0, r = 0, t = 0;
}
bool GlyphGeometry::isWhitespace() const {
return shape.contours.empty();
}
GlyphGeometry::operator GlyphBox() const {
GlyphBox box;
box.index = index;
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;
}
}

View File

@@ -0,0 +1,76 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "GlyphBox.h"
namespace msdf_atlas {
/// Represents 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, double geometryScale, msdfgen::GlyphIndex index, bool preprocessGeometry = true);
bool load(msdfgen::FontHandle *font, double geometryScale, unicode_t codepoint, bool preprocessGeometry = true);
/// Applies edge coloring to glyph shape
void edgeColoring(void (*fn)(msdfgen::Shape &, double, unsigned long long), double angleThreshold, unsigned long long seed);
/// Computes the dimensions of the glyph's box as well as the transformation for the generator function
void wrapBox(double scale, double range, double miterLimit);
/// Sets the glyph's box's position in the atlas
void placeBox(int x, int y);
/// Returns the glyph's index within the font
int getIndex() const;
/// Returns the glyph's index as a msdfgen::GlyphIndex
msdfgen::GlyphIndex getGlyphIndex() const;
/// Returns the Unicode codepoint represented by the glyph or 0 if unknown
unicode_t getCodepoint() const;
/// Returns the glyph's identifier specified by the supplied identifier type
int getIdentifier(GlyphIdentifierType type) const;
/// Returns the glyph's shape
const msdfgen::Shape & getShape() const;
/// Returns the glyph's advance
double getAdvance() 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 projection needed to generate the glyph's bitmap
msdfgen::Projection getBoxProjection() const;
/// Returns the scale needed to generate the glyph's bitmap
double getBoxScale() const;
/// Returns the translation vector needed to generate the glyph's bitmap
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:
int index;
unicode_t codepoint;
double geometryScale;
msdfgen::Shape shape;
msdfgen::Shape::Bounds bounds;
double advance;
struct {
struct {
int x, y, w, h;
} rect;
double range;
double scale;
msdfgen::Vector2 translate;
} box;
};
}

View File

@@ -0,0 +1,45 @@
#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;
std::vector<byte> errorCorrectionBuffer;
GeneratorAttributes attributes;
int threadCount;
};
}
#include "ImmediateAtlasGenerator.hpp"

View File

@@ -0,0 +1,77 @@
#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);
if (threadCount*maxBoxArea > (int) errorCorrectionBuffer.size())
errorCorrectionBuffer.resize(threadCount*maxBoxArea);
std::vector<GeneratorAttributes> threadAttributes(threadCount);
for (int i = 0; i < threadCount; ++i) {
threadAttributes[i] = attributes;
threadAttributes[i].config.errorCorrection.buffer = errorCorrectionBuffer.data()+i*maxBoxArea;
}
Workload([this, glyphs, &threadAttributes, threadBufferSize](int i, int threadNo) -> bool {
const GlyphGeometry &glyph = glyphs[i];
if (!glyph.isWhitespace()) {
int l, b, w, h;
glyph.getBoxRect(l, b, w, h);
msdfgen::BitmapRef<T, N> glyphBitmap(glyphBuffer.data()+threadNo*threadBufferSize, w, h);
GEN_FN(glyphBitmap, glyph, threadAttributes[threadNo]);
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;
}
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Viktor Chlumsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,142 @@
# Multi-channel signed distance field atlas generator
This is a utility for generating compact font atlases using [MSDFgen](https://github.com/Chlumsky/msdfgen).
The atlas generator loads a subset of glyphs from a TTF or OTF font file, generates a distance field for each of them, and tightly packs them into an atlas bitmap (example below). The finished atlas and/or its layout metadata can be exported as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file, a plain image file, a CSV sheet or a structured JSON file.
![Atlas example](https://user-images.githubusercontent.com/18639794/76163889-811f2e80-614a-11ea-9b28-1eed54dbb899.png)
A font atlas is typically stored in texture memory and used to draw text in real-time rendering contexts such as video games.
- See what's new in the [changelog](CHANGELOG.md).
## Atlas types
The atlas generator can generate the following six types of atlases.
| |Hard mask|Soft mask|SDF|PSDF|MSDF|MTSDF|
|-|-|-|-|-|-|-|
| |![Hard mask](https://user-images.githubusercontent.com/18639794/76163903-9eec9380-614a-11ea-92cb-d49485bbad31.png)|![Soft mask](https://user-images.githubusercontent.com/18639794/76163904-a1e78400-614a-11ea-912a-b220fed081cb.png)|![SDF](https://user-images.githubusercontent.com/18639794/76163905-a4e27480-614a-11ea-93eb-c80819a44e6e.png)|![PSDF](https://user-images.githubusercontent.com/18639794/76163907-a6ac3800-614a-11ea-8d97-dafc1db6711d.png)|![MSDF](https://user-images.githubusercontent.com/18639794/76163909-a9a72880-614a-11ea-9726-e825ee0dde94.png)|![MTSDF](https://user-images.githubusercontent.com/18639794/76163910-ac098280-614a-11ea-8b6b-811d864cd584.png)|
|Channels:|1 (1-bit)|1|1|1|3|4|
|Anti-aliasing:|-|Yes|Yes|Yes|Yes|Yes|
|Scalability:|-|-|Yes|Yes|Yes|Yes|
|Sharp corners:|-|-|-|-|Yes|Yes|
|Soft effects:|-|-|Yes|-|-|Yes|
|Hard effects:|-|-|-|Yes|Yes|Yes|
Notes:
- *Sharp corners* refers to preservation of corner sharpness when upscaled.
- *Soft effects* refers to the support of effects that use true distance, such as glows, rounded borders, or simplified shadows.
- *Hard effects* refers to the support of effects that use pseudo-distance, such as mitered borders or thickness adjustment.
## Getting started
This project can be used either as a library or as a standalone console program.
To start using the program immediately, there is a Windows binary available for download in the ["Releases" section](https://github.com/Chlumsky/msdf-atlas-gen/releases).
To build the project, you may use the included [Visual Studio solution](msdf-atlas-gen.sln) or the [Unix Makefile](Makefile).
## Command line arguments
Use the following command line arguments for the standalone version of the atlas generator.
### Input
- `-font <fontfile.ttf/otf>` (required) &ndash; sets the input font file.
- `-charset <charset.txt>` &ndash; sets the character set. The ASCII charset will be used if not specified. See [the syntax specification](#character-set-specification-syntax) of `charset.txt`.
- `-glyphset <glyphset.txt>` &ndash; sets the set of input glyphs using their indices within the font file. See [the syntax specification](#glyph-set-specification).
- `-fontscale <scale>` &ndash; applies a scaling transformation to the font's glyphs. Mainly to be used to generate multiple sizes in a single atlas, otherwise use [`-size`](#glyph-configuration).
- `-fontname <name>` &ndash; sets a name for the font that will be stored in certain output files as metadata.
- `-and` &ndash; separates multiple inputs to be combined into a single atlas.
### Bitmap atlas type
`-type <type>` &ndash; see [Atlas types](#atlas-types)
`<type>` can be one of:
- `hardmask` &ndash; a non-anti-aliased binary image
- `softmask` &ndash; an anti-aliased image
- `sdf` &ndash; a true signed distance field (SDF)
- `psdf` &ndash; a pseudo-distance field
- `msdf` (default) &ndash; a multi-channel signed distance field (MSDF)
- `mtsdf` &ndash; a combination of MSDF and true SDF in the alpha channel
### Atlas image format
`-format <format>`
`<format>` can be one of:
- `png` &ndash; a compressed PNG image
- `bmp` &ndash; an uncompressed BMP image
- `tiff` &ndash; an uncompressed floating-point TIFF image
- `text` &ndash; a sequence of pixel values in plain text
- `textfloat` &ndash; a sequence of floating-point pixel values in plain text
- `bin` &ndash; a sequence of pixel values encoded as raw bytes of data
- `binfloat` &ndash; a sequence of pixel values encoded as raw 32-bit floating-point values
### Atlas dimensions
`-dimensions <width> <height>` &ndash; sets fixed atlas dimensions
Alternativelly, the minimum possible dimensions may be selected automatically if a dimensions constraint is set instead:
- `-pots` &ndash; a power-of-two square
- `-potr` &ndash; a power-of-two square or rectangle (2:1)
- `-square` &ndash; any square dimensions
- `-square2` &ndash; square with even side length
- `-square4` (default) &ndash; square with side length divisible by four
### Outputs
Any non-empty subset of the following may be specified:
- `-imageout <filename.*>` &ndash; saves the atlas bitmap as a plain image file. Format matches `-format`
- `-json <filename.json>` &ndash; writes the atlas's layout data as well as other metrics into a structured JSON file
- `-csv <filename.csv>` &ndash; writes the glyph layout data into a simple CSV file
- `-arfont <filename.arfont>` &ndash; saves the atlas and its layout data as an [Artery Font](https://github.com/Chlumsky/artery-font-format) file
- `-shadronpreview <filename.shadron> <sample text>` &ndash; generates a [Shadron script](https://www.arteryengine.com/shadron/) that uses the generated atlas to draw a sample text as a preview
### Glyph configuration
- `-size <EM size>` &ndash; sets the size of the glyphs in the atlas in pixels per EM
- `-minsize <EM size>` &ndash; sets the minimum size. The largest possible size that fits the same atlas dimensions will be used
- `-emrange <EM range>` &ndash; sets the distance field range in EM's
- `-pxrange <pixel range>` (default = 2) &ndash; sets the distance field range in output pixels
### Distance field generator settings
- `-angle <angle>` &ndash; sets the minimum angle between adjacent edges to be considered a corner. Append D for degrees (`msdf` / `mtsdf` only)
- `-coloringstrategy <simple / inktrap / distance>` &ndash; selects the edge coloring heuristic (`msdf` / `mtsdf` only)
- `-errorcorrection <mode>` &ndash; selects the error correction algorithm. Use `help` as mode for more information (`msdf` / `mtsdf` only)
- `-miterlimit <value>` &ndash; sets the miter limit that limits the extension of each glyph's bounding box due to very sharp corners (`psdf` / `msdf` / `mtsdf` only)
- `-overlap` &ndash; switches to distance field generator with support for overlapping contours
- `-nopreprocess` &ndash; disables path preprocessing which resolves self-intersections and overlapping contours
- `-scanline` &ndash; performs an additional scanline pass to fix the signs of the distances
- `-seed <N>` &ndash; sets the initial seed for the edge coloring heuristic
- `-threads <N>` &ndash; sets the number of threads for the parallel computation (0 = auto)
Use `-help` for an exhaustive list of options.
## Character set specification syntax
The character set file is a text file with UTF-8 or ASCII encoding.
The characters can be denoted in the following ways:
- Single character: `'A'` (UTF-8 encoded), `65` (decimal Unicode), `0x41` (hexadecimal Unicode)
- Range of characters: `['A', 'Z']`, `[65, 90]`, `[0x41, 0x5a]`
- String of characters: `"ABCDEFGHIJKLMNOPQRSTUVWXYZ"` (UTF-8 encoded)
The entries should be separated by commas or whitespace.
In between quotation marks, backslash is used as the escape character (e.g. `'\''`, `'\\'`, `"!\"#"`).
The order in which characters appear is not taken into consideration.
Additionally, the include directive can be used to include other charset files and combine character sets in a hierarchical way.
It must be written on a separate line:
`@include "base-charset.txt"`
### Glyph set specification
The syntax of the glyph set specification is mostly the same as that of a character set, but only numeric values (decimal and hexadecimal) are allowed.

View File

@@ -0,0 +1,14 @@
#pragma once
namespace msdf_atlas {
struct Rectangle {
int x, y, w, h;
};
struct OrientedRectangle : Rectangle {
bool rotated;
};
}

View File

@@ -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();
}
}

View File

@@ -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);
};
}

View File

@@ -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;
};
}

View File

@@ -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 && ((maxScale = 2*minScale), TRY_PACK(maxScale)))
minScale = maxScale;
} else {
while (minScale > 1e-32 && ((minScale = .5*maxScale), !TRY_PACK(minScale)))
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;
}
}

View File

@@ -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);
};
}

View File

@@ -0,0 +1,50 @@
#include "Workload.h"
#include <vector>
#include <thread>
#include <atomic>
#include <algorithm>
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(std::min(threadCount, chunks));
return false;
}
}

View File

@@ -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);
};
}

View File

@@ -0,0 +1,191 @@
#include "artery-font-export.h"
/*#include <std-artery-font.h>
#include <artery-font/stdio-serialization.h>
#include "GlyphGeometry.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;
}
static artery_font::CodepointType convertCodepointType(GlyphIdentifierType glyphIdentifierType) {
switch (glyphIdentifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
return artery_font::CP_INDEXED;
case GlyphIdentifierType::UNICODE_CODEPOINT:
return artery_font::CP_UNICODE;
}
return artery_font::CP_UNSPECIFIED;
}
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(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) {
artery_font::StdArteryFont<REAL> arfont = { };
arfont.metadataFormat = artery_font::METADATA_NONE;
for (int i = 0; i < fontCount; ++i) {
const FontGeometry &font = fonts[i];
GlyphIdentifierType identifierType = font.getPreferredIdentifierType();
const msdfgen::FontMetrics &fontMetrics = font.getMetrics();
artery_font::StdFontVariant<REAL> fontVariant = { };
fontVariant.codepointType = convertCodepointType(identifierType);
fontVariant.imageType = convertImageType(properties.imageType);
fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
if (properties.imageType != ImageType::HARD_MASK)
fontVariant.metrics.distanceRange = REAL(properties.pxRange);
fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
fontVariant.metrics.lineHeight = REAL(fontMetrics.lineHeight);
fontVariant.metrics.underlineY = REAL(fontMetrics.underlineY);
fontVariant.metrics.underlineThickness = REAL(fontMetrics.underlineThickness);
const char *name = font.getName();
if (name)
fontVariant.name.string = name;
fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(font.getGlyphs().size());
int j = 0;
for (const GlyphGeometry &glyphGeom : font.getGlyphs()) {
artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[j++];
glyph.codepoint = glyphGeom.getIdentifier(identifierType);
glyph.image = 0;
double l, b, r, t;
glyphGeom.getQuadPlaneBounds(l, b, r, t);
glyph.planeBounds.l = REAL(l);
glyph.planeBounds.b = REAL(b);
glyph.planeBounds.r = REAL(r);
glyph.planeBounds.t = REAL(t);
glyphGeom.getQuadAtlasBounds(l, b, r, t);
glyph.imageBounds.l = REAL(l);
glyph.imageBounds.b = REAL(b);
glyph.imageBounds.r = REAL(r);
glyph.imageBounds.t = REAL(t);
glyph.advance.h = REAL(glyphGeom.getAdvance());
glyph.advance.v = REAL(0);
}
switch (identifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
artery_font::KernPair<REAL> kernPair = { };
kernPair.codepoint1 = elem.first.first;
kernPair.codepoint2 = elem.first.second;
kernPair.advance.h = REAL(elem.second);
fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair);
}
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(elem.first.first));
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(elem.first.second));
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
artery_font::KernPair<REAL> kernPair = { };
kernPair.codepoint1 = glyph1->getCodepoint();
kernPair.codepoint2 = glyph2->getCodepoint();
kernPair.advance.h = REAL(elem.second);
fontVariant.kernPairs.vector.push_back((artery_font::KernPair<REAL> &&) kernPair);
}
}
break;
}
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(properties.imageType);
switch (properties.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.data = artery_font::StdByteArray(N*sizeof(T)*atlas.width*atlas.height);
switch (properties.yDirection) {
case YDirection::BOTTOM_UP:
image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP;
memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height);
break;
case YDirection::TOP_DOWN: {
image.rawBinaryFormat.orientation = artery_font::ORIENTATION_TOP_DOWN;
byte *imageData = (byte *) image.data;
for (int y = atlas.height-1; y >= 0; --y) {
memcpy(imageData, atlas.pixels+N*atlas.width*y, N*sizeof(T)*atlas.width);
imageData += N*sizeof(T)*atlas.width;
}
break;
}
}
break;
default:
return false;
}
arfont.images.vector.push_back((artery_font::StdImage &&) image);
}
return artery_font::writeFile(arfont, filename);
}
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
}
*/

View File

@@ -0,0 +1,23 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "FontGeometry.h"
namespace msdf_atlas {
struct ArteryFontExportProperties {
double fontSize;
double pxRange;
ImageType imageType;
ImageFormat imageFormat;
YDirection yDirection;
};
/// Encodes the atlas bitmap and its layout into an Artery Atlas Font file
template <typename REAL, typename T, int N>
bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties);
}

View File

@@ -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]);
}
}
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,250 @@
#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, bool disableCharLiterals) {
if (FILE *f = fopen(filename, "rb")) {
enum {
CLEAR,
TIGHT,
RANGE_BRACKET,
RANGE_START,
RANGE_SEPARATOR,
RANGE_END
} state = CLEAR;
std::string buffer;
std::vector<unicode_t> unicodeBuffer;
unicode_t rangeStart = 0;
for (int c = fgetc(f), start = true; c >= 0; start = false) {
switch (c) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // number
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR))
goto FAIL;
buffer.push_back((char) c);
c = readWord(buffer, f);
{
int cp;
if (!parseInt(cp, buffer.c_str()))
goto FAIL;
switch (state) {
case CLEAR:
if (cp >= 0)
add((unicode_t) cp);
state = TIGHT;
break;
case RANGE_BRACKET:
rangeStart = (unicode_t) cp;
state = RANGE_START;
break;
case RANGE_SEPARATOR:
for (unicode_t u = rangeStart; (int) u <= cp; ++u)
add(u);
state = RANGE_END;
break;
default:;
}
}
buffer.clear();
continue; // next character already read
case '\'': // single UTF-8 character
if (!(state == CLEAR || state == RANGE_BRACKET || state == RANGE_SEPARATOR) || disableCharLiterals)
goto FAIL;
if (!readString(buffer, f, '\''))
goto FAIL;
utf8Decode(unicodeBuffer, buffer.c_str());
if (unicodeBuffer.size() == 1) {
switch (state) {
case CLEAR:
if (unicodeBuffer[0] > 0)
add(unicodeBuffer[0]);
state = TIGHT;
break;
case RANGE_BRACKET:
rangeStart = unicodeBuffer[0];
state = RANGE_START;
break;
case RANGE_SEPARATOR:
for (unicode_t u = rangeStart; u <= unicodeBuffer[0]; ++u)
add(u);
state = RANGE_END;
break;
default:;
}
} else
goto FAIL;
unicodeBuffer.clear();
buffer.clear();
break;
case '"': // string of UTF-8 characters
if (state != CLEAR || disableCharLiterals)
goto FAIL;
if (!readString(buffer, f, '"'))
goto FAIL;
utf8Decode(unicodeBuffer, buffer.c_str());
for (unicode_t cp : unicodeBuffer)
add(cp);
unicodeBuffer.clear();
buffer.clear();
state = TIGHT;
break;
case '[': // character range start
if (state != CLEAR)
goto FAIL;
state = RANGE_BRACKET;
break;
case ']': // character range end
if (state == RANGE_END)
state = TIGHT;
else
goto FAIL;
break;
case '@': // annotation
if (state != CLEAR)
goto FAIL;
c = readWord(buffer, f);
if (buffer == "include") {
while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
c = fgetc(f);
if (c != '"')
goto FAIL;
buffer.clear();
if (!readString(buffer, f, '"'))
goto FAIL;
load(combinePath(filename, buffer.c_str()).c_str());
state = TIGHT;
} else
goto FAIL;
buffer.clear();
break;
case ',': case ';': // separator
if (!(state == CLEAR || state == TIGHT)) {
if (state == RANGE_START)
state = RANGE_SEPARATOR;
else
goto FAIL;
} // else treat as whitespace
case ' ': case '\n': case '\r': case '\t': // whitespace
if (state == TIGHT)
state = CLEAR;
break;
case 0xef: // UTF-8 byte order mark
if (start) {
if (!(fgetc(f) == 0xbb && fgetc(f) == 0xbf))
goto FAIL;
break;
}
default: // unexpected character
goto FAIL;
}
c = fgetc(f);
}
fclose(f);
return state == CLEAR || state == TIGHT;
FAIL:
fclose(f);
return false;
}
return false;
}
}

View File

@@ -0,0 +1,45 @@
#include "csv-export.h"
#include <cstdio>
#include "GlyphGeometry.h"
namespace msdf_atlas {
bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename) {
FILE *f = fopen(filename, "w");
if (!f)
return false;
for (int i = 0; i < fontCount; ++i) {
for (const GlyphGeometry &glyph : fonts[i].getGlyphs()) {
double l, b, r, t;
if (fontCount > 1)
fprintf(f, "%d,", i);
fprintf(f, "%d,%.17g,", glyph.getIdentifier(fonts[i].getPreferredIdentifierType()), glyph.getAdvance());
glyph.getQuadPlaneBounds(l, b, r, t);
switch (yDirection) {
case YDirection::BOTTOM_UP:
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, b, r, t);
break;
case YDirection::TOP_DOWN:
fprintf(f, "%.17g,%.17g,%.17g,%.17g,", l, -t, r, -b);
break;
}
glyph.getQuadAtlasBounds(l, b, r, t);
switch (yDirection) {
case YDirection::BOTTOM_UP:
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, b, r, t);
break;
case YDirection::TOP_DOWN:
fprintf(f, "%.17g,%.17g,%.17g,%.17g\n", l, atlasHeight-t, r, atlasHeight-b);
break;
}
}
}
fclose(f);
return true;
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "FontGeometry.h"
namespace msdf_atlas {
/**
* Writes the positioning data and atlas layout of the glyphs into a CSV file
* The columns are: font variant index (if fontCount > 1), glyph identifier (index or Unicode), horizontal advance, plane bounds (l, b, r, t), atlas bounds (l, b, r, t)
*/
bool exportCSV(const FontGeometry *fonts, int fontCount, int atlasWidth, int atlasHeight, YDirection yDirection, const char *filename);
}

View File

@@ -0,0 +1,52 @@
#include "glyph-generators.h"
namespace msdf_atlas {
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.getBoxProjection(), glyph.getBoxRange(), attribs.config);
if (attribs.scanlinePass)
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
}
void psdfGenerator(const msdfgen::BitmapRef<float, 1> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::generatePseudoSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), attribs.config);
if (attribs.scanlinePass)
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
}
void msdfGenerator(const msdfgen::BitmapRef<float, 3> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::MSDFGeneratorConfig config = attribs.config;
if (attribs.scanlinePass)
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
msdfgen::generateMSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
if (attribs.scanlinePass) {
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
}
}
}
void mtsdfGenerator(const msdfgen::BitmapRef<float, 4> &output, const GlyphGeometry &glyph, const GeneratorAttributes &attribs) {
msdfgen::MSDFGeneratorConfig config = attribs.config;
if (attribs.scanlinePass)
config.errorCorrection.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
msdfgen::generateMTSDF(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
if (attribs.scanlinePass) {
msdfgen::distanceSignCorrection(output, glyph.getShape(), glyph.getBoxProjection(), MSDF_ATLAS_GLYPH_FILL_RULE);
if (attribs.config.errorCorrection.mode != msdfgen::ErrorCorrectionConfig::DISABLED) {
config.errorCorrection.mode = attribs.config.errorCorrection.mode;
config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
msdfgen::msdfErrorCorrection(output, glyph.getShape(), glyph.getBoxProjection(), glyph.getBoxRange(), config);
}
}
}
}

View File

@@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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, YDirection outputYDirection = YDirection::BOTTOM_UP);
}
#include "image-save.hpp"

View File

@@ -0,0 +1,172 @@
#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, YDirection outputYDirection);
template <int N>
bool saveImageBinaryLE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
template <int N>
bool saveImageBinaryBE(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
template <int N>
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection);
template <int N>
bool saveImageText(const msdfgen::BitmapConstRef<float, N> &bitmap, const char *filename, YDirection outputYDirection);
template <int N>
bool saveImage(const msdfgen::BitmapConstRef<byte, N> &bitmap, ImageFormat format, const char *filename, YDirection outputYDirection) {
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, outputYDirection);
case ImageFormat::TEXT_FLOAT:
return false;
case ImageFormat::BINARY:
return saveImageBinary(bitmap, filename, outputYDirection);
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, YDirection outputYDirection) {
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, outputYDirection);
case ImageFormat::BINARY:
return false;
case ImageFormat::BINARY_FLOAT:
return saveImageBinaryLE(bitmap, filename, outputYDirection);
case ImageFormat::BINARY_FLOAT_BE:
return saveImageBinaryBE(bitmap, filename, outputYDirection);
default:;
}
return false;
}
template <int N>
bool saveImageBinary(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection) {
bool success = false;
if (FILE *f = fopen(filename, "wb")) {
int written = 0;
switch (outputYDirection) {
case YDirection::BOTTOM_UP:
written = fwrite(bitmap.pixels, 1, N*bitmap.width*bitmap.height, f);
break;
case YDirection::TOP_DOWN:
for (int y = bitmap.height-1; y >= 0; --y)
written += fwrite(bitmap.pixels+N*bitmap.width*y, 1, N*bitmap.width, f);
break;
}
success = written == 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, YDirection outputYDirection) {
bool success = false;
if (FILE *f = fopen(filename, "wb")) {
int written = 0;
switch (outputYDirection) {
case YDirection::BOTTOM_UP:
written = fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f);
break;
case YDirection::TOP_DOWN:
for (int y = bitmap.height-1; y >= 0; --y)
written += fwrite(bitmap.pixels+N*bitmap.width*y, sizeof(float), N*bitmap.width, f);
break;
}
success = written == 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, YDirection outputYDirection) {
bool success = false;
if (FILE *f = fopen(filename, "wb")) {
int written = 0;
for (int y = 0; y < bitmap.height; ++y) {
const float *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y);
for (int x = 0; x < bitmap.width; ++x) {
const unsigned char *b = reinterpret_cast<const unsigned char *>(p++);
for (int i = sizeof(float)-1; i >= 0; --i)
written += fwrite(b+i, 1, 1, f);
}
}
success = written == sizeof(float)*N*bitmap.width*bitmap.height;
fclose(f);
}
return success;
}
template <int N>
bool saveImageText(const msdfgen::BitmapConstRef<byte, N> &bitmap, const char *filename, YDirection outputYDirection) {
bool success = false;
if (FILE *f = fopen(filename, "wb")) {
for (int y = 0; y < bitmap.height; ++y) {
const byte *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : 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, YDirection outputYDirection) {
bool success = false;
if (FILE *f = fopen(filename, "wb")) {
for (int y = 0; y < bitmap.height; ++y) {
const float *p = bitmap.pixels+N*bitmap.width*(outputYDirection == YDirection::TOP_DOWN ? bitmap.height-y-1 : y);
for (int x = 0; x < N*bitmap.width; ++x) {
fprintf(f, x ? " %g" : "%g", *p++);
}
fprintf(f, "\n");
}
fclose(f);
}
return success;
}
}

View File

@@ -0,0 +1,186 @@
#include "json-export.h"
#include <string>
#include "GlyphGeometry.h"
namespace msdf_atlas {
static std::string escapeJsonString(const char *str) {
char uval[7] = "\\u0000";
std::string outStr;
while (*str) {
switch (*str) {
case '\\':
outStr += "\\\\";
break;
case '"':
outStr += "\\\"";
break;
case '\n':
outStr += "\\n";
break;
case '\r':
outStr += "\\r";
break;
case '\t':
outStr += "\\t";
break;
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: /* \\t */ /* \\n */ case 0x0b: case 0x0c: /* \\r */ case 0x0e: case 0x0f:
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
uval[4] = '0'+(*str >= 0x10);
uval[5] = "0123456789abcdef"[*str&0x0f];
outStr += uval;
break;
default:
outStr.push_back(*str);
}
++str;
}
return outStr;
}
static const char * imageTypeString(ImageType type) {
switch (type) {
case ImageType::HARD_MASK:
return "hardmask";
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(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, YDirection yDirection, const char *filename, bool kerning) {
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);
fprintf(f, "\"yOrigin\":\"%s\"", yDirection == YDirection::TOP_DOWN ? "top" : "bottom");
} fputs("},", f);
if (fontCount > 1)
fputs("\"variants\":[", f);
for (int i = 0; i < fontCount; ++i) {
const FontGeometry &font = fonts[i];
if (fontCount > 1)
fputs(i == 0 ? "{" : ",{", f);
// Font name
const char *name = font.getName();
if (name)
fprintf(f, "\"name\":\"%s\",", escapeJsonString(name).c_str());
// Font metrics
fputs("\"metrics\":{", f); {
double yFactor = yDirection == YDirection::TOP_DOWN ? -1 : 1;
const msdfgen::FontMetrics &metrics = font.getMetrics();
fprintf(f, "\"emSize\":%.17g,", metrics.emSize);
fprintf(f, "\"lineHeight\":%.17g,", metrics.lineHeight);
fprintf(f, "\"ascender\":%.17g,", yFactor*metrics.ascenderY);
fprintf(f, "\"descender\":%.17g,", yFactor*metrics.descenderY);
fprintf(f, "\"underlineY\":%.17g,", yFactor*metrics.underlineY);
fprintf(f, "\"underlineThickness\":%.17g", metrics.underlineThickness);
} fputs("},", f);
// Glyph mapping
fputs("\"glyphs\":[", f);
bool firstGlyph = true;
for (const GlyphGeometry &glyph : font.getGlyphs()) {
fputs(firstGlyph ? "{" : ",{", f);
switch (font.getPreferredIdentifierType()) {
case GlyphIdentifierType::GLYPH_INDEX:
fprintf(f, "\"index\":%d,", glyph.getIndex());
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
fprintf(f, "\"unicode\":%u,", glyph.getCodepoint());
break;
}
fprintf(f, "\"advance\":%.17g", glyph.getAdvance());
double l, b, r, t;
glyph.getQuadPlaneBounds(l, b, r, t);
if (l || b || r || t) {
switch (yDirection) {
case YDirection::BOTTOM_UP:
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
break;
case YDirection::TOP_DOWN:
fprintf(f, ",\"planeBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, -t, r, -b);
break;
}
}
glyph.getQuadAtlasBounds(l, b, r, t);
if (l || b || r || t) {
switch (yDirection) {
case YDirection::BOTTOM_UP:
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"bottom\":%.17g,\"right\":%.17g,\"top\":%.17g}", l, b, r, t);
break;
case YDirection::TOP_DOWN:
fprintf(f, ",\"atlasBounds\":{\"left\":%.17g,\"top\":%.17g,\"right\":%.17g,\"bottom\":%.17g}", l, atlasHeight-t, r, atlasHeight-b);
break;
}
}
fputs("}", f);
firstGlyph = false;
} fputs("]", f);
// Kerning pairs
if (kerning) {
fputs(",\"kerning\":[", f);
bool firstPair = true;
switch (font.getPreferredIdentifierType()) {
case GlyphIdentifierType::GLYPH_INDEX:
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
fputs(firstPair ? "{" : ",{", f);
fprintf(f, "\"index1\":%d,", kernPair.first.first);
fprintf(f, "\"index2\":%d,", kernPair.first.second);
fprintf(f, "\"advance\":%.17g", kernPair.second);
fputs("}", f);
firstPair = false;
}
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (const std::pair<std::pair<int, int>, double> &kernPair : font.getKerning()) {
const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.first));
const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(kernPair.first.second));
if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
fputs(firstPair ? "{" : ",{", f);
fprintf(f, "\"unicode1\":%u,", glyph1->getCodepoint());
fprintf(f, "\"unicode2\":%u,", glyph2->getCodepoint());
fprintf(f, "\"advance\":%.17g", kernPair.second);
fputs("}", f);
firstPair = false;
}
}
break;
} fputs("]", f);
}
if (fontCount > 1)
fputs("}", f);
}
if (fontCount > 1)
fputs("]", f);
fputs("}\n", f);
fclose(f);
return true;
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <msdfgen.h>
#include <msdfgen-ext.h>
#include "types.h"
#include "FontGeometry.h"
namespace msdf_atlas {
/// Writes the font and glyph metrics and atlas layout data into a comprehensive JSON file
bool exportJSON(const FontGeometry *fonts, int fontCount, double fontSize, double pxRange, int atlasWidth, int atlasHeight, ImageType imageType, YDirection yDirection, const char *filename, bool kerning);
}

View File

@@ -0,0 +1,999 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR v1.2 (2021-05-29) - standalone console program
* --------------------------------------------------------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2020 - 2021
*
*/
#ifdef MSDF_ATLAS_STANDALONE
#define _USE_MATH_DEFINES
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cassert>
#include <vector>
#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_PIXEL_RANGE 2.0
#define SDF_ERROR_ESTIMATE_PRECISION 19
#define GLYPH_FILL_RULE msdfgen::FILL_NONZERO
#define LCG_MULTIPLIER 6364136223846793005ull
#define LCG_INCREMENT 1442695040888963407ull
#ifdef MSDFGEN_USE_SKIA
#define TITLE_SUFFIX " & Skia"
#define EXTRA_UNDERLINE "-------"
#else
#define TITLE_SUFFIX
#define EXTRA_UNDERLINE
#endif
static const char * const helpText = R"(
MSDF Atlas Generator by Viktor Chlumsky v)" MSDF_ATLAS_VERSION R"( (with MSDFGEN v)" MSDFGEN_VERSION TITLE_SUFFIX R"()
----------------------------------------------------------------)" EXTRA_UNDERLINE 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.
-glyphset <filename>
Specifies the set of input glyphs as glyph indices within the font file.
-fontscale <scale>
Specifies the scale to be applied to the glyph geometry of the font.
-fontname <name>
Specifies a name for the font that will be propagated into the output files as metadata.
-and
Separates multiple inputs to be combined into a single atlas.
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
-yorigin <bottom / top>
Determines whether the Y-axis is oriented upwards (bottom origin, default) or downwards (top origin).
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>
Specifies 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.
-nokerning
Disables inclusion of kerning pair table in output files.
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)
-coloringstrategy <simple / inktrap / distance>
Selects the strategy of the edge coloring heuristic.
-errorcorrection <mode>
Changes the MSDF/MTSDF error correction mode. Use -errorcorrection help for a list of valid modes.
-errordeviationratio <ratio>
Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
-errorimproveratio <ratio>
Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
-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))"
#ifdef MSDFGEN_USE_SKIA
R"(
-overlap
Switches to distance field generator with support for overlapping contours.
-nopreprocess
Disables path preprocessing which resolves self-intersections and overlapping contours.
-scanline
Performs an additional scanline pass to fix the signs of the distances.)"
#else
R"(
-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.)"
#endif
R"(
-seed <N>
Sets the initial seed for the edge coloring heuristic.
-threads <N>
Sets the number of threads for the parallel computation. (0 = auto)
)";
static const char *errorCorrectionHelpText = R"(
ERROR CORRECTION MODES
auto-fast
Detects inversion artifacts and distance errors that do not affect edges by range testing.
auto-full
Detects inversion artifacts and distance errors that do not affect edges by exact distance evaluation.
auto-mixed (default)
Detects inversions by distance evaluation and distance errors that do not affect edges by range testing.
disabled
Disables error correction.
distance-fast
Detects distance errors by range testing. Does not care if edges and corners are affected.
distance-full
Detects distance errors by exact distance evaluation. Does not care if edges and corners are affected, slow.
edge-fast
Detects inversion artifacts only by range testing.
edge-full
Detects inversion artifacts only by exact distance evaluation.
help
Displays this help.
)";
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;
}
struct FontInput {
const char *fontFilename;
GlyphIdentifierType glyphIdentifierType;
const char *charsetFilename;
double fontScale;
const char *fontName;
};
struct Configuration {
ImageType imageType;
ImageFormat imageFormat;
YDirection yDirection;
int width, height;
double emSize;
double pxRange;
double angleThreshold;
double miterLimit;
void (*edgeColoring)(msdfgen::Shape &, double, unsigned long long);
bool expensiveColoring;
unsigned long long coloringSeed;
GeneratorAttributes generatorAttributes;
bool preprocessGeometry;
bool kerning;
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, const std::vector<FontGeometry> &fonts, 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, config.yDirection))
puts("Atlas image file saved.");
else {
success = false;
puts("Failed to save the atlas as an image file.");
}
}
if (config.arteryFontFilename) {
ArteryFontExportProperties arfontProps;
arfontProps.fontSize = config.emSize;
arfontProps.pxRange = config.pxRange;
arfontProps.imageType = config.imageType;
arfontProps.imageFormat = config.imageFormat;
arfontProps.yDirection = config.yDirection;
if (exportArteryFont<float>(fonts.data(), fonts.size(), bitmap, config.arteryFontFilename, arfontProps))
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;
std::vector<FontInput> fontInputs;
FontInput fontInput = { };
Configuration config = { };
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
fontInput.fontScale = -1;
config.imageType = ImageType::MSDF;
config.imageFormat = ImageFormat::UNSPECIFIED;
config.yDirection = YDirection::BOTTOM_UP;
config.edgeColoring = msdfgen::edgeColoringInkTrap;
config.kerning = true;
const char *imageFormatName = nullptr;
int fixedWidth = -1, fixedHeight = -1;
config.preprocessGeometry = (
#ifdef MSDFGEN_USE_SKIA
true
#else
false
#endif
);
config.generatorAttributes.config.overlapSupport = !config.preprocessGeometry;
config.generatorAttributes.scanlinePass = !config.preprocessGeometry;
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 = 0;
// Parse command line
int argPos = 1;
bool suggestHelp = false;
bool explicitErrorCorrectionMode = 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) {
fontInput.fontFilename = argv[++argPos];
++argPos;
continue;
}
ARG_CASE("-charset", 1) {
fontInput.charsetFilename = argv[++argPos];
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
++argPos;
continue;
}
ARG_CASE("-glyphset", 1) {
fontInput.charsetFilename = argv[++argPos];
fontInput.glyphIdentifierType = GlyphIdentifierType::GLYPH_INDEX;
++argPos;
continue;
}
ARG_CASE("-fontscale", 1) {
double fs;
if (!(parseDouble(fs, argv[++argPos]) && fs > 0))
ABORT("Invalid font scale argument. Use -fontscale <font scale> with a positive real number.");
fontInput.fontScale = fs;
++argPos;
continue;
}
ARG_CASE("-fontname", 1) {
fontInput.fontName = argv[++argPos];
++argPos;
continue;
}
ARG_CASE("-and", 0) {
if (!fontInput.fontFilename && !fontInput.charsetFilename && fontInput.fontScale < 0)
ABORT("No font, character set, or font scale specified before -and separator.");
if (!fontInputs.empty() && !memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
ABORT("No changes between subsequent inputs. A different font, character set, or font scale must be set inbetween -and separators.");
fontInputs.push_back(fontInput);
fontInput.fontName = nullptr;
++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("-yorigin", 1) {
arg = argv[++argPos];
if (!strcmp(arg, "bottom"))
config.yDirection = YDirection::BOTTOM_UP;
else if (!strcmp(arg, "top"))
config.yDirection = YDirection::TOP_DOWN;
else
ABORT("Invalid Y-axis origin. Use bottom or top.");
++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) {
msdfgen::ErrorCorrectionConfig &ec = config.generatorAttributes.config.errorCorrection;
if (!strcmp(argv[argPos+1], "disabled") || !strcmp(argv[argPos+1], "0") || !strcmp(argv[argPos+1], "none")) {
ec.mode = msdfgen::ErrorCorrectionConfig::DISABLED;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "default") || !strcmp(argv[argPos+1], "auto") || !strcmp(argv[argPos+1], "auto-mixed") || !strcmp(argv[argPos+1], "mixed")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::CHECK_DISTANCE_AT_EDGE;
} else if (!strcmp(argv[argPos+1], "auto-fast") || !strcmp(argv[argPos+1], "fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "auto-full") || !strcmp(argv[argPos+1], "full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "distance") || !strcmp(argv[argPos+1], "distance-fast") || !strcmp(argv[argPos+1], "indiscriminate") || !strcmp(argv[argPos+1], "indiscriminate-fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::INDISCRIMINATE;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "distance-full") || !strcmp(argv[argPos+1], "indiscriminate-full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::INDISCRIMINATE;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "edge-fast")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_ONLY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "edge") || !strcmp(argv[argPos+1], "edge-full")) {
ec.mode = msdfgen::ErrorCorrectionConfig::EDGE_ONLY;
ec.distanceCheckMode = msdfgen::ErrorCorrectionConfig::ALWAYS_CHECK_DISTANCE;
} else if (!strcmp(argv[argPos+1], "help")) {
puts(errorCorrectionHelpText);
return 0;
} else
ABORT("Unknown error correction mode. Use -errorcorrection help for more information.");
explicitErrorCorrectionMode = true;
argPos += 2;
continue;
}
ARG_CASE("-errordeviationratio", 1) {
double edr;
if (!(parseDouble(edr, argv[argPos+1]) && edr > 0))
ABORT("Invalid error deviation ratio. Use -errordeviationratio <ratio> with a positive real number.");
config.generatorAttributes.config.errorCorrection.minDeviationRatio = edr;
argPos += 2;
continue;
}
ARG_CASE("-errorimproveratio", 1) {
double eir;
if (!(parseDouble(eir, argv[argPos+1]) && eir > 0))
ABORT("Invalid error improvement ratio. Use -errorimproveratio <ratio> with a positive real number.");
config.generatorAttributes.config.errorCorrection.minImproveRatio = eir;
argPos += 2;
continue;
}
ARG_CASE("-coloringstrategy", 1) {
if (!strcmp(argv[argPos+1], "simple")) config.edgeColoring = msdfgen::edgeColoringSimple, config.expensiveColoring = false;
else if (!strcmp(argv[argPos+1], "inktrap")) config.edgeColoring = msdfgen::edgeColoringInkTrap, config.expensiveColoring = false;
else if (!strcmp(argv[argPos+1], "distance")) config.edgeColoring = msdfgen::edgeColoringByDistance, config.expensiveColoring = true;
else
puts("Unknown coloring strategy specified.");
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("-nokerning", 0) {
config.kerning = false;
++argPos;
continue;
}
ARG_CASE("-kerning", 0) {
config.kerning = true;
++argPos;
continue;
}
ARG_CASE("-nopreprocess", 0) {
config.preprocessGeometry = false;
++argPos;
continue;
}
ARG_CASE("-preprocess", 0) {
config.preprocessGeometry = true;
++argPos;
continue;
}
ARG_CASE("-nooverlap", 0) {
config.generatorAttributes.config.overlapSupport = false;
++argPos;
continue;
}
ARG_CASE("-overlap", 0) {
config.generatorAttributes.config.overlapSupport = true;
++argPos;
continue;
}
ARG_CASE("-noscanline", 0) {
config.generatorAttributes.scanlinePass = false;
++argPos;
continue;
}
ARG_CASE("-scanline", 0) {
config.generatorAttributes.scanlinePass = true;
++argPos;
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("-threads", 1) {
unsigned tc;
if (!parseUnsigned(tc, argv[argPos+1]) || (int) tc < 0)
ABORT("Invalid thread count. Use -threads <N> with N being a non-negative integer.");
config.threadCount = (int) tc;
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 (!fontInput.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);
// Finalize font inputs
const FontInput *nextFontInput = &fontInput;
for (std::vector<FontInput>::reverse_iterator it = fontInputs.rbegin(); it != fontInputs.rend(); ++it) {
if (!it->fontFilename && nextFontInput->fontFilename)
it->fontFilename = nextFontInput->fontFilename;
if (!it->charsetFilename && nextFontInput->charsetFilename) {
it->charsetFilename = nextFontInput->charsetFilename;
it->glyphIdentifierType = nextFontInput->glyphIdentifierType;
}
if (it->fontScale < 0 && nextFontInput->fontScale >= 0)
it->fontScale = nextFontInput->fontScale;
nextFontInput = &*it;
}
if (fontInputs.empty() || memcmp(&fontInputs.back(), &fontInput, sizeof(FontInput)))
fontInputs.push_back(fontInput);
// 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 = MSDF_ATLAS_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;
}
if (config.kerning && !(config.arteryFontFilename || config.jsonFilename || config.shadronPreviewFilename))
config.kerning = false;
if (config.threadCount <= 0)
config.threadCount = std::max((int) std::thread::hardware_concurrency(), 1);
if (config.generatorAttributes.scanlinePass) {
if (explicitErrorCorrectionMode && config.generatorAttributes.config.errorCorrection.distanceCheckMode != msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE) {
const char *fallbackModeName = "unknown";
switch (config.generatorAttributes.config.errorCorrection.mode) {
case msdfgen::ErrorCorrectionConfig::DISABLED: fallbackModeName = "disabled"; break;
case msdfgen::ErrorCorrectionConfig::INDISCRIMINATE: fallbackModeName = "distance-fast"; break;
case msdfgen::ErrorCorrectionConfig::EDGE_PRIORITY: fallbackModeName = "auto-fast"; break;
case msdfgen::ErrorCorrectionConfig::EDGE_ONLY: fallbackModeName = "edge-fast"; break;
}
printf("Selected error correction mode not compatible with scanline mode, falling back to %s.\n", fallbackModeName);
}
config.generatorAttributes.config.errorCorrection.distanceCheckMode = msdfgen::ErrorCorrectionConfig::DO_NOT_CHECK_DISTANCE;
}
// 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
bool floatingPointFormat = (
config.imageFormat == ImageFormat::TIFF ||
config.imageFormat == ImageFormat::TEXT_FLOAT ||
config.imageFormat == ImageFormat::BINARY_FLOAT ||
config.imageFormat == ImageFormat::BINARY_FLOAT_BE
);
// Load fonts
std::vector<GlyphGeometry> glyphs;
std::vector<FontGeometry> fonts;
bool anyCodepointsAvailable = false;
{
class FontHolder {
msdfgen::FreetypeHandle *ft;
msdfgen::FontHandle *font;
const char *fontFilename;
public:
FontHolder() : ft(msdfgen::initializeFreetype()), font(nullptr), fontFilename(nullptr) { }
~FontHolder() {
if (ft) {
if (font)
msdfgen::destroyFont(font);
msdfgen::deinitializeFreetype(ft);
}
}
bool load(const char *fontFilename) {
if (ft && fontFilename) {
if (this->fontFilename && !strcmp(this->fontFilename, fontFilename))
return true;
if (font)
msdfgen::destroyFont(font);
if ((font = msdfgen::loadFont(ft, fontFilename))) {
this->fontFilename = fontFilename;
return true;
}
this->fontFilename = nullptr;
}
return false;
}
operator msdfgen::FontHandle *() const {
return font;
}
} font;
for (FontInput &fontInput : fontInputs) {
if (!font.load(fontInput.fontFilename))
ABORT("Failed to load specified font file.");
if (fontInput.fontScale <= 0)
fontInput.fontScale = 1;
// Load character set
Charset charset;
if (fontInput.charsetFilename) {
if (!charset.load(fontInput.charsetFilename, fontInput.glyphIdentifierType != GlyphIdentifierType::UNICODE_CODEPOINT))
ABORT(fontInput.glyphIdentifierType == GlyphIdentifierType::GLYPH_INDEX ? "Failed to load glyph set specification." : "Failed to load character set specification.");
} else {
charset = Charset::ASCII;
fontInput.glyphIdentifierType = GlyphIdentifierType::UNICODE_CODEPOINT;
}
// Load glyphs
FontGeometry fontGeometry(&glyphs);
int glyphsLoaded = -1;
switch (fontInput.glyphIdentifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
glyphsLoaded = fontGeometry.loadGlyphset(font, fontInput.fontScale, charset, config.preprocessGeometry, config.kerning);
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
glyphsLoaded = fontGeometry.loadCharset(font, fontInput.fontScale, charset, config.preprocessGeometry, config.kerning);
anyCodepointsAvailable |= glyphsLoaded > 0;
break;
}
if (glyphsLoaded < 0)
ABORT("Failed to load glyphs from font.");
printf("Loaded geometry of %d out of %d glyphs", glyphsLoaded, (int) charset.size());
if (fontInputs.size() > 1)
printf(" from font \"%s\"", fontInput.fontFilename);
printf(".\n");
// List missing glyphs
if (glyphsLoaded < (int) charset.size()) {
printf("Missing %d %s", (int) charset.size()-glyphsLoaded, fontInput.glyphIdentifierType == GlyphIdentifierType::UNICODE_CODEPOINT ? "codepoints" : "glyphs");
bool first = true;
switch (fontInput.glyphIdentifierType) {
case GlyphIdentifierType::GLYPH_INDEX:
for (unicode_t cp : charset)
if (!fontGeometry.getGlyph(msdfgen::GlyphIndex(cp)))
printf("%c 0x%02X", first ? ((first = false), ':') : ',', cp);
break;
case GlyphIdentifierType::UNICODE_CODEPOINT:
for (unicode_t cp : charset)
if (!fontGeometry.getGlyph(cp))
printf("%c 0x%02X", first ? ((first = false), ':') : ',', cp);
break;
}
printf("\n");
}
if (fontInput.fontName)
fontGeometry.setName(fontInput.fontName);
fonts.push_back((FontGeometry &&) fontGeometry);
}
}
if (glyphs.empty())
ABORT("No glyphs loaded.");
// Determine final atlas dimensions, scale and range, pack glyphs
{
double unitRange = 0, pxRange = 0;
switch (rangeMode) {
case RANGE_EM:
unitRange = rangeValue;
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);
else
atlasPacker.setMinimumScale(minEmSize);
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();
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) {
if (config.expensiveColoring) {
Workload([&glyphs, &config](int i, int threadNo) -> bool {
unsigned long long glyphSeed = (LCG_MULTIPLIER*(config.coloringSeed^i)+LCG_INCREMENT)*!!config.coloringSeed;
glyphs[i].edgeColoring(config.edgeColoring, config.angleThreshold, glyphSeed);
return true;
}, glyphs.size()).finish(config.threadCount);
} else {
unsigned long long glyphSeed = config.coloringSeed;
for (GlyphGeometry &glyph : glyphs) {
glyphSeed *= LCG_MULTIPLIER;
glyph.edgeColoring(config.edgeColoring, config.angleThreshold, glyphSeed);
}
}
}
bool success = false;
switch (config.imageType) {
case ImageType::HARD_MASK:
if (floatingPointFormat)
success = makeAtlas<float, float, 1, scanlineGenerator>(glyphs, fonts, config);
else
success = makeAtlas<byte, float, 1, scanlineGenerator>(glyphs, fonts, config);
break;
case ImageType::SOFT_MASK:
case ImageType::SDF:
if (floatingPointFormat)
success = makeAtlas<float, float, 1, sdfGenerator>(glyphs, fonts, config);
else
success = makeAtlas<byte, float, 1, sdfGenerator>(glyphs, fonts, config);
break;
case ImageType::PSDF:
if (floatingPointFormat)
success = makeAtlas<float, float, 1, psdfGenerator>(glyphs, fonts, config);
else
success = makeAtlas<byte, float, 1, psdfGenerator>(glyphs, fonts, config);
break;
case ImageType::MSDF:
if (floatingPointFormat)
success = makeAtlas<float, float, 3, msdfGenerator>(glyphs, fonts, config);
else
success = makeAtlas<byte, float, 3, msdfGenerator>(glyphs, fonts, config);
break;
case ImageType::MTSDF:
if (floatingPointFormat)
success = makeAtlas<float, float, 4, mtsdfGenerator>(glyphs, fonts, config);
else
success = makeAtlas<byte, float, 4, mtsdfGenerator>(glyphs, fonts, config);
break;
}
if (!success)
result = 1;
}
if (config.csvFilename) {
if (exportCSV(fonts.data(), fonts.size(), config.width, config.height, config.yDirection, config.csvFilename))
puts("Glyph layout written into CSV file.");
else {
result = 1;
puts("Failed to write CSV output file.");
}
}
if (config.jsonFilename) {
if (exportJSON(fonts.data(), fonts.size(), config.emSize, config.pxRange, config.width, config.height, config.imageType, config.yDirection, config.jsonFilename, config.kerning))
puts("Glyph layout and metadata written into JSON file.");
else {
result = 1;
puts("Failed to write JSON output file.");
}
}
if (config.shadronPreviewFilename && config.shadronPreviewText) {
if (anyCodepointsAvailable) {
std::vector<unicode_t> previewText;
utf8Decode(previewText, config.shadronPreviewText);
previewText.push_back(0);
if (generateShadronPreview(fonts.data(), fonts.size(), config.imageType, config.width, config.height, config.pxRange, previewText.data(), config.imageFilename, floatingPointFormat, config.shadronPreviewFilename))
puts("Shadron preview script generated.");
else {
result = 1;
puts("Failed to generate Shadron preview file.");
}
} else {
result = 1;
puts("Shadron preview not supported in -glyphset mode.");
}
}
return result;
}
#endif

View File

@@ -0,0 +1,42 @@
#pragma once
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD ATLAS GENERATOR v1.2 (2021-05-29)
* ---------------------------------------------------------------------
* A utility by Viktor Chlumsky, (c) 2020 - 2021
*
* 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 "FontGeometry.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.2"

Binary file not shown.

View File

@@ -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

View File

@@ -0,0 +1,374 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug Library|Win32">
<Configuration>Debug Library</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug Library|x64">
<Configuration>Debug Library</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Library|Win32">
<Configuration>Release Library</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Library|x64">
<Configuration>Release Library</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{223EDB94-5B35-45F2-A584-273DE6E45F6F}</ProjectGuid>
<RootNamespace>msdfatlasgen</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>bin\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>bin\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
<TargetName>msdf-atlas-gen</TargetName>
<OutDir>$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\msdfgen\freetype\win32;$(SolutionDir)$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\debug;msdfgen\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\msdfgen\freetype\win64;$(SolutionDir)$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\debug;msdfgen\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DebugInformationFormat>None</DebugInformationFormat>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\msdfgen\freetype\win32;$(SolutionDir)bin;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib>
<TargetMachine>MachineX86</TargetMachine>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\release;msdfgen\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDF_ATLAS_STANDALONE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DebugInformationFormat>None</DebugInformationFormat>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;skia.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\$(Configuration);msdfgen\$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>false</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>msdfgen\include;msdfgen\freetype\include;msdfgen;artery-font-format;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>freetype.lib;msdfgen.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\msdfgen\freetype\win64;$(SolutionDir)$(Platform)\$(Configuration) Library;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib>
<AdditionalLibraryDirectories>msdfgen\freetype\win$(PlatformArchitecture);msdfgen\skia\win$(PlatformArchitecture)\release;msdfgen\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="msdf-atlas-gen\artery-font-export.cpp" />
<ClCompile Include="msdf-atlas-gen\bitmap-blit.cpp" />
<ClCompile Include="msdf-atlas-gen\charset-parser.cpp" />
<ClCompile Include="msdf-atlas-gen\Charset.cpp" />
<ClCompile Include="msdf-atlas-gen\csv-export.cpp" />
<ClCompile Include="msdf-atlas-gen\FontGeometry.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\FontGeometry.h" />
<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>

Some files were not shown because too many files have changed in this diff Show More