diff --git a/Editor/resources/Interface/Testing.css b/Editor/resources/Interface/Testing.css
index bca5b904..ef92915e 100644
--- a/Editor/resources/Interface/Testing.css
+++ b/Editor/resources/Interface/Testing.css
@@ -1,11 +1,27 @@
.button {
- width: 25%;
+ width: 100%;
height: 100%;
}
-.left {
- width: 100px;
- height: 150px;
- margin: 50px 100px;
+.screen{
+ height: 100%;
+ width: 100%;
+ justify-content: center;
+ flex-direction: row;
+ align-content:flex-end;
}
+.container {
+ height: 50px;
+ width: 50%;
+ flex-direction: row;
+ justify-content: space-between;
+ align-content: center;
+ align-self:center;
+ padding: 8px;
+}
+
+.items{
+ height: 30px;
+ width: 30px;
+}
diff --git a/Editor/resources/Interface/Testing.interface b/Editor/resources/Interface/Testing.interface
index 14be270f..79b86565 100644
--- a/Editor/resources/Interface/Testing.interface
+++ b/Editor/resources/Interface/Testing.interface
@@ -1,31 +1,22 @@
diff --git a/Editor/resources/Shaders/basic.shader b/Editor/resources/Shaders/basic.shader
index 159de2cd..8260f726 100644
--- a/Editor/resources/Shaders/basic.shader
+++ b/Editor/resources/Shaders/basic.shader
@@ -330,6 +330,7 @@ void main()
finalNormal = finalNormal * 2.0 - 1.0;
finalNormal = v_TBN * normalize(finalNormal);
+
vec3 N = normalize(finalNormal);
vec3 V = normalize(u_EyePosition - v_FragPos);
vec3 R = reflect(-V, N);
@@ -343,6 +344,7 @@ void main()
vec3 Fog = vec3(0.0);
float shadow = 0.0f;
vec3 Lo = vec3(0.0);
+
for (int i = 0; i < LightCount; i++)
{
vec3 L = normalize(Lights[i].Position - v_FragPos);
@@ -404,6 +406,7 @@ void main()
// HDR tonemapping
color = color / (color + vec3(1.0));
+
const float gamma = 2.2;
color = vec3(1.0) - exp(-color * u_Exposure);
diff --git a/Editor/resources/Shaders/ui.shader b/Editor/resources/Shaders/ui.shader
new file mode 100644
index 00000000..8de694a1
--- /dev/null
+++ b/Editor/resources/Shaders/ui.shader
@@ -0,0 +1,63 @@
+#shader vertex
+#version 460 core
+layout(location = 0) in vec3 Position;
+layout(location = 1) in vec2 UV;
+uniform mat4 model;
+uniform mat4 projection;
+
+
+out vec2 a_UV;
+
+void main()
+{
+ a_UV = UV;
+ gl_Position = projection * model * vec4(Position, 1.0);
+}
+
+#shader fragment
+#version 460 core
+
+uniform vec4 background_color;
+uniform float u_border_radius;
+uniform vec2 u_size;
+
+out vec4 FragColor;
+
+in vec2 a_UV;
+
+bool ShouldDiscard(vec2 coords, vec2 dimensions, float radius)
+{
+ vec2 circle_center = vec2(radius, radius);
+
+ if (length(coords - circle_center) > radius
+ && coords.x < circle_center.x && coords.y < circle_center.y) return true; //first circle
+
+ circle_center.x += dimensions.x - 2 * radius;
+
+ if (length(coords - circle_center) > radius
+ && coords.x > circle_center.x && coords.y < circle_center.y) return true; //second circle
+
+ circle_center.y += dimensions.y - 2 * radius;
+
+ if (length(coords - circle_center) > radius
+ && coords.x > circle_center.x && coords.y > circle_center.y) return true; //third circle
+
+ circle_center.x -= dimensions.x - 2 * radius;
+
+ if (length(coords - circle_center) > radius
+ && coords.x < circle_center.x && coords.y > circle_center.y) return true; //fourth circle
+
+ return false;
+
+}
+
+void main()
+{
+ vec2 coords = a_UV * u_size;
+ vec2 center = u_size / 2.0;
+
+ // Border rounding
+ if (ShouldDiscard(coords, u_size, u_border_radius))
+ discard;
+ FragColor = background_color;
+}
diff --git a/Nuake/src/UI/Node.cpp b/Nuake/src/UI/Node.cpp
index cbc2fff6..202db3c2 100644
--- a/Nuake/src/UI/Node.cpp
+++ b/Nuake/src/UI/Node.cpp
@@ -12,7 +12,7 @@ void Node::ApplyStyle(Ref stylegroup)
float value = s.second.value.Number;
Height.Value = value;
}
- break;
+ break;
case PropType::MIN_HEIGHT:
MinHeight.Unit = (Layout::Unit)s.second.type;
MinHeight.Value = s.second.value.Number;
@@ -49,6 +49,43 @@ void Node::ApplyStyle(Ref stylegroup)
Margin.Bottom.Unit = (Layout::Unit)s.second.type;
Margin.Bottom.Value = s.second.value.Number;
break;
+ case PropType::JUSTIFY_CONTENT:
+ JustifyContent = (Layout::JustifyContent)s.second.value.Enum;
+ break;
+ case PropType::FLEX_WRAP:
+ FlexWrap = (Layout::FlexWrap)s.second.value.Enum;
+ break;
+ case PropType::FLEX_DIRECTION:
+ FlexDirection = (Layout::FlexDirection)s.second.value.Enum;
+ break;
+ case PropType::ALIGN_ITEMS:
+ AlignItems = (Layout::AlignItems)s.second.value.Enum;
+ break;
+ case PropType::SELF_ALIGN:
+ AlignSelf = (Layout::AlignItems)s.second.value.Enum;
+ break;
+ case PropType::ALIGN_CONTENT:
+ AlignContent = (Layout::AlignContent)s.second.value.Enum;
+ break;
+ case PropType::POSITION:
+ PositionType = (Layout::PositionType)s.second.value.Enum;
+ break;
+ case PropType::LEFT:
+ Position.Left.Unit = (Layout::Unit)s.second.type;
+ Position.Left.Value = s.second.value.Number;
+ break;
+ case PropType::RIGHT:
+ Position.Right.Unit = (Layout::Unit)s.second.type;
+ Position.Right.Value = s.second.value.Number;
+ break;
+ case PropType::TOP:
+ Position.Top.Unit = (Layout::Unit)s.second.type;
+ Position.Top.Value = s.second.value.Number;
+ break;
+ case PropType::BOTTOM:
+ Position.Bottom.Unit = (Layout::Unit)s.second.type;
+ Position.Bottom.Value = s.second.value.Number;
+ break;
}
}
}
@@ -57,7 +94,6 @@ Node::Node()
{
Childrens = std::vector[>();
Groups = std::vector();
-
PositionType = Layout::PositionType::RELATIVE;
Position = Layout::LayoutVec4();
Height = MaxHeight = MinHeight = Layout::LayoutUnit();
@@ -69,6 +105,6 @@ Node::Node()
FlexShrink = 1.f;
FlexBasis = 10.f;
AspectRatio = 1.f;
- AlignItems = Layout::AlignItems::FLEX_START;
- SelfAlign = Layout::AlignItems::FLEX_START;
+ AlignItems = Layout::AlignItems::AUTO;
+ AlignSelf = Layout::AlignItems::AUTO;
}
\ No newline at end of file
diff --git a/Nuake/src/UI/Node.h b/Nuake/src/UI/Node.h
index 5d66f5b4..04de96db 100644
--- a/Nuake/src/UI/Node.h
+++ b/Nuake/src/UI/Node.h
@@ -30,7 +30,7 @@ namespace Layout
enum class AlignItems
{
- STRETCH, FLEX_START, FLEX_END, CENTER, BASELINE
+ STRETCH, FLEX_START, FLEX_END, CENTER, BASELINE, AUTO,
};
enum class AlignContent
@@ -97,9 +97,11 @@ public:
float FlexShrink;
float FlexBasis;
float AspectRatio;
+ Layout::JustifyContent JustifyContent;
Layout::AlignItems AlignItems;
- Layout::AlignItems SelfAlign;
+ Layout::AlignItems AlignSelf;
+ Layout::AlignContent AlignContent;
Node();
@@ -261,7 +263,6 @@ public:
YGNodeStyleSetWidthAuto(YogaNode);
break;
}
-
switch (Height.Unit)
{
case Layout::Unit::PIXEL:
@@ -279,9 +280,78 @@ public:
SetPadding();
SetBorder();
- YGNodeStyleSetFlexWrap(YogaNode, YGWrapWrap);
- YGNodeStyleSetFlexDirection(YogaNode, YGFlexDirectionRow);
- YGNodeStyleSetJustifyContent(YogaNode, YGJustifyFlexStart);
+ if(FlexWrap == Layout::FlexWrap::WRAP)
+ YGNodeStyleSetFlexWrap(YogaNode, YGWrapWrap);
+ else if (FlexWrap == Layout::FlexWrap::NO_WRAP)
+ YGNodeStyleSetFlexWrap(YogaNode, YGWrapNoWrap);
+ else if (FlexWrap == Layout::FlexWrap::WRAP_REVERSED)
+ YGNodeStyleSetFlexWrap(YogaNode, YGWrapWrapReverse);
+
+ if(FlexDirection == Layout::FlexDirection::ROW)
+ YGNodeStyleSetFlexDirection(YogaNode, YGFlexDirectionRow);
+ else if (FlexDirection == Layout::FlexDirection::ROW_REVERSED)
+ YGNodeStyleSetFlexDirection(YogaNode, YGFlexDirectionRowReverse);
+ else if (FlexDirection == Layout::FlexDirection::COLUMN)
+ YGNodeStyleSetFlexDirection(YogaNode, YGFlexDirectionColumn);
+ else if (FlexDirection == Layout::FlexDirection::COLUMN_REVERSED)
+ YGNodeStyleSetFlexDirection(YogaNode, YGFlexDirectionColumnReverse);
+
+ if(JustifyContent == Layout::JustifyContent::FLEX_START)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifyFlexStart);
+ else if (JustifyContent == Layout::JustifyContent::FLEX_END)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifyFlexEnd);
+ else if (JustifyContent == Layout::JustifyContent::CENTER)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifyCenter);
+ else if (JustifyContent == Layout::JustifyContent::SPACE_BETWEEN)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifySpaceBetween);
+ else if (JustifyContent == Layout::JustifyContent::SPACE_AROUND)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifySpaceAround);
+ else if (JustifyContent == Layout::JustifyContent::SPACE_EVENLY)
+ YGNodeStyleSetJustifyContent(YogaNode, YGJustifySpaceEvenly);
+
+ if (AlignItems == Layout::AlignItems::FLEX_START)
+ YGNodeStyleSetAlignItems(YogaNode, YGAlignFlexStart);
+ else if (AlignItems == Layout::AlignItems::FLEX_END)
+ YGNodeStyleSetAlignItems(YogaNode, YGAlignFlexEnd);
+ else if (AlignItems == Layout::AlignItems::CENTER)
+ YGNodeStyleSetAlignItems(YogaNode, YGAlignCenter);
+ else if (AlignItems == Layout::AlignItems::BASELINE)
+ YGNodeStyleSetAlignItems(YogaNode, YGAlignBaseline);
+ else if (AlignItems == Layout::AlignItems::STRETCH)
+ YGNodeStyleSetAlignItems(YogaNode, YGAlignStretch);
+ //else if (AlignItems == Layout::AlignItems::AUTO)
+ // YGNodeStyleSetAlignItems(YogaNode, YGAlignAuto);
+
+ if (AlignSelf == Layout::AlignItems::FLEX_START)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignFlexStart);
+ else if (AlignSelf == Layout::AlignItems::FLEX_END)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignFlexEnd);
+ else if (AlignSelf == Layout::AlignItems::CENTER)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignCenter);
+ else if (AlignSelf == Layout::AlignItems::BASELINE)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignBaseline);
+ else if (AlignSelf == Layout::AlignItems::STRETCH)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignStretch);
+ else if (AlignSelf == Layout::AlignItems::AUTO)
+ YGNodeStyleSetAlignSelf(YogaNode, YGAlignAuto);
+
+ if (AlignContent == Layout::AlignContent::FLEX_START)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignFlexStart);
+ else if (AlignContent == Layout::AlignContent::FLEX_END)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignFlexEnd);
+ else if (AlignContent == Layout::AlignContent::CENTER)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignCenter);
+ else if (AlignContent == Layout::AlignContent::STRETCH)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignStretch);
+ else if (AlignContent == Layout::AlignContent::SPACE_BETWEEN)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignSpaceBetween);
+ else if (AlignContent == Layout::AlignContent::SPACE_AROUND)
+ YGNodeStyleSetAlignContent(YogaNode, YGAlignSpaceAround);
+
+ if (PositionType == Layout::PositionType::RELATIVE)
+ YGNodeStyleSetPositionType(YogaNode, YGPositionTypeRelative);
+ else if (PositionType == Layout::PositionType::ABSOLUTE)
+ YGNodeStyleSetPositionType(YogaNode, YGPositionTypeAbsolute);
}
void Draw(float z, Vector2 offset)
@@ -329,7 +399,8 @@ public:
//Logger::Log("Left: " + std::to_string(left) + " Top:" + std::to_string(top));
Renderer2D::UIShader->SetUniformMat4f("model", transform);
-
+ Renderer2D::UIShader->SetUniform1f("u_border_radius", 8.f);
+ Renderer2D::UIShader->SetUniform2f("u_size", width, height);
Renderer2D::DrawRect();
}
};
\ No newline at end of file
diff --git a/Nuake/src/UI/Stylesheet.h b/Nuake/src/UI/Stylesheet.h
index b41fae01..2bd1c20c 100644
--- a/Nuake/src/UI/Stylesheet.h
+++ b/Nuake/src/UI/Stylesheet.h
@@ -80,8 +80,11 @@ namespace UI
PropType type;
if (name == "height") type = PropType::HEIGHT;
- if (name == "width")type = PropType::WIDTH;
-
+ if (name == "width") type = PropType::WIDTH;
+ if (name == "left") type = PropType::LEFT;
+ if (name == "right") type = PropType::RIGHT;
+ if (name == "top") type = PropType::TOP;
+ if (name == "bottom") type = PropType::BOTTOM;
if (name == "margin")
{
std::regex regex("[0-9]+[^ ]+");
@@ -114,7 +117,112 @@ namespace UI
}
idx++;
}
- return true;
+ continue;
+ }
+ if (name == "flex-direction")
+ {
+ type = PropType::FLEX_DIRECTION;
+ Value propValue{};
+
+ if (value == "row")
+ propValue.Enum = (int)Layout::FlexDirection::ROW;
+ if (value == "row-reversed")
+ propValue.Enum = (int)Layout::FlexDirection::ROW_REVERSED;
+ if (value == "column")
+ propValue.Enum = (int)Layout::FlexDirection::COLUMN;
+ if (value == "column-reversed")
+ propValue.Enum = (int)Layout::FlexDirection::COLUMN_REVERSED;
+
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
+ }
+ if (name == "justify-content")
+ {
+ type = PropType::JUSTIFY_CONTENT;
+ Value propValue{};
+ if (value == "flex-start")
+ propValue.Enum = (int)Layout::JustifyContent::FLEX_START;
+ if (value == "flex-end")
+ propValue.Enum = (int)Layout::JustifyContent::FLEX_END;
+ if (value == "center")
+ propValue.Enum = (int)Layout::JustifyContent::CENTER;
+ if (value == "space-between")
+ propValue.Enum = (int)Layout::JustifyContent::SPACE_BETWEEN;
+ if (value == "space-around")
+ propValue.Enum = (int)Layout::JustifyContent::SPACE_AROUND;
+ if (value == "space-evenly")
+ propValue.Enum = (int)Layout::JustifyContent::SPACE_EVENLY;
+
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
+ }
+ if (name == "flex-wrap")
+ {
+ type = PropType::FLEX_WRAP;
+ Value propValue{};
+ if (value == "no-wrap")
+ propValue.Enum = (int)Layout::FlexWrap::NO_WRAP;
+ if (value == "wrap")
+ propValue.Enum = (int)Layout::FlexWrap::WRAP;
+ if (value == "wrap-reversed")
+ propValue.Enum = (int)Layout::FlexWrap::WRAP_REVERSED;
+
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
+ }
+ if (name == "align-self" || name == "align-items")
+ {
+ if (name == "align-self")
+ type = PropType::SELF_ALIGN;
+ else
+ type = PropType::ALIGN_ITEMS;
+
+ Value propValue{};
+ if (value == "stretch")
+ propValue.Enum = (int)Layout::AlignItems::STRETCH;
+ if (value == "flex-start")
+ propValue.Enum = (int)Layout::AlignItems::FLEX_START;
+ if (value == "flex-end")
+ propValue.Enum = (int)Layout::AlignItems::FLEX_END;
+ if (value == "center")
+ propValue.Enum = (int)Layout::AlignItems::CENTER;
+ if (value == "baseline")
+ propValue.Enum = (int)Layout::AlignItems::BASELINE;
+
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
+ }
+ if (name == "align-content")
+ {
+ type = PropType::ALIGN_CONTENT;
+
+ Value propValue{};
+ if (value == "stretch")
+ propValue.Enum = (int)Layout::AlignContent::STRETCH;
+ if (value == "flex-start")
+ propValue.Enum = (int)Layout::AlignContent::FLEX_START;
+ if (value == "flex-end")
+ propValue.Enum = (int)Layout::AlignContent::FLEX_END;
+ if (value == "center")
+ propValue.Enum = (int)Layout::AlignContent::CENTER;
+ if (value == "space-between")
+ propValue.Enum = (int)Layout::AlignContent::SPACE_BETWEEN;
+ if (value == "space-around")
+ propValue.Enum = (int)Layout::AlignContent::SPACE_AROUND;
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
+ }
+ if (name == "position")
+ {
+ type = PropType::POSITION;
+ Value propValue{};
+ if (value == "relative")
+ propValue.Enum = (int)Layout::PositionType::RELATIVE;
+ if (value == "absolute")
+ propValue.Enum = (int)Layout::PositionType::ABSOLUTE;
+
+ styleGroup->SetProp(type, PropValue{ PropValueType::ENUM, propValue });
+ continue;
}
styleGroup->SetProp(type, StyleSheetParser::ParsePropType(value, type));
Logger::Log(declaration->property);
diff --git a/Nuake/src/Vendors/artery-font-format/LICENSE.txt b/Nuake/src/Vendors/artery-font-format/LICENSE.txt
new file mode 100644
index 00000000..83edc597
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/LICENSE.txt
@@ -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.
diff --git a/Nuake/src/Vendors/artery-font-format/README.md b/Nuake/src/Vendors/artery-font-format/README.md
new file mode 100644
index 00000000..1b75fb0c
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/README.md
@@ -0,0 +1,6 @@
+
+# Artery Atlas Font format library
+
+This is a header-only C++ library that facilitates encoding and decoding of the Artery Atlas Font format – a specialized binary file format for storing fonts as bitmap atlases used by the [Artery Engine](https://www.arteryengine.com/), intended for use in video games and other hardware accelerated applications.
+
+An Artery Atlas font file (*.arfont) wraps together the atlas bitmap(s), which can be compressed e.g. in PNG format, the layout of the atlas, as well as the font's and the individual glyphs' metrics and positioning data, including kerning pairs.
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/artery-font.h b/Nuake/src/Vendors/artery-font-format/artery-font/artery-font.h
new file mode 100644
index 00000000..5a9c620d
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/artery-font.h
@@ -0,0 +1,10 @@
+
+#pragma once
+
+#include "types.h"
+#include "enums.h"
+#include "structures.h"
+#include "serialization.h"
+
+// ARTERY ENGINE ATLAS FONT FORMAT LIBRARY v1.0
+// Author: Viktor Chlumsky (c) 2020
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/crc32.h b/Nuake/src/Vendors/artery-font-format/artery-font/crc32.h
new file mode 100644
index 00000000..a05341bf
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/crc32.h
@@ -0,0 +1,13 @@
+
+#pragma once
+
+#include "types.h"
+
+namespace artery_font {
+
+uint32 crc32Init();
+uint32 crc32Update(uint32 crc, byte x);
+
+}
+
+#include "crc32.hpp"
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/crc32.hpp b/Nuake/src/Vendors/artery-font-format/artery-font/crc32.hpp
new file mode 100644
index 00000000..17ab4b33
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/crc32.hpp
@@ -0,0 +1,48 @@
+
+#include "crc32.h"
+
+namespace artery_font {
+
+inline uint32 crc32Init() {
+ return ~0u;
+}
+
+inline uint32 crc32Update(uint32 crc, byte x) {
+ static const uint32 crc32Table[256] = {
+ 0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u, 0x706af48fu, 0xe963a535u, 0x9e6495a3u,
+ 0x0edb8832u, 0x79dcb8a4u, 0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u, 0x90bf1d91u,
+ 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu, 0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u,
+ 0x136c9856u, 0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u, 0xfa0f3d63u, 0x8d080df5u,
+ 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u, 0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,
+ 0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u, 0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u,
+ 0x26d930acu, 0x51de003au, 0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u, 0xb8bda50fu,
+ 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u, 0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du,
+ 0x76dc4190u, 0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu, 0x9fbfe4a5u, 0xe8b8d433u,
+ 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu, 0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,
+ 0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu, 0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u,
+ 0x65b0d9c6u, 0x12b7e950u, 0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u, 0xfbd44c65u,
+ 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u, 0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu,
+ 0x4369e96au, 0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u, 0xaa0a4c5fu, 0xdd0d7cc9u,
+ 0x5005713cu, 0x270241aau, 0xbe0b1010u, 0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,
+ 0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u, 0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu,
+ 0xedb88320u, 0x9abfb3b6u, 0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u, 0x73dc1683u,
+ 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u, 0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u,
+ 0xf00f9344u, 0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu, 0x196c3671u, 0x6e6b06e7u,
+ 0xfed41b76u, 0x89d32be0u, 0x10da7a5au, 0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,
+ 0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u, 0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu,
+ 0xd80d2bdau, 0xaf0a1b4cu, 0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu, 0x4669be79u,
+ 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u, 0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu,
+ 0xc5ba3bbeu, 0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u, 0x2cd99e8bu, 0x5bdeae1du,
+ 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu, 0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,
+ 0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu, 0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u,
+ 0x86d3d2d4u, 0xf1d4e242u, 0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u, 0x18b74777u,
+ 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu, 0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u,
+ 0xa00ae278u, 0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u, 0x4969474du, 0x3e6e77dbu,
+ 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u, 0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,
+ 0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u, 0xcdd70693u, 0x54de5729u, 0x23d967bfu,
+ 0xb3667a2eu, 0xc4614ab8u, 0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu, 0x2d02ef8du,
+ };
+ return crc32Table[byte(x^crc)]^crc>>8;
+}
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/enums.h b/Nuake/src/Vendors/artery-font-format/artery-font/enums.h
new file mode 100644
index 00000000..979bf5f2
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/enums.h
@@ -0,0 +1,66 @@
+
+#pragma once
+
+namespace artery_font {
+
+enum FontFlags {
+ FONT_BOLD = 0x01,
+ FONT_LIGHT = 0x02,
+ FONT_EXTRA_BOLD = 0x04,
+ FONT_CONDENSED = 0x08,
+ FONT_ITALIC = 0x10,
+ FONT_SMALL_CAPS = 0x20,
+ FONT_ICONOGRAPHIC = 0x0100,
+ FONT_SANS_SERIF = 0x0200,
+ FONT_SERIF = 0x0400,
+ FONT_MONOSPACE = 0x1000,
+ FONT_CURSIVE = 0x2000
+};
+
+enum CodepointType {
+ CP_UNSPECIFIED = 0,
+ CP_UNICODE = 1,
+ CP_INDEXED = 2,
+ CP_ICONOGRAPHIC = 14
+};
+
+enum MetadataFormat {
+ METADATA_NONE = 0,
+ METADATA_PLAINTEXT = 1,
+ METADATA_JSON = 2
+};
+
+enum ImageType {
+ IMAGE_NONE = 0,
+ IMAGE_SRGB_IMAGE = 1,
+ IMAGE_LINEAR_MASK = 2,
+ IMAGE_MASKED_SRGB_IMAGE = 3,
+ IMAGE_SDF = 4,
+ IMAGE_PSDF = 5,
+ IMAGE_MSDF = 6,
+ IMAGE_MTSDF = 7,
+ IMAGE_MIXED_CONTENT = 255
+};
+
+enum PixelFormat {
+ PIXEL_UNKNOWN = 0,
+ PIXEL_BOOLEAN1 = 1,
+ PIXEL_UNSIGNED8 = 8,
+ PIXEL_FLOAT32 = 32
+};
+
+enum ImageEncoding {
+ IMAGE_UNKNOWN_ENCODING = 0,
+ IMAGE_RAW_BINARY = 1,
+ IMAGE_BMP = 4,
+ IMAGE_TIFF = 5,
+ IMAGE_PNG = 8,
+ IMAGE_TGA = 9
+};
+
+enum ImageOrientation {
+ ORIENTATION_TOP_DOWN = 1,
+ ORIENTATION_BOTTOM_UP = -1
+};
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/serialization.h b/Nuake/src/Vendors/artery-font-format/artery-font/serialization.h
new file mode 100644
index 00000000..da949f82
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/serialization.h
@@ -0,0 +1,18 @@
+
+#pragma once
+
+#include "types.h"
+#include "enums.h"
+#include "structures.h"
+
+namespace artery_font {
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool decode(ArteryFont &font, void *userData);
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool encode(const ArteryFont &font, void *userData);
+
+}
+
+#include "serialization.hpp"
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/serialization.hpp b/Nuake/src/Vendors/artery-font-format/artery-font/serialization.hpp
new file mode 100644
index 00000000..9b8582fe
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/serialization.hpp
@@ -0,0 +1,394 @@
+
+#include "serialization.h"
+
+#include
+#include "crc32.h"
+
+namespace artery_font {
+
+namespace internal {
+
+#define ARTERY_FONT_HEADER_TAG "ARTERY/FONT\0\0\0\0\0"
+#define ARTERY_FONT_HEADER_VERSION 1u
+#define ARTERY_FONT_HEADER_MAGIC_NO 0x4d276a5cu
+#define ARTERY_FONT_FOOTER_MAGIC_NO 0x55ccb363u
+
+struct ArteryFontHeader {
+ char tag[16];
+ uint32 magicNo;
+ uint32 version;
+ uint32 flags;
+ uint32 realType;
+ uint32 reserved[4];
+
+ uint32 metadataFormat;
+ uint32 metadataLength;
+ uint32 variantCount;
+ uint32 variantsLength;
+ uint32 imageCount;
+ uint32 imagesLength;
+ uint32 appendixCount;
+ uint32 appendicesLength;
+ uint32 reserved2[8];
+};
+
+struct ArteryFontFooter {
+ uint32 salt;
+ uint32 magicNo;
+ uint32 reserved[4];
+ uint32 totalLength;
+ uint32 checksum;
+};
+
+template
+struct FontVariantHeader {
+ uint32 flags;
+ uint32 weight;
+ uint32 codepointType;
+ uint32 imageType;
+ uint32 fallbackVariant;
+ uint32 fallbackGlyph;
+ uint32 reserved[6];
+ REAL metrics[32];
+ uint32 nameLength;
+ uint32 metadataLength;
+ uint32 glyphCount;
+ uint32 kernPairCount;
+};
+
+struct ImageHeader {
+ uint32 flags;
+ uint32 encoding;
+ uint32 width, height;
+ uint32 channels;
+ uint32 pixelFormat;
+ uint32 imageType;
+ uint32 rowLength;
+ sint32 orientation;
+ uint32 childImages;
+ uint32 textureFlags;
+ uint32 reserved[3];
+ uint32 metadataLength;
+ uint32 dataLength;
+};
+
+struct AppendixHeader {
+ uint32 metadataLength;
+ uint32 dataLength;
+};
+
+template
+uint32 realTypeCode();
+
+template <>
+uint32 realTypeCode() {
+ return 0x14u;
+}
+template <>
+uint32 realTypeCode() {
+ return 0x18u;
+}
+
+inline uint32 paddedLength(uint32 len) {
+ if (len&0x03u)
+ len += 0x04u-(len&0x03u);
+ return len;
+}
+
+template
+uint32 paddedStringLength(const STRING &str) {
+ uint32 len = str.length();
+ return paddedLength(len+(len > 0));
+}
+
+}
+
+#ifndef __BIG_ENDIAN__
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool decode(ArteryFont &font, void *userData) {
+ uint32 totalLength = 0;
+ uint32 prevLength = 0;
+ uint32 checksum = crc32Init();
+ byte dump[4];
+ #define ARTERY_FONT_DECODE_READ(target, len) { \
+ if (READ((void *) (target), (len), userData) != (len)) \
+ return false; \
+ totalLength += (len); \
+ for (int i = 0; i < int(len); ++i) \
+ checksum = crc32Update(checksum, ((const byte *) (const void *) (target))[i]); \
+ }
+ #define ARTERY_FONT_DECODE_REALIGN() { \
+ if (totalLength&0x03u) { \
+ uint32 len = 0x04u-(totalLength&0x03u); \
+ ARTERY_FONT_DECODE_READ(dump, len); \
+ } \
+ }
+ #define ARTERY_FONT_DECODE_READ_STRING(str, len) { \
+ if ((len) > 0) { \
+ LIST characters((len)+1); \
+ ARTERY_FONT_DECODE_READ((char *) characters, (len)+1); \
+ ((char *) characters)[len] = '\0'; \
+ (str) = STRING((const char *) characters, uint32(len)); \
+ ARTERY_FONT_DECODE_REALIGN(); \
+ } else \
+ (str) = STRING(); \
+ }
+ int variantCount = 0;
+ int imageCount = 0;
+ int appendixCount = 0;
+ uint32 variantsLength = 0;
+ uint32 imagesLength = 0;
+ uint32 appendicesLength = 0;
+ // Read header
+ {
+ internal::ArteryFontHeader header;
+ ARTERY_FONT_DECODE_READ(&header, sizeof(header));
+ if (memcmp(header.tag, ARTERY_FONT_HEADER_TAG, sizeof(header.tag)))
+ return false;
+ if (header.magicNo != ARTERY_FONT_HEADER_MAGIC_NO)
+ return false;
+ if (header.realType != internal::realTypeCode())
+ return false;
+ font.metadataFormat = (MetadataFormat) header.metadataFormat;
+ ARTERY_FONT_DECODE_READ_STRING(font.metadata, header.metadataLength);
+ variantCount = header.variantCount;
+ imageCount = header.imageCount;
+ appendixCount = header.appendixCount;
+ font.variants = LIST >(header.variantCount);
+ font.images = LIST >(header.imageCount);
+ font.appendices = LIST >(header.appendixCount);
+ variantsLength = header.variantsLength;
+ imagesLength = header.imagesLength;
+ appendicesLength = header.appendicesLength;
+ }
+ prevLength = totalLength;
+ // Read variants
+ for (int i = 0; i < variantCount; ++i) {
+ FontVariant &variant = font.variants[i];
+ internal::FontVariantHeader header;
+ ARTERY_FONT_DECODE_READ(&header, sizeof(header));
+ variant.flags = header.flags;
+ variant.weight = header.weight;
+ variant.codepointType = (CodepointType) header.codepointType;
+ variant.imageType = (ImageType) header.imageType;
+ variant.fallbackVariant = header.fallbackVariant;
+ variant.fallbackGlyph = header.fallbackGlyph;
+ memcpy(&variant.metrics, header.metrics, sizeof(header.metrics));
+ ARTERY_FONT_DECODE_READ_STRING(variant.name, header.nameLength);
+ ARTERY_FONT_DECODE_READ_STRING(variant.metadata, header.metadataLength);
+ variant.glyphs = LIST >(header.glyphCount);
+ variant.kernPairs = LIST >(header.kernPairCount);
+ ARTERY_FONT_DECODE_READ((Glyph *) variant.glyphs, header.glyphCount*sizeof(Glyph));
+ ARTERY_FONT_DECODE_READ((KernPair *) variant.kernPairs, header.kernPairCount*sizeof(KernPair));
+ }
+ if (totalLength-prevLength != variantsLength)
+ return false;
+ prevLength = totalLength;
+ // Read images
+ for (int i = 0; i < imageCount; ++i) {
+ Image &image = font.images[i];
+ internal::ImageHeader header;
+ ARTERY_FONT_DECODE_READ(&header, sizeof(header));
+ image.flags = header.flags;
+ image.encoding = (ImageEncoding) header.encoding;
+ image.width = header.width;
+ image.height = header.height;
+ image.channels = header.channels;
+ image.pixelFormat = (PixelFormat) header.pixelFormat;
+ image.imageType = (ImageType) header.imageType;
+ image.rawBinaryFormat.rowLength = header.rowLength;
+ image.rawBinaryFormat.orientation = (ImageOrientation) header.orientation;
+ image.childImages = header.childImages;
+ image.textureFlags = header.textureFlags;
+ ARTERY_FONT_DECODE_READ_STRING(image.metadata, header.metadataLength);
+ image.data = BYTE_ARRAY(header.dataLength);
+ ARTERY_FONT_DECODE_READ((unsigned char *) image.data, header.dataLength);
+ ARTERY_FONT_DECODE_REALIGN();
+ }
+ if (totalLength-prevLength != imagesLength)
+ return false;
+ prevLength = totalLength;
+ // Read appendices
+ for (int i = 0; i < appendixCount; ++i) {
+ Appendix &appendix = font.appendices[i];
+ internal::AppendixHeader header;
+ ARTERY_FONT_DECODE_READ(&header, sizeof(header));
+ ARTERY_FONT_DECODE_READ_STRING(appendix.metadata, header.metadataLength);
+ appendix.data = BYTE_ARRAY(header.dataLength);
+ ARTERY_FONT_DECODE_READ((unsigned char *) appendix.data, header.dataLength);
+ ARTERY_FONT_DECODE_REALIGN();
+ }
+ if (totalLength-prevLength != appendicesLength)
+ return false;
+ prevLength = totalLength;
+ // Read footer
+ {
+ internal::ArteryFontFooter footer;
+ ARTERY_FONT_DECODE_READ(&footer, sizeof(footer)-sizeof(footer.checksum));
+ if (footer.magicNo != ARTERY_FONT_FOOTER_MAGIC_NO)
+ return false;
+ uint32 prevChecksum = checksum;
+ ARTERY_FONT_DECODE_READ(&footer.checksum, sizeof(footer.checksum));
+ if (footer.checksum != prevChecksum)
+ return false;
+ if (totalLength != footer.totalLength)
+ return false;
+ }
+ return true;
+ #undef ARTERY_FONT_DECODE_READ
+ #undef ARTERY_FONT_DECODE_REALIGN
+ #undef ARTERY_FONT_DECODE_READ_STRING
+}
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool encode(const ArteryFont &font, void *userData) {
+ uint32 totalLength = 0;
+ uint32 checksum = crc32Init();
+ const byte padding[4] = { };
+ #define ARTERY_FONT_ENCODE_WRITE(data, len) { \
+ if (WRITE((const void *) (data), (len), userData) != (len)) \
+ return false; \
+ totalLength += (len); \
+ for (int i = 0; i < int(len); ++i) \
+ checksum = crc32Update(checksum, ((const byte *) (const void *) (data))[i]); \
+ }
+ #define ARTERY_FONT_ENCODE_REALIGN() { \
+ if (totalLength&0x03u) { \
+ uint32 len = 0x04u-(totalLength&0x03u); \
+ ARTERY_FONT_ENCODE_WRITE(padding, len); \
+ } \
+ }
+ #define ARTERY_FONT_ENCODE_WRITE_STRING(str) { \
+ uint32 len = (str).length(); \
+ if ((len) > 0) { \
+ ARTERY_FONT_ENCODE_WRITE((const char *) (str), (len)); \
+ ARTERY_FONT_ENCODE_WRITE(padding, 1) \
+ ARTERY_FONT_ENCODE_REALIGN(); \
+ } \
+ }
+ int variantCount = 0;
+ int imageCount = 0;
+ int appendixCount = 0;
+ // Write header
+ {
+ internal::ArteryFontHeader header;
+ memcpy(header.tag, ARTERY_FONT_HEADER_TAG, sizeof(header.tag));
+ header.magicNo = ARTERY_FONT_HEADER_MAGIC_NO;
+ header.version = ARTERY_FONT_HEADER_VERSION;
+ header.flags = 0;
+ header.realType = internal::realTypeCode();
+ memset(header.reserved, 0, sizeof(header.reserved));
+ header.metadataFormat = (uint32) font.metadataFormat;
+ header.metadataLength = font.metadata.length();
+ header.variantCount = variantCount = font.variants.length();
+ header.variantsLength = 0;
+ header.imageCount = imageCount = font.images.length();
+ header.imagesLength = 0;
+ header.appendixCount = appendixCount = font.appendices.length();
+ header.appendicesLength = 0;
+ memset(header.reserved2, 0, sizeof(header.reserved2));
+ for (int i = 0; i < variantCount; ++i) {
+ const FontVariant &variant = font.variants[i];
+ header.variantsLength += sizeof(internal::FontVariantHeader);
+ header.variantsLength += internal::paddedStringLength(variant.name);
+ header.variantsLength += internal::paddedStringLength(variant.metadata);
+ header.variantsLength += variant.glyphs.length()*sizeof(Glyph);
+ header.variantsLength += variant.kernPairs.length()*sizeof(KernPair);
+ }
+ for (int i = 0; i < imageCount; ++i) {
+ const Image &image = font.images[i];
+ header.imagesLength += sizeof(internal::ImageHeader);
+ header.imagesLength += internal::paddedStringLength(image.metadata);
+ header.imagesLength += internal::paddedLength(image.data.length());
+ }
+ for (int i = 0; i < appendixCount; ++i) {
+ const Appendix &appendix = font.appendices[i];
+ header.appendicesLength += sizeof(internal::AppendixHeader);
+ header.appendicesLength += internal::paddedStringLength(appendix.metadata);
+ header.appendicesLength += internal::paddedLength(appendix.data.length());
+ }
+ ARTERY_FONT_ENCODE_WRITE(&header, sizeof(header));
+ ARTERY_FONT_ENCODE_WRITE_STRING(font.metadata);
+ }
+ // Write variants
+ for (int i = 0; i < variantCount; ++i) {
+ const FontVariant &variant = font.variants[i];
+ internal::FontVariantHeader header;
+ header.flags = variant.flags;
+ header.weight = variant.weight;
+ header.codepointType = (uint32) variant.codepointType;
+ header.imageType = (uint32) variant.imageType;
+ header.fallbackVariant = variant.fallbackVariant;
+ header.fallbackGlyph = variant.fallbackGlyph;
+ memset(header.reserved, 0, sizeof(header.reserved));
+ memcpy(header.metrics, &variant.metrics, sizeof(header.metrics));
+ header.nameLength = variant.name.length();
+ header.metadataLength = variant.metadata.length();
+ header.glyphCount = variant.glyphs.length();
+ header.kernPairCount = variant.kernPairs.length();
+ ARTERY_FONT_ENCODE_WRITE(&header, sizeof(header));
+ ARTERY_FONT_ENCODE_WRITE_STRING(variant.name);
+ ARTERY_FONT_ENCODE_WRITE_STRING(variant.metadata);
+ ARTERY_FONT_ENCODE_WRITE((const Glyph *) variant.glyphs, header.glyphCount*sizeof(Glyph));
+ ARTERY_FONT_ENCODE_WRITE((const KernPair *) variant.kernPairs, header.kernPairCount*sizeof(KernPair));
+ }
+ // Write images
+ for (int i = 0; i < imageCount; ++i) {
+ const Image &image = font.images[i];
+ internal::ImageHeader header;
+ header.flags = image.flags;
+ header.encoding = (uint32) image.encoding;
+ header.width = image.width;
+ header.height = image.height;
+ header.channels = image.channels;
+ header.pixelFormat = (uint32) image.pixelFormat;
+ header.imageType = (uint32) image.imageType;
+ header.rowLength = image.rawBinaryFormat.rowLength;
+ header.orientation = (sint32) image.rawBinaryFormat.orientation;
+ header.childImages = image.childImages;
+ header.textureFlags = image.textureFlags;
+ memset(header.reserved, 0, sizeof(header.reserved));
+ header.metadataLength = image.metadata.length();
+ header.dataLength = image.data.length();
+ ARTERY_FONT_ENCODE_WRITE(&header, sizeof(header));
+ ARTERY_FONT_ENCODE_WRITE_STRING(image.metadata);
+ ARTERY_FONT_ENCODE_WRITE((const unsigned char *) image.data, header.dataLength);
+ ARTERY_FONT_ENCODE_REALIGN();
+ }
+ // Write appendices
+ for (int i = 0; i < appendixCount; ++i) {
+ const Appendix &appendix = font.appendices[i];
+ internal::AppendixHeader header;
+ header.metadataLength = appendix.metadata.length();
+ header.dataLength = appendix.data.length();
+ ARTERY_FONT_ENCODE_WRITE(&header, sizeof(header));
+ ARTERY_FONT_ENCODE_WRITE_STRING(appendix.metadata);
+ ARTERY_FONT_ENCODE_WRITE((const unsigned char *) appendix.data, header.dataLength);
+ ARTERY_FONT_ENCODE_REALIGN();
+ }
+ // Write footer
+ {
+ internal::ArteryFontFooter footer;
+ footer.salt = 0;
+ footer.magicNo = ARTERY_FONT_FOOTER_MAGIC_NO;
+ memset(footer.reserved, 0, sizeof(footer.reserved));
+ footer.totalLength = totalLength+sizeof(footer);
+ ARTERY_FONT_ENCODE_WRITE(&footer, sizeof(footer)-sizeof(footer.checksum));
+ footer.checksum = checksum;
+ ARTERY_FONT_ENCODE_WRITE(&footer.checksum, sizeof(footer.checksum));
+ }
+ return true;
+ #undef ARTERY_FONT_ENCODE_WRITE
+ #undef ARTERY_FONT_ENCODE_REALIGN
+ #undef ARTERY_FONT_ENCODE_WRITE_STRING
+}
+
+#endif
+
+#undef ARTERY_FONT_HEADER_TAG
+#undef ARTERY_FONT_HEADER_VERSION
+#undef ARTERY_FONT_HEADER_MAGIC_NO
+#undef ARTERY_FONT_FOOTER_MAGIC_NO
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/std-artery-font.h b/Nuake/src/Vendors/artery-font-format/artery-font/std-artery-font.h
new file mode 100644
index 00000000..2f01da86
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/std-artery-font.h
@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include
+#include
+#include "artery-font.h"
+
+namespace artery_font {
+
+template
+class StdList {
+
+public:
+ std::vector vector;
+
+ StdList() { }
+ explicit StdList(int length) : vector((size_t) length) { }
+ int length() const { return (int) vector.size(); }
+ explicit operator T *() { return vector.data(); }
+ explicit operator const T *() const { return vector.data(); }
+ T & operator[](int index) { return vector[index]; }
+ const T & operator[](int index) const { return vector[index]; }
+
+};
+
+class StdString {
+
+public:
+ std::string string;
+
+ StdString() { }
+ StdString(const char *characters, int length) : string(characters, (size_t) length) { }
+ int length() const { return (int) string.size(); }
+ explicit operator const char *() const { return string.c_str(); }
+
+};
+
+typedef StdList StdByteArray;
+
+template
+using StdArteryFont = ArteryFont;
+template
+using StdFontVariant = FontVariant;
+using StdImage = Image;
+using StdAppendix = Appendix;
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.h b/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.h
new file mode 100644
index 00000000..65d5245c
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.h
@@ -0,0 +1,23 @@
+
+#pragma once
+
+#include
+#include "serialization.h"
+
+namespace artery_font {
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool read(ArteryFont &font, FILE *file);
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool write(const ArteryFont &font, FILE *file);
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool readFile(ArteryFont &font, const char *filename);
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool writeFile(const ArteryFont &font, const char *filename);
+
+}
+
+#include "stdio-serialization.hpp"
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.hpp b/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.hpp
new file mode 100644
index 00000000..fff2561f
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/stdio-serialization.hpp
@@ -0,0 +1,48 @@
+
+#include "stdio-serialization.h"
+
+namespace artery_font {
+
+namespace internal {
+
+inline int fileRead(void *buffer, int length, void *file) {
+ return fread(buffer, 1, length, reinterpret_cast(file));
+}
+
+inline int fileWrite(const void *buffer, int length, void *file) {
+ return fwrite(buffer, 1, length, reinterpret_cast(file));
+}
+
+}
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool read(ArteryFont &font, FILE *file) {
+ return decode(font, file);
+}
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool write(const ArteryFont &font, FILE *file) {
+ return encode(font, file);
+}
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool readFile(ArteryFont &font, const char *filename) {
+ FILE *file = fopen(filename, "rb");
+ if (!file)
+ return false;
+ bool result = read(font, file);
+ fclose(file);
+ return result;
+}
+
+template class LIST, class BYTE_ARRAY, class STRING>
+bool writeFile(const ArteryFont &font, const char *filename) {
+ FILE *file = fopen(filename, "wb");
+ if (!file)
+ return false;
+ bool result = write(font, file);
+ fclose(file);
+ return result;
+}
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/structures.h b/Nuake/src/Vendors/artery-font-format/artery-font/structures.h
new file mode 100644
index 00000000..12cb806e
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/structures.h
@@ -0,0 +1,87 @@
+
+#pragma once
+
+#include "types.h"
+#include "enums.h"
+
+namespace artery_font {
+
+template
+struct Glyph {
+ uint32 codepoint;
+ uint32 image;
+ struct {
+ REAL l, b, r, t;
+ } planeBounds, imageBounds;
+ struct {
+ REAL h, v;
+ } advance;
+};
+
+template
+struct KernPair {
+ uint32 codepoint1, codepoint2;
+ struct {
+ REAL h, v;
+ } advance;
+};
+
+template class LIST, class STRING>
+struct FontVariant {
+ uint32 flags;
+ uint32 weight;
+ CodepointType codepointType;
+ ImageType imageType;
+ uint32 fallbackVariant;
+ uint32 fallbackGlyph;
+ struct Metrics {
+ // In pixels:
+ REAL fontSize;
+ REAL distanceRange;
+ // Proportional to font size:
+ REAL emSize;
+ REAL ascender, descender;
+ REAL lineHeight;
+ REAL underlineY, underlineThickness;
+ REAL reserved[24];
+ } metrics;
+ STRING name;
+ STRING metadata;
+ LIST > glyphs;
+ LIST > kernPairs;
+};
+
+template
+struct Image {
+ uint32 flags;
+ ImageEncoding encoding;
+ uint32 width, height;
+ uint32 channels;
+ PixelFormat pixelFormat;
+ ImageType imageType;
+ struct {
+ uint32 rowLength;
+ ImageOrientation orientation;
+ } rawBinaryFormat;
+ uint32 childImages;
+ uint32 textureFlags;
+ STRING metadata;
+ BYTE_ARRAY data;
+};
+
+template
+struct Appendix {
+ STRING metadata;
+ BYTE_ARRAY data;
+};
+
+template class LIST, class BYTE_ARRAY, class STRING>
+struct ArteryFont {
+ MetadataFormat metadataFormat;
+ STRING metadata;
+ LIST > variants;
+ LIST > images;
+ LIST > appendices;
+};
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/artery-font/types.h b/Nuake/src/Vendors/artery-font-format/artery-font/types.h
new file mode 100644
index 00000000..d81db4ec
--- /dev/null
+++ b/Nuake/src/Vendors/artery-font-format/artery-font/types.h
@@ -0,0 +1,12 @@
+
+#pragma once
+
+#include
+
+namespace artery_font {
+
+typedef unsigned char byte;
+typedef int32_t sint32;
+typedef uint32_t uint32;
+
+}
diff --git a/Nuake/src/Vendors/artery-font-format/example.arfont b/Nuake/src/Vendors/artery-font-format/example.arfont
new file mode 100644
index 00000000..a1530434
Binary files /dev/null and b/Nuake/src/Vendors/artery-font-format/example.arfont differ
diff --git a/Nuake/src/Vendors/msdfgen/.gitattributes b/Nuake/src/Vendors/msdfgen/.gitattributes
new file mode 100644
index 00000000..3fc77eb7
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/.gitattributes
@@ -0,0 +1,5 @@
+* text=auto
+include/** linguist-vendored
+lib/** linguist-vendored
+freetype/** linguist-vendored
+skia/** linguist-vendored
diff --git a/Nuake/src/Vendors/msdfgen/.gitignore b/Nuake/src/Vendors/msdfgen/.gitignore
new file mode 100644
index 00000000..033ff852
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/.gitignore
@@ -0,0 +1,28 @@
+/Debug/
+/Release/
+/Release OpenMP/
+/Debug Library/
+/Release Library/
+/Release Library OpenMP/
+/x86/
+/x64/
+.vs/
+*.exe
+*.zip
+*.user
+*.sdf
+*.pdb
+*.ipdb
+*.iobj
+*.suo
+*.VC.opendb
+*.VC.db
+/bin/*.lib
+/bin/msdfgen
+output.png
+render.png
+out/
+build/
+build_xcode/
+skia/win32/
+skia/win64/
diff --git a/Nuake/src/Vendors/msdfgen/CHANGELOG.md b/Nuake/src/Vendors/msdfgen/CHANGELOG.md
new file mode 100644
index 00000000..4b9a7526
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/CHANGELOG.md
@@ -0,0 +1,82 @@
+
+## Version 1.9 (2021-05-28)
+
+- Error correction of multi-channel distance fields has been completely reworked
+- Added new edge coloring strategy that optimizes colors based on distances between edges
+- Added some minor functions for the library API
+- Minor code refactor and optimizations
+
+## Version 1.8 (2020-10-17)
+
+- Integrated the Skia library into the project, which is used to preprocess the shape geometry and eliminate any self-intersections and other irregularities previously unsupported by the software
+ - 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`
+- Significantly improved performance of the core algorithm by reusing results from previously computed pixels
+- Introduced an additional error correction routine which eliminates MSDF artifacts by analytically predicting results of bilinear interpolation
+- Added the possibility to load font glyphs by their index rather than a Unicode value (use the prefix `g` before the character code in `-font` argument)
+- Added `-distanceshift` argument that can be used to adjust the center of the distance range in the output distance field
+- Fixed several errors in the evaluation of curve distances
+- Fixed an issue with paths containing convergent corners (those whose inner angle is zero)
+- The algorithm for pseudo-distance computation slightly changed, fixing certain rare edge cases and improving consistency
+- Added the ability to supply own `FT_Face` handle to the msdfgen library
+- Minor refactor of the core algorithm
+
+### Version 1.7.1 (2020-03-09)
+
+- Fixed an edge case bug in scanline rasterization
+
+## Version 1.7 (2020-03-07)
+
+- Added `mtsdf` mode - a combination of `msdf` with `sdf` in the alpha channel
+- Distance fields can now be stored as uncompressed TIFF image files with floating point precision
+- Bitmap class refactor - template argument split into data type and number of channels, bitmap reference classes introduced
+- Added a secondary "ink trap" edge coloring heuristic, can be selected using `-coloringstrategy inktrap`
+- Added computation of estimated rendering error for a given SDF
+- Added computation of bounding box that includes sharp mitered corners
+- The API for bounds computation of the `Shape` class changed for clarity
+- Fixed several edge case bugs
+
+## Version 1.6 (2019-04-08)
+
+- Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
+- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
+- MSDF error correction reworked to also fix distances away from edges and consider diagonal pairs. Code simplified.
+- Added scanline rasterization support for `Shape`.
+- Added a scanline pass in the standalone version, which corrects the signs in the distance field according to the selected fill rule (`-fillrule`). Can be disabled using `-noscanline`.
+- Fixed autoframe scaling, which previously caused the output to have unnecessary empty border.
+- `-guessorder` switch no longer enabled by default, as the functionality is now provided by the scanline pass.
+- Updated FreeType and other libraries, changed to static linkage
+- Added 64-bit and static library builds to the Visual Studio solution
+
+## Version 1.5 (2017-07-23)
+
+- Fixed rounding error in cubic curve splitting.
+- SVG parser fixes and support for additional path commands.
+- Added CMake build script.
+
+## Version 1.4 (2017-02-09)
+
+- Reworked contour combining logic to support overlapping contours. Original algorithm preserved in functions with `_legacy` suffix, which are invoked by the new `-legacy` switch.
+- Fixed a severe bug in cubic curve distance computation, where a control point lies at the endpoint.
+- Standalone version now automatically detects if the input has the wrong orientation and adjusts the distance field accordingly. Can be disabled by `-keeporder` or `-reverseorder` switch.
+- SVG parser fixes and improvements.
+
+## Version 1.3 (2016-12-07)
+
+- Fixed `-reverseorder` switch.
+- Fixed glyph loading to use the proper method of acquiring outlines from FreeType.
+
+## Version 1.2 (2016-07-20)
+
+- Added option to specify that shape vertices are listed in reverse order (`-reverseorder`).
+- Added option to set a seed for the edge coloring heuristic (-seed \), which can be used to adjust the output.
+- Fixed parsing of glyph contours that start with a curve control point.
+
+## Version 1.1 (2016-05-08)
+
+- Switched to MIT license due to popular demand.
+- Fixed SDF rendering anti-aliasing when the output is smaller than the distance field.
+
+## Version 1.0 (2016-04-28)
+
+- Project published.
diff --git a/Nuake/src/Vendors/msdfgen/CMakeLists.txt b/Nuake/src/Vendors/msdfgen/CMakeLists.txt
new file mode 100644
index 00000000..01b048d5
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/CMakeLists.txt
@@ -0,0 +1,194 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(msdfgen VERSION 1.8 LANGUAGES CXX)
+option(MSDFGEN_BUILD_MSDFGEN_STANDALONE "Build the msdfgen standalone executable" ON)
+option(MSDFGEN_USE_OPENMP "Build with OpenMP support for multithreaded code" OFF)
+option(MSDFGEN_USE_CPP11 "Build with C++11 enabled" ON)
+option(MSDFGEN_USE_SKIA "Build with the Skia library" OFF)
+option(MSDFGEN_INSTALL "Generate installation target" ON)
+option(FREETYPE_WITH_PNG "Link libpng and zlib because FreeType is configured to require it" OFF)
+option(FREETYPE_WITH_HARFBUZZ "Link HarfBuzz because FreeType is configured to require it" OFF)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+if(NOT TARGET Freetype::Freetype)
+ find_package(Freetype REQUIRED)
+endif()
+
+#----------------------------------------------------------------
+# Gathering File
+#----------------------------------------------------------------
+
+file(GLOB_RECURSE msdfgen_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "core/*.h"
+ "core/*.hpp"
+)
+
+file(GLOB_RECURSE msdfgen_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "core/*.cpp"
+)
+
+file(GLOB_RECURSE msdfgen-ext_PUBLIC_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "ext/*.h"
+ "ext/*.hpp"
+)
+
+file(GLOB_RECURSE msdfgen-ext_PRIVATE_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "include/*.h"
+)
+
+file(GLOB_RECURSE msdfgen-ext_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "ext/*.cpp"
+ "lib/*.cpp"
+ "lib/*.cpp"
+)
+
+# Build the library (aliased name because it's the same target name the exe)
+include(folderize)
+folderize_sources(msdfgen_HEADERS ${CMAKE_SOURCE_DIR})
+folderize_sources(msdfgen_SOURCES ${CMAKE_SOURCE_DIR})
+folderize_sources(msdfgen-ext_PUBLIC_HEADERS ${CMAKE_SOURCE_DIR})
+folderize_sources(msdfgen-ext_PRIVATE_HEADERS ${CMAKE_SOURCE_DIR})
+folderize_sources(msdfgen-ext_SOURCES ${CMAKE_SOURCE_DIR})
+
+#----------------------------------------------------------------
+# Target configuration
+#----------------------------------------------------------------
+
+add_library(msdfgen ${msdfgen_SOURCES} ${msdfgen_HEADERS} "./msdfgen.h")
+add_library(msdfgen::msdfgen ALIAS msdfgen)
+set_target_properties(msdfgen PROPERTIES PUBLIC_HEADER "${msdfgen_HEADERS}")
+target_include_directories(msdfgen INTERFACE
+ $
+ $
+)
+
+if(MSDFGEN_USE_CPP11)
+ target_compile_features(msdfgen PUBLIC cxx_std_11)
+ target_compile_definitions(msdfgen PUBLIC MSDFGEN_USE_CPP11)
+endif()
+
+if(MSDFGEN_USE_OPENMP)
+ # Note: Clang doesn't support OpenMP by default...
+ find_package(OpenMP REQUIRED COMPONENTS CXX)
+ target_link_libraries(msdfgen PRIVATE OpenMP::OpenMP_CXX)
+ target_compile_definitions(msdfgen PRIVATE MSDFGEN_USE_OPENMP)
+endif()
+
+if(MSDFGEN_USE_SKIA)
+ find_package(Skia REQUIRED)
+ target_link_libraries(msdfgen-ext PRIVATE Skia::Skia)
+ target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_USE_SKIA)
+endif()
+
+add_library(msdfgen-ext ${msdfgen-ext_SOURCES} ${msdfgen-ext_PUBLIC_HEADERS} ${msdfgen-ext_PRIVATE_HEADERS} "./msdfgen-ext.h")
+add_library(msdfgen::msdfgen-ext ALIAS msdfgen-ext)
+set_target_properties(msdfgen-ext PROPERTIES
+ PUBLIC_HEADER "${msdfgen-ext_PUBLIC_HEADERS}"
+)
+target_link_libraries(msdfgen-ext PUBLIC msdfgen::msdfgen Freetype::Freetype)
+target_include_directories(msdfgen-ext
+PUBLIC
+ $
+ $
+
+PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+)
+
+target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_CMAKE_BUILD)
+
+if(MSDFGEN_USE_CPP11)
+ target_compile_features(msdfgen-ext PUBLIC cxx_std_11)
+ target_compile_definitions(msdfgen-ext PUBLIC MSDFGEN_USE_CPP11)
+endif()
+
+# Build the executable if requested
+if(MSDFGEN_BUILD_MSDFGEN_STANDALONE)
+ add_executable(msdfgen-standalone main.cpp)
+ set_target_properties(msdfgen-standalone PROPERTIES ARCHIVE_OUTPUT_DIRECTORY archive OUTPUT_NAME msdfgen)
+ target_compile_definitions(msdfgen-standalone PRIVATE MSDFGEN_STANDALONE)
+ target_link_libraries(msdfgen-standalone PRIVATE msdfgen::msdfgen msdfgen::msdfgen-ext)
+
+ if(FREETYPE_WITH_PNG)
+ find_package(ZLIB REQUIRED)
+ find_package(PNG REQUIRED)
+ target_link_libraries(msdfgen-standalone PRIVATE ZLIB::ZLIB PNG::PNG)
+ endif()
+ if(FREETYPE_WITH_HARFBUZZ)
+ # No FindHarfBuzz.cmake available so falling back to CMAKE_LIBRARY_PATH
+ find_library(FREETYPE_HARFBUZZ_LIB "harfbuzz")
+ target_link_libraries(msdfgen-standalone PRIVATE "${FREETYPE_HARFBUZZ_LIB}")
+ endif()
+endif()
+
+#----------------------------------------------------------------
+# Installation and exportation of the libraries
+#----------------------------------------------------------------
+
+if(MSDFGEN_INSTALL)
+ include(CMakePackageConfigHelpers)
+ set(MSDFGEN_CONFIG_PATH "lib/cmake/msdfgen")
+
+ # install tree package config
+ write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/msdfgenConfigVersion.cmake"
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion
+ )
+
+ configure_package_config_file(
+ cmake/msdfgenConfig.cmake.in
+ ${MSDFGEN_CONFIG_PATH}/msdfgenConfig.cmake
+ INSTALL_DESTINATION ${MSDFGEN_CONFIG_PATH}
+ NO_CHECK_REQUIRED_COMPONENTS_MACRO
+ )
+
+ # build tree package config
+ configure_file(
+ cmake/msdfgenConfig.cmake.in
+ msdfgenConfig.cmake
+ @ONLY
+ )
+
+ install(TARGETS msdfgen EXPORT msdfgenTargets
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+ FRAMEWORK DESTINATION lib
+ PUBLIC_HEADER DESTINATION include/msdfgen/core
+ )
+
+ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen.h" "${CMAKE_CURRENT_SOURCE_DIR}/msdfgen-ext.h" DESTINATION include/msdfgen)
+
+ install(TARGETS msdfgen-ext EXPORT msdfgenTargets
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+ FRAMEWORK DESTINATION lib
+ PUBLIC_HEADER DESTINATION include/msdfgen/ext
+ )
+
+ if(MSDFGEN_BUILD_MSDFGEN_STANDALONE)
+ install(TARGETS msdfgen-standalone EXPORT msdfgenTargets RUNTIME DESTINATION bin)
+ endif()
+
+ install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${MSDFGEN_CONFIG_PATH}/msdfgenConfig.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/msdfgenConfigVersion.cmake"
+ DESTINATION ${MSDFGEN_CONFIG_PATH}
+ )
+
+ export(
+ EXPORT msdfgenTargets
+ NAMESPACE msdfgen::
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/msdfgenTargets.cmake"
+ )
+
+ install(
+ EXPORT msdfgenTargets FILE msdfgenTargets.cmake
+ NAMESPACE msdfgen::
+ DESTINATION ${MSDFGEN_CONFIG_PATH}
+ )
+endif()
diff --git a/Nuake/src/Vendors/msdfgen/LICENSE.txt b/Nuake/src/Vendors/msdfgen/LICENSE.txt
new file mode 100644
index 00000000..5fb05446
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 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.
diff --git a/Nuake/src/Vendors/msdfgen/Msdfgen.rc b/Nuake/src/Vendors/msdfgen/Msdfgen.rc
new file mode 100644
index 00000000..38b344f8
Binary files /dev/null and b/Nuake/src/Vendors/msdfgen/Msdfgen.rc differ
diff --git a/Nuake/src/Vendors/msdfgen/Msdfgen.sln b/Nuake/src/Vendors/msdfgen/Msdfgen.sln
new file mode 100644
index 00000000..19d35c0d
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/Msdfgen.sln
@@ -0,0 +1,52 @@
+
+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.vcxproj", "{84BE2D91-F071-4151-BE12-61460464C494}"
+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 OpenMP|x64 = Release Library OpenMP|x64
+ Release Library OpenMP|x86 = Release Library OpenMP|x86
+ Release Library|x64 = Release Library|x64
+ Release Library|x86 = Release Library|x86
+ Release OpenMP|x64 = Release OpenMP|x64
+ Release OpenMP|x86 = Release OpenMP|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|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Debug|x64.Build.0 = Debug|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Debug|x86.ActiveCfg = Debug|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Debug|x86.Build.0 = Debug|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release Library OpenMP|x64.ActiveCfg = Release Library OpenMP|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release Library OpenMP|x64.Build.0 = Release Library OpenMP|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release Library OpenMP|x86.ActiveCfg = Release Library OpenMP|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release Library OpenMP|x86.Build.0 = Release Library OpenMP|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 OpenMP|x64.ActiveCfg = Release OpenMP|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release OpenMP|x64.Build.0 = Release OpenMP|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release OpenMP|x86.ActiveCfg = Release OpenMP|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release OpenMP|x86.Build.0 = Release OpenMP|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release|x64.ActiveCfg = Release|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release|x64.Build.0 = Release|x64
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release|x86.ActiveCfg = Release|Win32
+ {84BE2D91-F071-4151-BE12-61460464C494}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj b/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj
new file mode 100644
index 00000000..88cbd772
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj
@@ -0,0 +1,531 @@
+
+
+
+
+ Debug Library
+ Win32
+
+
+ Debug Library
+ x64
+
+
+ Debug
+ Win32
+
+
+ Release Library OpenMP
+ Win32
+
+
+ Release Library OpenMP
+ x64
+
+
+ Release Library
+ Win32
+
+
+ Release Library
+ x64
+
+
+ Release OpenMP
+ Win32
+
+
+ Release OpenMP
+ x64
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {84BE2D91-F071-4151-BE12-61460464C494}
+ Msdfgen
+ 8.1
+
+
+
+ Application
+ true
+ v140
+ MultiByte
+
+
+ StaticLibrary
+ true
+ v140
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v140
+ true
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v140
+ true
+ MultiByte
+
+
+ Application
+ true
+ v140
+ MultiByte
+
+
+ StaticLibrary
+ true
+ v140
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v140
+ true
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v140
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ msdfgen
+ $(Configuration)\
+
+
+ msdfgen
+ $(Configuration)\
+
+
+ msdfgen
+ bin\
+
+
+ msdfgen
+ $(Configuration)\
+
+
+ msdfgen
+ bin\
+
+
+ msdfgen
+ $(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+ msdfgen
+ $(Platform)\$(Configuration)\
+
+
+
+ Level3
+ Disabled
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_STANDALONE;%(PreprocessorDefinitions)
+ MultiThreadedDebug
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+
+
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/debug;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ Disabled
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)
+ MultiThreadedDebug
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+
+
+ Console
+ freetype;%(AdditionalLibraryDirectories)
+
+
+ MachineX86
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/debug;%(AdditionalLibraryDirectories)
+
+
+
+
+
+ Level3
+ Disabled
+ true
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_STANDALONE;%(PreprocessorDefinitions)
+ MultiThreadedDebug
+
+
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/debug;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ Disabled
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+
+
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/debug;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_STANDALONE;%(PreprocessorDefinitions)
+ MultiThreaded
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+
+
+ true
+ true
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+ No
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_USE_OPENMP;MSDFGEN_STANDALONE;%(PreprocessorDefinitions)
+ MultiThreaded
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ true
+
+
+ true
+ true
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+ No
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)
+ MultiThreaded
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+
+
+ true
+ true
+ Console
+ lib;%(AdditionalLibraryDirectories)
+ No
+
+
+ MachineX86
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_USE_OPENMP;%(PreprocessorDefinitions)
+ MultiThreaded
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ true
+
+
+ true
+ true
+ Console
+ lib;%(AdditionalLibraryDirectories)
+ No
+
+
+ MachineX86
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_STANDALONE;%(PreprocessorDefinitions)
+ MultiThreaded
+
+
+ true
+ true
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+ false
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_STANDALONE;MSDFGEN_USE_OPENMP;%(PreprocessorDefinitions)
+ MultiThreaded
+ true
+
+
+ true
+ true
+ Console
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+ false
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;%(PreprocessorDefinitions)
+ MultiThreaded
+
+
+ true
+ true
+
+
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ include;freetype/include;skia/include;skia/include/core;skia/include/config;%(AdditionalIncludeDirectories)
+ MSDFGEN_USE_CPP11;MSDFGEN_USE_SKIA;MSDFGEN_USE_OPENMP;%(PreprocessorDefinitions)
+ MultiThreaded
+ true
+
+
+ true
+ true
+
+
+ freetype/win$(PlatformArchitecture);skia/win$(PlatformArchitecture)/release;%(AdditionalLibraryDirectories)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj.filters b/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj.filters
new file mode 100644
index 00000000..083df943
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/Msdfgen.vcxproj.filters
@@ -0,0 +1,229 @@
+
+
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {110bf5de-0605-40a4-92b4-68ca012d572f}
+
+
+ {8abe3d60-6507-4ee1-8d4f-eab2c43a3220}
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {42db228a-5d46-439c-ad30-7595a74d635f}
+
+
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Extensions
+
+
+ Extensions
+
+
+ Extensions
+
+
+ Extensions
+
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Extensions
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+
+
+ Standalone
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Extensions
+
+
+ Extensions
+
+
+ Core
+
+
+ Core
+
+
+ Extensions
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Source Dependencies
+
+
+ Source Dependencies
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Core
+
+
+ Extensions
+
+
+ Core
+
+
+ Core
+
+
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/Nuake/src/Vendors/msdfgen/README.md b/Nuake/src/Vendors/msdfgen/README.md
new file mode 100644
index 00000000..c624256a
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/README.md
@@ -0,0 +1,209 @@
+# Multi-channel signed distance field generator
+
+This is a utility for generating signed distance fields from vector shapes and font glyphs,
+which serve as a texture representation that can be used in real-time graphics to efficiently reproduce said shapes.
+Although it can also be used to generate conventional signed distance fields best known from
+[this Valve paper](https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf)
+and pseudo-distance fields, its primary purpose is to generate multi-channel distance fields,
+using a method I have developed. Unlike monochrome distance fields, they have the ability
+to reproduce sharp corners almost perfectly by utilizing all three color channels.
+
+The following comparison demonstrates the improvement in image quality.
+
+
+
+
+
+- To learn more about this method, you can read my [Master's thesis](https://github.com/Chlumsky/msdfgen/files/3050967/thesis.pdf).
+- Check out my [MSDF-Atlas-Gen](https://github.com/Chlumsky/msdf-atlas-gen) if you want to generate entire glyph atlases for text rendering.
+- See what's new in the [changelog](CHANGELOG.md).
+
+## Getting started
+
+The project can be used either as a library or as a console program. It is divided into two parts, **[core](core)**
+and **[extensions](ext)**. The core module has no dependencies and only uses bare C++. It contains all
+key data structures and algorithms, which can be accessed through the [msdfgen.h](msdfgen.h) header.
+Extensions contain utilities for loading fonts and SVG files, as well as saving PNG images.
+Those are exposed by the [msdfgen-ext.h](msdfgen-ext.h) header. This module uses
+[FreeType](http://www.freetype.org/),
+[TinyXML2](http://www.grinninglizard.com/tinyxml2/),
+[LodePNG](http://lodev.org/lodepng/),
+and (optionally) [Skia](https://skia.org/).
+
+Additionally, there is the [main.cpp](main.cpp), which wraps the functionality into
+a comprehensive 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/msdfgen/releases).
+To build the project, you may use the included [Visual Studio solution](Msdfgen.sln)
+or [CMake script](CMakeLists.txt).
+
+## Console commands
+
+The standalone program is executed as
+```
+msdfgen.exe
+```
+where only the input specification is required.
+
+Mode can be one of:
+ - **sdf** – generates a conventional monochrome (true) signed distance field.
+ - **psdf** – generates a monochrome signed pseudo-distance field.
+ - **msdf** (default) – generates a multi-channel signed distance field using my new method.
+ - **mtsdf** – generates a combined multi-channel and true signed distance field in the alpha channel.
+
+The input can be specified as one of:
+ - **-font \ \** – to load a glyph from a font file.
+ Character code can be expressed as either a decimal (63) or hexadecimal (0x3F) Unicode value, or an ASCII character
+ in single quotes ('?').
+ - **-svg \** – to load an SVG file. Note that only the last vector path in the file will be used.
+ - **-shapedesc \**, -defineshape \, -stdin – to load a text description of the shape
+ from either a file, the next argument, or the standard input, respectively. Its syntax is documented further down.
+
+The complete list of available options can be printed with **-help**.
+Some of the important ones are:
+ - **-o \** – specifies the output file name. The desired format will be deduced from the extension
+ (png, bmp, tif, txt, bin). Otherwise, use -format.
+ - **-size \ \** – specifies the dimensions of the output distance field (in pixels).
+ - **-range \**, **-pxrange \** – specifies the width of the range around the shape
+ between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.
+ - **-scale \** – sets the scale used to convert shape units to distance field pixels.
+ - **-translate \ \** – sets the translation of the shape in shape units. Otherwise the origin (0, 0)
+ lies in the bottom left corner.
+ - **-autoframe** – automatically frames the shape to fit the distance field. If the output must be precisely aligned,
+ you should manually position it using -translate and -scale instead.
+ - **-angle \** – specifies the maximum angle to be considered a corner.
+ Can be expressed in radians (3.0) or degrees with D at the end (171.9D).
+ - **-testrender \ \ \** - tests the generated distance field by using it to render an image
+ of the original shape into a PNG file with the specified dimensions. Alternatively, -testrendermulti renders
+ an image without combining the color channels, and may give you an insight in how the multi-channel distance field works.
+ - **-exportshape \** - saves the text description of the shape with edge coloring to the specified file.
+ This can be later edited and used as input through -shapedesc.
+ - **-printmetrics** – prints some useful information about the shape's layout.
+
+For example,
+```
+msdfgen.exe msdf -font C:\Windows\Fonts\arialbd.ttf 'M' -o msdf.png -size 32 32 -pxrange 4 -autoframe -testrender render.png 1024 1024
+```
+
+will take the glyph capital M from the Arial Bold typeface, generate a 32×32 multi-channel distance field
+with a 4 pixels wide distance range, store it into msdf.png, and create a test render of the glyph as render.png.
+
+**Note:** Do not use `-autoframe` to generate character maps! It is intended as a quick preview only.
+
+## Library API
+
+If you choose to use this utility inside your own program, there are a few simple steps you need to perform
+in order to generate a distance field. Please note that all classes and functions are in the `msdfgen` namespace.
+
+ - Acquire a `Shape` object. You can either load it via `loadGlyph` or `loadSvgShape`, or construct it manually.
+ It consists of closed contours, which in turn consist of edges. An edge is represented by a `LinearEdge`, `QuadraticEdge`,
+ or `CubicEdge`. You can construct them from two endpoints and 0 to 2 Bézier control points.
+ - Normalize the shape using its `normalize` method and assign colors to edges if you need a multi-channel SDF.
+ This can be performed automatically using the `edgeColoringSimple` heuristic, or manually by setting each edge's
+ `color` member. Keep in mind that at least two color channels must be turned on in each edge, and iff two edges meet
+ at a sharp corner, they must only have one channel in common.
+ - Call `generateSDF`, `generatePseudoSDF`, or `generateMSDF` to generate a distance field into a floating point
+ `Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, or `saveTiff`.
+ - You may also render an image from the distance field using `renderSDF`. Consider calling `simulate8bit`
+ on the distance field beforehand to simulate the standard 8 bits/channel image format.
+
+Example:
+```c++
+#include "msdfgen.h"
+#include "msdfgen-ext.h"
+
+using namespace msdfgen;
+
+int main() {
+ FreetypeHandle *ft = initializeFreetype();
+ if (ft) {
+ FontHandle *font = loadFont(ft, "C:\\Windows\\Fonts\\arialbd.ttf");
+ if (font) {
+ Shape shape;
+ if (loadGlyph(shape, font, 'A')) {
+ shape.normalize();
+ // max. angle
+ edgeColoringSimple(shape, 3.0);
+ // image width, height
+ Bitmap msdf(32, 32);
+ // range, scale, translation
+ generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
+ savePng(msdf, "output.png");
+ }
+ destroyFont(font);
+ }
+ deinitializeFreetype(ft);
+ }
+ return 0;
+}
+
+```
+
+## Using a multi-channel distance field
+
+Using a multi-channel distance field generated by this program is similarly simple to how a monochrome distance field is used.
+The only additional operation is computing the **median** of the three channels inside the fragment shader,
+right after sampling the distance field. This signed distance value can then be used the same way as usual.
+
+The following is an example GLSL fragment shader with anti-aliasing:
+
+```glsl
+in vec2 texCoord;
+out vec4 color;
+uniform sampler2D msdf;
+uniform vec4 bgColor;
+uniform vec4 fgColor;
+
+float median(float r, float g, float b) {
+ return max(min(r, g), min(max(r, g), b));
+}
+
+void main() {
+ vec3 msd = texture(msdf, texCoord).rgb;
+ float sd = median(msd.r, msd.g, msd.b);
+ float screenPxDistance = screenPxRange()*(sd - 0.5);
+ float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
+ color = mix(bgColor, fgColor, opacity);
+}
+```
+
+Here, `screenPxRange()` represents the distance field range in output screen pixels. For example, if the pixel range was set to 2
+when generating a 32x32 distance field, and it is used to draw a quad that is 72x72 pixels on the screen,
+it should return 4.5 (because 72/32 * 2 = 4.5).
+**For 2D rendering, this can generally be replaced by a precomputed uniform value.**
+
+For rendering in a **3D perspective only**, where the texture scale varies across the screen,
+you may want to implement this function with fragment derivatives in the following way.
+I would suggest precomputing `unitRange` as a uniform variable instead of `pxRange` for better performance.
+
+```glsl
+uniform float pxRange; // set to distance field's pixel range
+
+float screenPxRange() {
+ vec2 unitRange = vec2(pxRange)/vec2(textureSize(msdf, 0));
+ vec2 screenTexSize = vec2(1.0)/fwidth(texCoord);
+ return max(0.5*dot(unitRange, screenTexSize), 1.0);
+}
+```
+
+`screenPxRange()` must never be lower than 1. If it is lower than 2, there is a high probability that the anti-aliasing will fail
+and you may want to re-generate your distance field with a wider range.
+
+## Shape description syntax
+
+The text shape description has the following syntax.
+ - Each closed contour is enclosed by braces: `{ } { }`
+ - Each point (and control point) is written as two real numbers separated by a comma.
+ - Points in a contour are separated with semicolons.
+ - The last point of each contour must be equal to the first, or the symbol `#` can be used, which represents the first point.
+ - There can be an edge segment specification between any two points, also separated by semicolons.
+ This can include the edge's color (`c`, `m`, `y` or `w`) and/or one or two Bézier curve control points inside parentheses.
+
+For example,
+```
+{ -1, -1; m; -1, +1; y; +1, +1; m; +1, -1; y; # }
+```
+would represent a square with magenta and yellow edges,
+```
+{ 0, 1; (+1.6, -0.8; -1.6, -0.8); # }
+```
+is a teardrop shape formed by a single cubic Bézier curve.
diff --git a/Nuake/src/Vendors/msdfgen/cmake/folderize.cmake b/Nuake/src/Vendors/msdfgen/cmake/folderize.cmake
new file mode 100644
index 00000000..93e08adf
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/cmake/folderize.cmake
@@ -0,0 +1,17 @@
+# Mirror the folder structure for sources inside the IDE...
+function(folderize_sources sources prefix)
+ foreach(FILE ${${sources}})
+ get_filename_component(PARENT_DIR "${FILE}" PATH)
+
+ # skip src or include and changes /'s to \\'s
+ string(REPLACE "${prefix}" "" GROUP "${PARENT_DIR}")
+ string(REPLACE "/" "\\" GROUP "${GROUP}")
+
+ # If it's got a path, then append a "\\" separator (otherwise leave it blank)
+ if ("${GROUP}" MATCHES ".+")
+ set(GROUP "\\${GROUP}")
+ endif()
+
+ source_group("${GROUP}" FILES "${FILE}")
+ endforeach()
+endfunction(folderize_sources)
diff --git a/Nuake/src/Vendors/msdfgen/cmake/msdfgenConfig.cmake.in b/Nuake/src/Vendors/msdfgen/cmake/msdfgenConfig.cmake.in
new file mode 100644
index 00000000..15a571f9
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/cmake/msdfgenConfig.cmake.in
@@ -0,0 +1,12 @@
+include(CMakeFindDependencyMacro)
+
+find_dependency(Freetype REQUIRED)
+
+set(MSDFGEN_USE_OPENMP "@MSDFGEN_USE_OPENMP@")
+if(MSDFGEN_USE_OPENMP)
+ find_dependency(OpenMP REQUIRED COMPONENTS CXX)
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/msdfgenTargets.cmake")
+
+unset(MSDFGEN_USE_OPENMP)
diff --git a/Nuake/src/Vendors/msdfgen/core/Bitmap.h b/Nuake/src/Vendors/msdfgen/core/Bitmap.h
new file mode 100644
index 00000000..14407d6c
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Bitmap.h
@@ -0,0 +1,50 @@
+
+#pragma once
+
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// A 2D image bitmap with N channels of type T. Pixel memory is managed by the class.
+template
+class Bitmap {
+
+public:
+ Bitmap();
+ Bitmap(int width, int height);
+ Bitmap(const BitmapConstRef &orig);
+ Bitmap(const Bitmap &orig);
+#ifdef MSDFGEN_USE_CPP11
+ Bitmap(Bitmap &&orig);
+#endif
+ ~Bitmap();
+ Bitmap & operator=(const BitmapConstRef &orig);
+ Bitmap & operator=(const Bitmap &orig);
+#ifdef MSDFGEN_USE_CPP11
+ Bitmap & operator=(Bitmap &&orig);
+#endif
+ /// Bitmap width in pixels.
+ int width() const;
+ /// Bitmap height in pixels.
+ int height() const;
+ T * operator()(int x, int y);
+ const T * operator()(int x, int y) const;
+#ifdef MSDFGEN_USE_CPP11
+ explicit operator T *();
+ explicit operator const T *() const;
+#else
+ operator T *();
+ operator const T *() const;
+#endif
+ operator BitmapRef();
+ operator BitmapConstRef() const;
+
+private:
+ T *pixels;
+ int w, h;
+
+};
+
+}
+
+#include "Bitmap.hpp"
diff --git a/Nuake/src/Vendors/msdfgen/core/Bitmap.hpp b/Nuake/src/Vendors/msdfgen/core/Bitmap.hpp
new file mode 100644
index 00000000..cb16cac8
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Bitmap.hpp
@@ -0,0 +1,117 @@
+
+#include "Bitmap.h"
+
+#include
+#include
+
+namespace msdfgen {
+
+template
+Bitmap::Bitmap() : pixels(NULL), w(0), h(0) { }
+
+template
+Bitmap::Bitmap(int width, int height) : w(width), h(height) {
+ pixels = new T[N*w*h];
+}
+
+template
+Bitmap::Bitmap(const BitmapConstRef &orig) : w(orig.width), h(orig.height) {
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+}
+
+template
+Bitmap::Bitmap(const Bitmap &orig) : w(orig.w), h(orig.h) {
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+template
+Bitmap::Bitmap(Bitmap &&orig) : pixels(orig.pixels), w(orig.w), h(orig.h) {
+ orig.pixels = NULL;
+ orig.w = 0, orig.h = 0;
+}
+#endif
+
+template
+Bitmap::~Bitmap() {
+ delete [] pixels;
+}
+
+template
+Bitmap & Bitmap::operator=(const BitmapConstRef &orig) {
+ if (pixels != orig.pixels) {
+ delete [] pixels;
+ w = orig.width, h = orig.height;
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+ }
+ return *this;
+}
+
+template
+Bitmap & Bitmap::operator=(const Bitmap &orig) {
+ if (this != &orig) {
+ delete [] pixels;
+ w = orig.w, h = orig.h;
+ pixels = new T[N*w*h];
+ memcpy(pixels, orig.pixels, sizeof(T)*N*w*h);
+ }
+ return *this;
+}
+
+#ifdef MSDFGEN_USE_CPP11
+template
+Bitmap & Bitmap::operator=(Bitmap &&orig) {
+ if (this != &orig) {
+ delete [] pixels;
+ pixels = orig.pixels;
+ w = orig.w, h = orig.h;
+ orig.pixels = NULL;
+ }
+ return *this;
+}
+#endif
+
+template
+int Bitmap::width() const {
+ return w;
+}
+
+template
+int Bitmap::height() const {
+ return h;
+}
+
+template
+T * Bitmap::operator()(int x, int y) {
+ return pixels+N*(w*y+x);
+}
+
+template
+const T * Bitmap::operator()(int x, int y) const {
+ return pixels+N*(w*y+x);
+}
+
+template
+Bitmap::operator T *() {
+ return pixels;
+}
+
+template
+Bitmap::operator const T *() const {
+ return pixels;
+}
+
+template
+Bitmap::operator BitmapRef() {
+ return BitmapRef(pixels, w, h);
+}
+
+template
+Bitmap::operator BitmapConstRef() const {
+ return BitmapConstRef(pixels, w, h);
+}
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/BitmapRef.hpp b/Nuake/src/Vendors/msdfgen/core/BitmapRef.hpp
new file mode 100644
index 00000000..6f9620dc
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/BitmapRef.hpp
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include
+
+namespace msdfgen {
+
+typedef unsigned char byte;
+
+/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
+template
+struct BitmapRef {
+
+ T *pixels;
+ int width, height;
+
+ inline BitmapRef() : pixels(NULL), width(0), height(0) { }
+ inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
+
+ inline T * operator()(int x, int y) const {
+ return pixels+N*(width*y+x);
+ }
+
+};
+
+/// Constant reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
+template
+struct BitmapConstRef {
+
+ const T *pixels;
+ int width, height;
+
+ inline BitmapConstRef() : pixels(NULL), width(0), height(0) { }
+ inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
+ inline BitmapConstRef(const BitmapRef &orig) : pixels(orig.pixels), width(orig.width), height(orig.height) { }
+
+ inline const T * operator()(int x, int y) const {
+ return pixels+N*(width*y+x);
+ }
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Contour.cpp b/Nuake/src/Vendors/msdfgen/core/Contour.cpp
new file mode 100644
index 00000000..ca80d3c5
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Contour.cpp
@@ -0,0 +1,90 @@
+
+#include "Contour.h"
+
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static double shoelace(const Point2 &a, const Point2 &b) {
+ return (b.x-a.x)*(a.y+b.y);
+}
+
+void Contour::addEdge(const EdgeHolder &edge) {
+ edges.push_back(edge);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Contour::addEdge(EdgeHolder &&edge) {
+ edges.push_back((EdgeHolder &&) edge);
+}
+#endif
+
+EdgeHolder & Contour::addEdge() {
+ edges.resize(edges.size()+1);
+ return edges.back();
+}
+
+static void boundPoint(double &l, double &b, double &r, double &t, Point2 p) {
+ if (p.x < l) l = p.x;
+ if (p.y < b) b = p.y;
+ if (p.x > r) r = p.x;
+ if (p.y > t) t = p.y;
+}
+
+void Contour::bound(double &l, double &b, double &r, double &t) const {
+ for (std::vector::const_iterator edge = edges.begin(); edge != edges.end(); ++edge)
+ (*edge)->bound(l, b, r, t);
+}
+
+void Contour::boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const {
+ if (edges.empty())
+ return;
+ Vector2 prevDir = edges.back()->direction(1).normalize(true);
+ for (std::vector::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
+ Vector2 dir = -(*edge)->direction(0).normalize(true);
+ if (polarity*crossProduct(prevDir, dir) >= 0) {
+ double miterLength = miterLimit;
+ double q = .5*(1-dotProduct(prevDir, dir));
+ if (q > 0)
+ miterLength = min(1/sqrt(q), miterLimit);
+ Point2 miter = (*edge)->point(0)+border*miterLength*(prevDir+dir).normalize(true);
+ boundPoint(l, b, r, t, miter);
+ }
+ prevDir = (*edge)->direction(1).normalize(true);
+ }
+}
+
+int Contour::winding() const {
+ if (edges.empty())
+ return 0;
+ double total = 0;
+ if (edges.size() == 1) {
+ Point2 a = edges[0]->point(0), b = edges[0]->point(1/3.), c = edges[0]->point(2/3.);
+ total += shoelace(a, b);
+ total += shoelace(b, c);
+ total += shoelace(c, a);
+ } else if (edges.size() == 2) {
+ Point2 a = edges[0]->point(0), b = edges[0]->point(.5), c = edges[1]->point(0), d = edges[1]->point(.5);
+ total += shoelace(a, b);
+ total += shoelace(b, c);
+ total += shoelace(c, d);
+ total += shoelace(d, a);
+ } else {
+ Point2 prev = edges.back()->point(0);
+ for (std::vector::const_iterator edge = edges.begin(); edge != edges.end(); ++edge) {
+ Point2 cur = (*edge)->point(0);
+ total += shoelace(prev, cur);
+ prev = cur;
+ }
+ }
+ return sign(total);
+}
+
+void Contour::reverse() {
+ for (int i = (int) edges.size()/2; i > 0; --i)
+ EdgeHolder::swap(edges[i-1], edges[edges.size()-i]);
+ for (std::vector::iterator edge = edges.begin(); edge != edges.end(); ++edge)
+ (*edge)->reverse();
+}
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Contour.h b/Nuake/src/Vendors/msdfgen/core/Contour.h
new file mode 100644
index 00000000..f79b2695
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Contour.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include
+#include "EdgeHolder.h"
+
+namespace msdfgen {
+
+/// A single closed contour of a shape.
+class Contour {
+
+public:
+ /// The sequence of edges that make up the contour.
+ std::vector edges;
+
+ /// Adds an edge to the contour.
+ void addEdge(const EdgeHolder &edge);
+#ifdef MSDFGEN_USE_CPP11
+ void addEdge(EdgeHolder &&edge);
+#endif
+ /// Creates a new edge in the contour and returns its reference.
+ EdgeHolder & addEdge();
+ /// Adjusts the bounding box to fit the contour.
+ void bound(double &l, double &b, double &r, double &t) const;
+ /// Adjusts the bounding box to fit the contour border's mitered corners.
+ void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
+ /// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
+ int winding() const;
+ /// Reverses the sequence of edges on the contour.
+ void reverse();
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/EdgeColor.h b/Nuake/src/Vendors/msdfgen/core/EdgeColor.h
new file mode 100644
index 00000000..9d49a5a8
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/EdgeColor.h
@@ -0,0 +1,18 @@
+
+#pragma once
+
+namespace msdfgen {
+
+/// Edge color specifies which color channels an edge belongs to.
+enum EdgeColor {
+ BLACK = 0,
+ RED = 1,
+ GREEN = 2,
+ YELLOW = 3,
+ BLUE = 4,
+ MAGENTA = 5,
+ CYAN = 6,
+ WHITE = 7
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/EdgeHolder.cpp b/Nuake/src/Vendors/msdfgen/core/EdgeHolder.cpp
new file mode 100644
index 00000000..1a8c5f66
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/EdgeHolder.cpp
@@ -0,0 +1,77 @@
+
+#include "EdgeHolder.h"
+
+namespace msdfgen {
+
+void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
+ EdgeSegment *tmp = a.edgeSegment;
+ a.edgeSegment = b.edgeSegment;
+ b.edgeSegment = tmp;
+}
+
+EdgeHolder::EdgeHolder() : edgeSegment(NULL) { }
+
+EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor) : edgeSegment(new LinearSegment(p0, p1, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor) : edgeSegment(new QuadraticSegment(p0, p1, p2, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : edgeSegment(new CubicSegment(p0, p1, p2, p3, edgeColor)) { }
+
+EdgeHolder::EdgeHolder(const EdgeHolder &orig) : edgeSegment(orig.edgeSegment ? orig.edgeSegment->clone() : NULL) { }
+
+#ifdef MSDFGEN_USE_CPP11
+EdgeHolder::EdgeHolder(EdgeHolder &&orig) : edgeSegment(orig.edgeSegment) {
+ orig.edgeSegment = NULL;
+}
+#endif
+
+EdgeHolder::~EdgeHolder() {
+ delete edgeSegment;
+}
+
+EdgeHolder & EdgeHolder::operator=(const EdgeHolder &orig) {
+ if (this != &orig) {
+ delete edgeSegment;
+ edgeSegment = orig.edgeSegment ? orig.edgeSegment->clone() : NULL;
+ }
+ return *this;
+}
+
+#ifdef MSDFGEN_USE_CPP11
+EdgeHolder & EdgeHolder::operator=(EdgeHolder &&orig) {
+ if (this != &orig) {
+ delete edgeSegment;
+ edgeSegment = orig.edgeSegment;
+ orig.edgeSegment = NULL;
+ }
+ return *this;
+}
+#endif
+
+EdgeSegment & EdgeHolder::operator*() {
+ return *edgeSegment;
+}
+
+const EdgeSegment & EdgeHolder::operator*() const {
+ return *edgeSegment;
+}
+
+EdgeSegment * EdgeHolder::operator->() {
+ return edgeSegment;
+}
+
+const EdgeSegment * EdgeHolder::operator->() const {
+ return edgeSegment;
+}
+
+EdgeHolder::operator EdgeSegment *() {
+ return edgeSegment;
+}
+
+EdgeHolder::operator const EdgeSegment *() const {
+ return edgeSegment;
+}
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/EdgeHolder.h b/Nuake/src/Vendors/msdfgen/core/EdgeHolder.h
new file mode 100644
index 00000000..c4c5be76
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/EdgeHolder.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "edge-segments.h"
+
+namespace msdfgen {
+
+/// Container for a single edge of dynamic type.
+class EdgeHolder {
+
+public:
+ /// Swaps the edges held by a and b.
+ static void swap(EdgeHolder &a, EdgeHolder &b);
+
+ EdgeHolder();
+ EdgeHolder(EdgeSegment *segment);
+ EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
+ EdgeHolder(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
+ EdgeHolder(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
+ EdgeHolder(const EdgeHolder &orig);
+#ifdef MSDFGEN_USE_CPP11
+ EdgeHolder(EdgeHolder &&orig);
+#endif
+ ~EdgeHolder();
+ EdgeHolder & operator=(const EdgeHolder &orig);
+#ifdef MSDFGEN_USE_CPP11
+ EdgeHolder & operator=(EdgeHolder &&orig);
+#endif
+ EdgeSegment & operator*();
+ const EdgeSegment & operator*() const;
+ EdgeSegment * operator->();
+ const EdgeSegment * operator->() const;
+ operator EdgeSegment *();
+ operator const EdgeSegment *() const;
+
+private:
+ EdgeSegment *edgeSegment;
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.cpp b/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.cpp
new file mode 100644
index 00000000..aaa6a866
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.cpp
@@ -0,0 +1,495 @@
+
+#include "MSDFErrorCorrection.h"
+
+#include
+#include "arithmetics.hpp"
+#include "equation-solver.h"
+#include "EdgeColor.h"
+#include "bitmap-interpolation.hpp"
+#include "edge-selectors.h"
+#include "contour-combiners.h"
+#include "ShapeDistanceFinder.h"
+#include "generator-config.h"
+
+namespace msdfgen {
+
+#define ARTIFACT_T_EPSILON .01
+#define PROTECTION_RADIUS_TOLERANCE 1.001
+
+#define CLASSIFIER_FLAG_CANDIDATE 0x01
+#define CLASSIFIER_FLAG_ARTIFACT 0x02
+
+const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
+const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
+
+/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
+class BaseArtifactClassifier {
+public:
+ inline BaseArtifactClassifier(double span, bool protectedFlag) : span(span), protectedFlag(protectedFlag) { }
+ /// Evaluates if the median value xm interpolated at xt in the range between am at at and bm at bt indicates an artifact.
+ inline int rangeTest(double at, double bt, double xt, float am, float bm, float xm) const {
+ // For protected texels, only consider inversion artifacts (interpolated median has different sign than boundaries). For the rest, it is sufficient that the interpolated median is outside its boundaries.
+ if ((am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f) || (!protectedFlag && median(am, bm, xm) != xm)) {
+ double axSpan = (xt-at)*span, bxSpan = (bt-xt)*span;
+ // Check if the interpolated median's value is in the expected range based on its distance (span) from boundaries a, b.
+ if (!(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan))
+ return CLASSIFIER_FLAG_CANDIDATE|CLASSIFIER_FLAG_ARTIFACT;
+ return CLASSIFIER_FLAG_CANDIDATE;
+ }
+ return 0;
+ }
+ /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
+ inline bool evaluate(double t, float m, int flags) const {
+ return (flags&2) != 0;
+ }
+private:
+ double span;
+ bool protectedFlag;
+};
+
+/// The shape distance checker evaluates the exact shape distance to find additional artifacts at a significant performance cost.
+template class ContourCombiner, int N>
+class ShapeDistanceChecker {
+public:
+ class ArtifactClassifier : public BaseArtifactClassifier {
+ public:
+ inline ArtifactClassifier(ShapeDistanceChecker *parent, const Vector2 &direction, double span) : BaseArtifactClassifier(span, parent->protectedFlag), parent(parent), direction(direction) { }
+ /// Returns true if the combined results of the tests performed on the median value m interpolated at t indicate an artifact.
+ inline bool evaluate(double t, float m, int flags) const {
+ if (flags&CLASSIFIER_FLAG_CANDIDATE) {
+ // Skip expensive distance evaluation if the point has already been classified as an artifact by the base classifier.
+ if (flags&CLASSIFIER_FLAG_ARTIFACT)
+ return true;
+ Vector2 tVector = t*direction;
+ float oldMSD[N], newMSD[3];
+ // Compute the color that would be currently interpolated at the artifact candidate's position.
+ Point2 sdfCoord = parent->sdfCoord+tVector;
+ interpolate(oldMSD, parent->sdf, sdfCoord);
+ // Compute the color that would be interpolated at the artifact candidate's position if error correction was applied on the current texel.
+ double aWeight = (1-fabs(tVector.x))*(1-fabs(tVector.y));
+ float aPSD = median(parent->msd[0], parent->msd[1], parent->msd[2]);
+ newMSD[0] = float(oldMSD[0]+aWeight*(aPSD-parent->msd[0]));
+ newMSD[1] = float(oldMSD[1]+aWeight*(aPSD-parent->msd[1]));
+ newMSD[2] = float(oldMSD[2]+aWeight*(aPSD-parent->msd[2]));
+ // Compute the evaluated distance (interpolated median) before and after error correction, as well as the exact shape distance.
+ float oldPSD = median(oldMSD[0], oldMSD[1], oldMSD[2]);
+ float newPSD = median(newMSD[0], newMSD[1], newMSD[2]);
+ float refPSD = float(parent->invRange*parent->distanceFinder.distance(parent->shapeCoord+tVector*parent->texelSize)+.5);
+ // Compare the differences of the exact distance and the before and after distances.
+ return parent->minImproveRatio*fabsf(newPSD-refPSD) < double(fabsf(oldPSD-refPSD));
+ }
+ return false;
+ }
+ private:
+ ShapeDistanceChecker *parent;
+ Vector2 direction;
+ };
+ Point2 shapeCoord, sdfCoord;
+ const float *msd;
+ bool protectedFlag;
+ inline ShapeDistanceChecker(const BitmapConstRef &sdf, const Shape &shape, const Projection &projection, double invRange, double minImproveRatio) : distanceFinder(shape), sdf(sdf), invRange(invRange), minImproveRatio(minImproveRatio) {
+ texelSize = projection.unprojectVector(Vector2(1));
+ }
+ inline ArtifactClassifier classifier(const Vector2 &direction, double span) {
+ return ArtifactClassifier(this, direction, span);
+ }
+private:
+ ShapeDistanceFinder > distanceFinder;
+ BitmapConstRef sdf;
+ double invRange;
+ Vector2 texelSize;
+ double minImproveRatio;
+};
+
+MSDFErrorCorrection::MSDFErrorCorrection() { }
+
+MSDFErrorCorrection::MSDFErrorCorrection(const BitmapRef &stencil, const Projection &projection, double range) : stencil(stencil), projection(projection) {
+ invRange = 1/range;
+ minDeviationRatio = ErrorCorrectionConfig::defaultMinDeviationRatio;
+ minImproveRatio = ErrorCorrectionConfig::defaultMinImproveRatio;
+ memset(stencil.pixels, 0, sizeof(byte)*stencil.width*stencil.height);
+}
+
+void MSDFErrorCorrection::setMinDeviationRatio(double minDeviationRatio) {
+ this->minDeviationRatio = minDeviationRatio;
+}
+
+void MSDFErrorCorrection::setMinImproveRatio(double minImproveRatio) {
+ this->minImproveRatio = minImproveRatio;
+}
+
+void MSDFErrorCorrection::protectCorners(const Shape &shape) {
+ for (std::vector::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour)
+ if (!contour->edges.empty()) {
+ const EdgeSegment *prevEdge = contour->edges.back();
+ for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ int commonColor = prevEdge->color&(*edge)->color;
+ // If the color changes from prevEdge to edge, this is a corner.
+ if (!(commonColor&(commonColor-1))) {
+ // Find the four texels that envelop the corner and mark them as protected.
+ Point2 p = projection.project((*edge)->point(0));
+ if (shape.inverseYAxis)
+ p.y = stencil.height-p.y;
+ int l = (int) floor(p.x-.5);
+ int b = (int) floor(p.y-.5);
+ int r = l+1;
+ int t = b+1;
+ // Check that the positions are within bounds.
+ if (l < stencil.width && b < stencil.height && r >= 0 && t >= 0) {
+ if (l >= 0 && b >= 0)
+ *stencil(l, b) |= (byte) PROTECTED;
+ if (r < stencil.width && b >= 0)
+ *stencil(r, b) |= (byte) PROTECTED;
+ if (l >= 0 && t < stencil.height)
+ *stencil(l, t) |= (byte) PROTECTED;
+ if (r < stencil.width && t < stencil.height)
+ *stencil(r, t) |= (byte) PROTECTED;
+ }
+ }
+ prevEdge = *edge;
+ }
+ }
+}
+
+/// Determines if the channel contributes to an edge between the two texels a, b.
+static bool edgeBetweenTexelsChannel(const float *a, const float *b, int channel) {
+ // Find interpolation ratio t (0 < t < 1) where an edge is expected (mix(a[channel], b[channel], t) == 0.5).
+ double t = (a[channel]-.5)/(a[channel]-b[channel]);
+ if (t > 0 && t < 1) {
+ // Interpolate channel values at t.
+ float c[3] = {
+ mix(a[0], b[0], t),
+ mix(a[1], b[1], t),
+ mix(a[2], b[2], t)
+ };
+ // This is only an edge if the zero-distance channel is the median.
+ return median(c[0], c[1], c[2]) == c[channel];
+ }
+ return false;
+}
+
+/// Returns a bit mask of which channels contribute to an edge between the two texels a, b.
+static int edgeBetweenTexels(const float *a, const float *b) {
+ return (
+ RED*edgeBetweenTexelsChannel(a, b, 0)+
+ GREEN*edgeBetweenTexelsChannel(a, b, 1)+
+ BLUE*edgeBetweenTexelsChannel(a, b, 2)
+ );
+}
+
+/// Marks texel as protected if one of its non-median channels is present in the channel mask.
+static void protectExtremeChannels(byte *stencil, const float *msd, float m, int mask) {
+ if (
+ (mask&RED && msd[0] != m) ||
+ (mask&GREEN && msd[1] != m) ||
+ (mask&BLUE && msd[2] != m)
+ )
+ *stencil |= (byte) MSDFErrorCorrection::PROTECTED;
+}
+
+template
+void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf) {
+ float radius;
+ // Horizontal texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange, 0)).length());
+ for (int y = 0; y < sdf.height; ++y) {
+ const float *left = sdf(0, y);
+ const float *right = sdf(1, y);
+ for (int x = 0; x < sdf.width-1; ++x) {
+ float lm = median(left[0], left[1], left[2]);
+ float rm = median(right[0], right[1], right[2]);
+ if (fabsf(lm-.5f)+fabsf(rm-.5f) < radius) {
+ int mask = edgeBetweenTexels(left, right);
+ protectExtremeChannels(stencil(x, y), left, lm, mask);
+ protectExtremeChannels(stencil(x+1, y), right, rm, mask);
+ }
+ left += N, right += N;
+ }
+ }
+ // Vertical texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(0, invRange)).length());
+ for (int y = 0; y < sdf.height-1; ++y) {
+ const float *bottom = sdf(0, y);
+ const float *top = sdf(0, y+1);
+ for (int x = 0; x < sdf.width; ++x) {
+ float bm = median(bottom[0], bottom[1], bottom[2]);
+ float tm = median(top[0], top[1], top[2]);
+ if (fabsf(bm-.5f)+fabsf(tm-.5f) < radius) {
+ int mask = edgeBetweenTexels(bottom, top);
+ protectExtremeChannels(stencil(x, y), bottom, bm, mask);
+ protectExtremeChannels(stencil(x, y+1), top, tm, mask);
+ }
+ bottom += N, top += N;
+ }
+ }
+ // Diagonal texel pairs
+ radius = float(PROTECTION_RADIUS_TOLERANCE*projection.unprojectVector(Vector2(invRange)).length());
+ for (int y = 0; y < sdf.height-1; ++y) {
+ const float *lb = sdf(0, y);
+ const float *rb = sdf(1, y);
+ const float *lt = sdf(0, y+1);
+ const float *rt = sdf(1, y+1);
+ for (int x = 0; x < sdf.width-1; ++x) {
+ float mlb = median(lb[0], lb[1], lb[2]);
+ float mrb = median(rb[0], rb[1], rb[2]);
+ float mlt = median(lt[0], lt[1], lt[2]);
+ float mrt = median(rt[0], rt[1], rt[2]);
+ if (fabsf(mlb-.5f)+fabsf(mrt-.5f) < radius) {
+ int mask = edgeBetweenTexels(lb, rt);
+ protectExtremeChannels(stencil(x, y), lb, mlb, mask);
+ protectExtremeChannels(stencil(x+1, y+1), rt, mrt, mask);
+ }
+ if (fabsf(mrb-.5f)+fabsf(mlt-.5f) < radius) {
+ int mask = edgeBetweenTexels(rb, lt);
+ protectExtremeChannels(stencil(x+1, y), rb, mrb, mask);
+ protectExtremeChannels(stencil(x, y+1), lt, mlt, mask);
+ }
+ lb += N, rb += N, lt += N, rt += N;
+ }
+ }
+}
+
+void MSDFErrorCorrection::protectAll() {
+ byte *end = stencil.pixels+stencil.width*stencil.height;
+ for (byte *mask = stencil.pixels; mask < end; ++mask)
+ *mask |= (byte) PROTECTED;
+}
+
+/// Returns the median of the linear interpolation of texels a, b at t.
+static float interpolatedMedian(const float *a, const float *b, double t) {
+ return median(
+ mix(a[0], b[0], t),
+ mix(a[1], b[1], t),
+ mix(a[2], b[2], t)
+ );
+}
+/// Returns the median of the bilinear interpolation with the given constant, linear, and quadratic terms at t.
+static float interpolatedMedian(const float *a, const float *l, const float *q, double t) {
+ return float(median(
+ t*(t*q[0]+l[0])+a[0],
+ t*(t*q[1]+l[1])+a[1],
+ t*(t*q[2]+l[2])+a[2]
+ ));
+}
+
+/// Determines if the interpolated median xm is an artifact.
+static bool isArtifact(bool isProtected, double axSpan, double bxSpan, float am, float bm, float xm) {
+ return (
+ // For protected texels, only report an artifact if it would cause fill inversion (change between positive and negative distance).
+ (!isProtected || (am > .5f && bm > .5f && xm < .5f) || (am < .5f && bm < .5f && xm > .5f)) &&
+ // This is an artifact if the interpolated median is outside the range of possible values based on its distance from a, b.
+ !(xm >= am-axSpan && xm <= am+axSpan && xm >= bm-bxSpan && xm <= bm+bxSpan)
+ );
+}
+
+/// Checks if a linear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
+template
+static bool hasLinearArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float bm, const float *a, const float *b, float dA, float dB) {
+ // Find interpolation ratio t (0 < t < 1) where two color channels are equal (mix(dA, dB, t) == 0).
+ double t = (double) dA/(dA-dB);
+ if (t > ARTIFACT_T_EPSILON && t < 1-ARTIFACT_T_EPSILON) {
+ // Interpolate median at t and let the classifier decide if its value indicates an artifact.
+ float xm = interpolatedMedian(a, b, t);
+ return artifactClassifier.evaluate(t, xm, artifactClassifier.rangeTest(0, 1, t, am, bm, xm));
+ }
+ return false;
+}
+
+/// Checks if a bilinear interpolation artifact will occur at a point where two specific color channels are equal - such points have extreme median values.
+template
+static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifier, float am, float dm, const float *a, const float *l, const float *q, float dA, float dBC, float dD, double tEx0, double tEx1) {
+ // Find interpolation ratios t (0 < t[i] < 1) where two color channels are equal.
+ double t[2];
+ int solutions = solveQuadratic(t, dD-dBC+dA, dBC-dA-dA, dA);
+ for (int i = 0; i < solutions; ++i) {
+ // Solutions t[i] == 0 and t[i] == 1 are singularities and occur very often because two channels are usually equal at texels.
+ if (t[i] > ARTIFACT_T_EPSILON && t[i] < 1-ARTIFACT_T_EPSILON) {
+ // Interpolate median xm at t.
+ float xm = interpolatedMedian(a, l, q, t[i]);
+ // Determine if xm deviates too much from medians of a, d.
+ int rangeFlags = artifactClassifier.rangeTest(0, 1, t[i], am, dm, xm);
+ // Additionally, check xm against the interpolated medians at the local extremes tEx0, tEx1.
+ double tEnd[2];
+ float em[2];
+ // tEx0
+ if (tEx0 > 0 && tEx0 < 1) {
+ tEnd[0] = 0, tEnd[1] = 1;
+ em[0] = am, em[1] = dm;
+ tEnd[tEx0 > t[i]] = tEx0;
+ em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ }
+ // tEx1
+ if (tEx1 > 0 && tEx1 < 1) {
+ tEnd[0] = 0, tEnd[1] = 1;
+ em[0] = am, em[1] = dm;
+ tEnd[tEx1 > t[i]] = tEx1;
+ em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ }
+ if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Checks if a linear interpolation artifact will occur inbetween two horizontally or vertically adjacent texels a, b.
+template
+static bool hasLinearArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b) {
+ float bm = median(b[0], b[1], b[2]);
+ return (
+ // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
+ fabsf(am-.5f) > fabsf(bm-.5f) && (
+ // Check points where each pair of color channels meets.
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[1]-a[0], b[1]-b[0]) ||
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[2]-a[1], b[2]-b[1]) ||
+ hasLinearArtifactInner(artifactClassifier, am, bm, a, b, a[0]-a[2], b[0]-b[2])
+ )
+ );
+}
+
+/// Checks if a bilinear interpolation artifact will occur inbetween two diagonally adjacent texels a, d (with b, c forming the other diagonal).
+template
+static bool hasDiagonalArtifact(const ArtifactClassifier &artifactClassifier, float am, const float *a, const float *b, const float *c, const float *d) {
+ float dm = median(d[0], d[1], d[2]);
+ // Out of the pair, only report artifacts for the texel further from the edge to minimize side effects.
+ if (fabsf(am-.5f) > fabsf(dm-.5f)) {
+ float abc[3] = {
+ a[0]-b[0]-c[0],
+ a[1]-b[1]-c[1],
+ a[2]-b[2]-c[2]
+ };
+ // Compute the linear terms for bilinear interpolation.
+ float l[3] = {
+ -a[0]-abc[0],
+ -a[1]-abc[1],
+ -a[2]-abc[2]
+ };
+ // Compute the quadratic terms for bilinear interpolation.
+ float q[3] = {
+ d[0]+abc[0],
+ d[1]+abc[1],
+ d[2]+abc[2]
+ };
+ // Compute interpolation ratios tEx (0 < tEx[i] < 1) for the local extremes of each color channel (the derivative 2*q[i]*tEx[i]+l[i] == 0).
+ double tEx[3] = {
+ -.5*l[0]/q[0],
+ -.5*l[1]/q[1],
+ -.5*l[2]/q[2]
+ };
+ // Check points where each pair of color channels meets.
+ return (
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[1]-a[0], b[1]-b[0]+c[1]-c[0], d[1]-d[0], tEx[0], tEx[1]) ||
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[2]-a[1], b[2]-b[1]+c[2]-c[1], d[2]-d[1], tEx[1], tEx[2]) ||
+ hasDiagonalArtifactInner(artifactClassifier, am, dm, a, l, q, a[0]-a[2], b[0]-b[2]+c[0]-c[2], d[0]-d[2], tEx[2], tEx[0])
+ );
+ }
+ return false;
+}
+
+template
+void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf) {
+ // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
+ double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+ double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+ double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+ // Inspect all texels.
+ for (int y = 0; y < sdf.height; ++y) {
+ for (int x = 0; x < sdf.width; ++x) {
+ const float *c = sdf(x, y);
+ float cm = median(c[0], c[1], c[2]);
+ bool protectedFlag = (*stencil(x, y)&PROTECTED) != 0;
+ const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
+ // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
+ *stencil(x, y) |= (byte) (ERROR*(
+ (x > 0 && ((l = sdf(x-1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, l))) ||
+ (y > 0 && ((b = sdf(x, y-1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, b))) ||
+ (x < sdf.width-1 && ((r = sdf(x+1, y)), hasLinearArtifact(BaseArtifactClassifier(hSpan, protectedFlag), cm, c, r))) ||
+ (y < sdf.height-1 && ((t = sdf(x, y+1)), hasLinearArtifact(BaseArtifactClassifier(vSpan, protectedFlag), cm, c, t))) ||
+ (x > 0 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, b, sdf(x-1, y-1))) ||
+ (x < sdf.width-1 && y > 0 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, b, sdf(x+1, y-1))) ||
+ (x > 0 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, l, t, sdf(x-1, y+1))) ||
+ (x < sdf.width-1 && y < sdf.height-1 && hasDiagonalArtifact(BaseArtifactClassifier(dSpan, protectedFlag), cm, c, r, t, sdf(x+1, y+1)))
+ ));
+ }
+ }
+}
+
+template class ContourCombiner, int N>
+void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf, const Shape &shape) {
+ // Compute the expected deltas between values of horizontally, vertically, and diagonally adjacent texels.
+ double hSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange, 0)).length();
+ double vSpan = minDeviationRatio*projection.unprojectVector(Vector2(0, invRange)).length();
+ double dSpan = minDeviationRatio*projection.unprojectVector(Vector2(invRange)).length();
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp parallel
+#endif
+ {
+ ShapeDistanceChecker shapeDistanceChecker(sdf, shape, projection, invRange, minImproveRatio);
+ bool rightToLeft = false;
+ // Inspect all texels.
+#ifdef MSDFGEN_USE_OPENMP
+ #pragma omp for
+#endif
+ for (int y = 0; y < sdf.height; ++y) {
+ int row = shape.inverseYAxis ? sdf.height-y-1 : y;
+ for (int col = 0; col < sdf.width; ++col) {
+ int x = rightToLeft ? sdf.width-col-1 : col;
+ if ((*stencil(x, row)&ERROR))
+ continue;
+ const float *c = sdf(x, row);
+ shapeDistanceChecker.shapeCoord = projection.unproject(Point2(x+.5, y+.5));
+ shapeDistanceChecker.sdfCoord = Point2(x+.5, row+.5);
+ shapeDistanceChecker.msd = c;
+ shapeDistanceChecker.protectedFlag = (*stencil(x, row)&PROTECTED) != 0;
+ float cm = median(c[0], c[1], c[2]);
+ const float *l = NULL, *b = NULL, *r = NULL, *t = NULL;
+ // Mark current texel c with the error flag if an artifact occurs when it's interpolated with any of its 8 neighbors.
+ *stencil(x, row) |= (byte) (ERROR*(
+ (x > 0 && ((l = sdf(x-1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(-1, 0), hSpan), cm, c, l))) ||
+ (row > 0 && ((b = sdf(x, row-1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, -1), vSpan), cm, c, b))) ||
+ (x < sdf.width-1 && ((r = sdf(x+1, row)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(+1, 0), hSpan), cm, c, r))) ||
+ (row < sdf.height-1 && ((t = sdf(x, row+1)), hasLinearArtifact(shapeDistanceChecker.classifier(Vector2(0, +1), vSpan), cm, c, t))) ||
+ (x > 0 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, -1), dSpan), cm, c, l, b, sdf(x-1, row-1))) ||
+ (x < sdf.width-1 && row > 0 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, -1), dSpan), cm, c, r, b, sdf(x+1, row-1))) ||
+ (x > 0 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(-1, +1), dSpan), cm, c, l, t, sdf(x-1, row+1))) ||
+ (x < sdf.width-1 && row < sdf.height-1 && hasDiagonalArtifact(shapeDistanceChecker.classifier(Vector2(+1, +1), dSpan), cm, c, r, t, sdf(x+1, row+1)))
+ ));
+ }
+ }
+ }
+}
+
+template
+void MSDFErrorCorrection::apply(const BitmapRef &sdf) const {
+ int texelCount = sdf.width*sdf.height;
+ const byte *mask = stencil.pixels;
+ float *texel = sdf.pixels;
+ for (int i = 0; i < texelCount; ++i) {
+ if (*mask&ERROR) {
+ // Set all color channels to the median.
+ float m = median(texel[0], texel[1], texel[2]);
+ texel[0] = m, texel[1] = m, texel[2] = m;
+ }
+ ++mask;
+ texel += N;
+ }
+}
+
+BitmapConstRef MSDFErrorCorrection::getStencil() const {
+ return stencil;
+}
+
+template void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf);
+template void MSDFErrorCorrection::protectEdges(const BitmapConstRef &sdf);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf, const Shape &shape);
+template void MSDFErrorCorrection::findErrors(const BitmapConstRef &sdf, const Shape &shape);
+template void MSDFErrorCorrection::apply(const BitmapRef &sdf) const;
+template void MSDFErrorCorrection::apply(const BitmapRef &sdf) const;
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.h b/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.h
new file mode 100644
index 00000000..c2e92fbc
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/MSDFErrorCorrection.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include "Projection.h"
+#include "Shape.h"
+#include "BitmapRef.hpp"
+
+namespace msdfgen {
+
+/// Performs error correction on a computed MSDF to eliminate interpolation artifacts. This is a low-level class, you may want to use the API in msdf-error-correction.h instead.
+class MSDFErrorCorrection {
+
+public:
+ /// Stencil flags.
+ enum Flags {
+ /// Texel marked as potentially causing interpolation errors.
+ ERROR = 1,
+ /// Texel marked as protected. Protected texels are only given the error flag if they cause inversion artifacts.
+ PROTECTED = 2
+ };
+
+ MSDFErrorCorrection();
+ explicit MSDFErrorCorrection(const BitmapRef &stencil, const Projection &projection, double range);
+ /// Sets the minimum ratio between the actual and maximum expected distance delta to be considered an error.
+ void setMinDeviationRatio(double minDeviationRatio);
+ /// Sets the minimum ratio between the pre-correction distance error and the post-correction distance error.
+ void setMinImproveRatio(double minImproveRatio);
+ /// Flags all texels that are interpolated at corners as protected.
+ void protectCorners(const Shape &shape);
+ /// Flags all texels that contribute to edges as protected.
+ template
+ void protectEdges(const BitmapConstRef &sdf);
+ /// Flags all texels as protected.
+ void protectAll();
+ /// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF only.
+ template
+ void findErrors(const BitmapConstRef &sdf);
+ /// Flags texels that are expected to cause interpolation artifacts based on analysis of the SDF and comparison with the exact shape distance.
+ template class ContourCombiner, int N>
+ void findErrors(const BitmapConstRef &sdf, const Shape &shape);
+ /// Modifies the MSDF so that all texels with the error flag are converted to single-channel.
+ template
+ void apply(const BitmapRef &sdf) const;
+ /// Returns the stencil in its current state (see Flags).
+ BitmapConstRef getStencil() const;
+
+private:
+ BitmapRef stencil;
+ Projection projection;
+ double invRange;
+ double minDeviationRatio;
+ double minImproveRatio;
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Projection.cpp b/Nuake/src/Vendors/msdfgen/core/Projection.cpp
new file mode 100644
index 00000000..fa2f5e25
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Projection.cpp
@@ -0,0 +1,42 @@
+
+#include "Projection.h"
+
+namespace msdfgen {
+
+Projection::Projection() : scale(1), translate(0) { }
+
+Projection::Projection(const Vector2 &scale, const Vector2 &translate) : scale(scale), translate(translate) { }
+
+Point2 Projection::project(const Point2 &coord) const {
+ return scale*(coord+translate);
+}
+
+Point2 Projection::unproject(const Point2 &coord) const {
+ return coord/scale-translate;
+}
+
+Vector2 Projection::projectVector(const Vector2 &vector) const {
+ return scale*vector;
+}
+
+Vector2 Projection::unprojectVector(const Vector2 &vector) const {
+ return vector/scale;
+}
+
+double Projection::projectX(double x) const {
+ return scale.x*(x+translate.x);
+}
+
+double Projection::projectY(double y) const {
+ return scale.y*(y+translate.y);
+}
+
+double Projection::unprojectX(double x) const {
+ return x/scale.x-translate.x;
+}
+
+double Projection::unprojectY(double y) const {
+ return y/scale.y-translate.y;
+}
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Projection.h b/Nuake/src/Vendors/msdfgen/core/Projection.h
new file mode 100644
index 00000000..7cdb1c30
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Projection.h
@@ -0,0 +1,37 @@
+
+#pragma once
+
+#include "Vector2.h"
+
+namespace msdfgen {
+
+/// A transformation from shape coordinates to pixel coordinates.
+class Projection {
+
+public:
+ Projection();
+ Projection(const Vector2 &scale, const Vector2 &translate);
+ /// Converts the shape coordinate to pixel coordinate.
+ Point2 project(const Point2 &coord) const;
+ /// Converts the pixel coordinate to shape coordinate.
+ Point2 unproject(const Point2 &coord) const;
+ /// Converts the vector to pixel coordinate space.
+ Vector2 projectVector(const Vector2 &vector) const;
+ /// Converts the vector from pixel coordinate space.
+ Vector2 unprojectVector(const Vector2 &vector) const;
+ /// Converts the X-coordinate from shape to pixel coordinate space.
+ double projectX(double x) const;
+ /// Converts the Y-coordinate from shape to pixel coordinate space.
+ double projectY(double y) const;
+ /// Converts the X-coordinate from pixel to shape coordinate space.
+ double unprojectX(double x) const;
+ /// Converts the Y-coordinate from pixel to shape coordinate space.
+ double unprojectY(double y) const;
+
+private:
+ Vector2 scale;
+ Vector2 translate;
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Scanline.cpp b/Nuake/src/Vendors/msdfgen/core/Scanline.cpp
new file mode 100644
index 00000000..8e5352db
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Scanline.cpp
@@ -0,0 +1,125 @@
+
+#include "Scanline.h"
+
+#include
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+static int compareIntersections(const void *a, const void *b) {
+ return sign(reinterpret_cast(a)->x-reinterpret_cast(b)->x);
+}
+
+bool interpretFillRule(int intersections, FillRule fillRule) {
+ switch (fillRule) {
+ case FILL_NONZERO:
+ return intersections != 0;
+ case FILL_ODD:
+ return intersections&1;
+ case FILL_POSITIVE:
+ return intersections > 0;
+ case FILL_NEGATIVE:
+ return intersections < 0;
+ }
+ return false;
+}
+
+double Scanline::overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule) {
+ double total = 0;
+ bool aInside = false, bInside = false;
+ int ai = 0, bi = 0;
+ double ax = !a.intersections.empty() ? a.intersections[ai].x : xTo;
+ double bx = !b.intersections.empty() ? b.intersections[bi].x : xTo;
+ while (ax < xFrom || bx < xFrom) {
+ double xNext = min(ax, bx);
+ if (ax == xNext && ai < (int) a.intersections.size()) {
+ aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
+ ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
+ }
+ if (bx == xNext && bi < (int) b.intersections.size()) {
+ bInside = interpretFillRule(b.intersections[bi].direction, fillRule);
+ bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
+ }
+ }
+ double x = xFrom;
+ while (ax < xTo || bx < xTo) {
+ double xNext = min(ax, bx);
+ if (aInside == bInside)
+ total += xNext-x;
+ if (ax == xNext && ai < (int) a.intersections.size()) {
+ aInside = interpretFillRule(a.intersections[ai].direction, fillRule);
+ ax = ++ai < (int) a.intersections.size() ? a.intersections[ai].x : xTo;
+ }
+ if (bx == xNext && bi < (int) b.intersections.size()) {
+ bInside = interpretFillRule(b.intersections[bi].direction, fillRule);
+ bx = ++bi < (int) b.intersections.size() ? b.intersections[bi].x : xTo;
+ }
+ x = xNext;
+ }
+ if (aInside == bInside)
+ total += xTo-x;
+ return total;
+}
+
+Scanline::Scanline() : lastIndex(0) { }
+
+void Scanline::preprocess() {
+ lastIndex = 0;
+ if (!intersections.empty()) {
+ qsort(&intersections[0], intersections.size(), sizeof(Intersection), compareIntersections);
+ int totalDirection = 0;
+ for (std::vector::iterator intersection = intersections.begin(); intersection != intersections.end(); ++intersection) {
+ totalDirection += intersection->direction;
+ intersection->direction = totalDirection;
+ }
+ }
+}
+
+void Scanline::setIntersections(const std::vector &intersections) {
+ this->intersections = intersections;
+ preprocess();
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Scanline::setIntersections(std::vector &&intersections) {
+ this->intersections = (std::vector &&) intersections;
+ preprocess();
+}
+#endif
+
+int Scanline::moveTo(double x) const {
+ if (intersections.empty())
+ return -1;
+ int index = lastIndex;
+ if (x < intersections[index].x) {
+ do {
+ if (index == 0) {
+ lastIndex = 0;
+ return -1;
+ }
+ --index;
+ } while (x < intersections[index].x);
+ } else {
+ while (index < (int) intersections.size()-1 && x >= intersections[index+1].x)
+ ++index;
+ }
+ lastIndex = index;
+ return index;
+}
+
+int Scanline::countIntersections(double x) const {
+ return moveTo(x)+1;
+}
+
+int Scanline::sumIntersections(double x) const {
+ int index = moveTo(x);
+ if (index >= 0)
+ return intersections[index].direction;
+ return 0;
+}
+
+bool Scanline::filled(double x, FillRule fillRule) const {
+ return interpretFillRule(sumIntersections(x), fillRule);
+}
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Scanline.h b/Nuake/src/Vendors/msdfgen/core/Scanline.h
new file mode 100644
index 00000000..9c8f3404
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Scanline.h
@@ -0,0 +1,55 @@
+
+#pragma once
+
+#include
+
+namespace msdfgen {
+
+/// Fill rule dictates how intersection total is interpreted during rasterization.
+enum FillRule {
+ FILL_NONZERO,
+ FILL_ODD, // "even-odd"
+ FILL_POSITIVE,
+ FILL_NEGATIVE
+};
+
+/// Resolves the number of intersection into a binary fill value based on fill rule.
+bool interpretFillRule(int intersections, FillRule fillRule);
+
+/// Represents a horizontal scanline intersecting a shape.
+class Scanline {
+
+public:
+ /// An intersection with the scanline.
+ struct Intersection {
+ /// X coordinate.
+ double x;
+ /// Normalized Y direction of the oriented edge at the point of intersection.
+ int direction;
+ };
+
+ static double overlap(const Scanline &a, const Scanline &b, double xFrom, double xTo, FillRule fillRule);
+
+ Scanline();
+ /// Populates the intersection list.
+ void setIntersections(const std::vector &intersections);
+#ifdef MSDFGEN_USE_CPP11
+ void setIntersections(std::vector &&intersections);
+#endif
+ /// Returns the number of intersections left of x.
+ int countIntersections(double x) const;
+ /// Returns the total sign of intersections left of x.
+ int sumIntersections(double x) const;
+ /// Decides whether the scanline is filled at x based on fill rule.
+ bool filled(double x, FillRule fillRule) const;
+
+private:
+ std::vector intersections;
+ mutable int lastIndex;
+
+ void preprocess();
+ int moveTo(double x) const;
+
+};
+
+}
diff --git a/Nuake/src/Vendors/msdfgen/core/Shape.cpp b/Nuake/src/Vendors/msdfgen/core/Shape.cpp
new file mode 100644
index 00000000..8d6f47c8
--- /dev/null
+++ b/Nuake/src/Vendors/msdfgen/core/Shape.cpp
@@ -0,0 +1,183 @@
+
+#include "Shape.h"
+
+#include
+#include "arithmetics.hpp"
+
+namespace msdfgen {
+
+Shape::Shape() : inverseYAxis(false) { }
+
+void Shape::addContour(const Contour &contour) {
+ contours.push_back(contour);
+}
+
+#ifdef MSDFGEN_USE_CPP11
+void Shape::addContour(Contour &&contour) {
+ contours.push_back((Contour &&) contour);
+}
+#endif
+
+Contour & Shape::addContour() {
+ contours.resize(contours.size()+1);
+ return contours.back();
+}
+
+bool Shape::validate() const {
+ for (std::vector::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
+ if (!contour->edges.empty()) {
+ Point2 corner = contour->edges.back()->point(1);
+ for (std::vector::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ if (!*edge)
+ return false;
+ if ((*edge)->point(0) != corner)
+ return false;
+ corner = (*edge)->point(1);
+ }
+ }
+ }
+ return true;
+}
+
+static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
+ {
+ const QuadraticSegment *quadraticSegment = dynamic_cast(&*edgeHolder);
+ if (quadraticSegment)
+ edgeHolder = quadraticSegment->convertToCubic();
+ }
+ {
+ CubicSegment *cubicSegment = dynamic_cast(&*edgeHolder);
+ if (cubicSegment)
+ cubicSegment->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
+ }
+}
+
+void Shape::normalize() {
+ for (std::vector::iterator contour = contours.begin(); contour != contours.end(); ++contour) {
+ if (contour->edges.size() == 1) {
+ EdgeSegment *parts[3] = { };
+ contour->edges[0]->splitInThirds(parts[0], parts[1], parts[2]);
+ contour->edges.clear();
+ contour->edges.push_back(EdgeHolder(parts[0]));
+ contour->edges.push_back(EdgeHolder(parts[1]));
+ contour->edges.push_back(EdgeHolder(parts[2]));
+ } else {
+ EdgeHolder *prevEdge = &contour->edges.back();
+ for (std::vector::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
+ Vector2 prevDir = (*prevEdge)->direction(1).normalize();
+ Vector2 curDir = (*edge)->direction(0).normalize();
+ if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
+ deconvergeEdge(*prevEdge, 1);
+ deconvergeEdge(*edge, 0);
+ }
+ prevEdge = &*edge;
+ }
+ }
+ }
+}
+
+void Shape::bound(double &l, double &b, double &r, double &t) const {
+ for (std::vector]