WIP M(T)SDF fonts

mtsdf
jackun 3 years ago
parent 365c4bef97
commit fd7404d9cd
No known key found for this signature in database
GPG Key ID: 119DB3F1D05A9ED3

@ -249,6 +249,8 @@ dearimgui_sp = subproject('imgui', default_options: [
'allegro5=disabled',
])
dearimgui_dep = dearimgui_sp.get_variable('imgui_dep')
artery_font_sp = subproject('artery-font-format', required: true)
artery_font_dep = artery_font_sp.get_variable('artery_font_dep')
spdlog_dep = cpp.find_library('spdlog', required: get_option('use_system_spdlog'))
if not spdlog_dep.found()

@ -1,10 +1,137 @@
#include <spdlog/spdlog.h>
#include <artery-font/std-artery-font.h>
#include <artery-font/stdio-serialization.h>
#include <artery-font/structures.h>
#include <imgui.h>
#include <imgui_internal.h>
#include "overlay.h"
#include "file_utils.h"
#include "string_utils.h"
#include "font_default.h"
#include "IconsForkAwesome.h"
#include "forkawesome.h"
// Generate font in binary format arfont with https://github.com/Chlumsky/msdf-atlas-gen
// ./bin/msdf-atlas-gen -type mtsdf -font /usr/share/fonts/TTF/FiraSans-Regular.ttf -format bin -size 16 -pots -charset charset_ascii.txt -arfont ascii.arfont
void create_font_from_mtsdf(const overlay_params& params, ImFont*& small_font, ImFont*& text_font)
{
auto& io = ImGui::GetIO();
io.Fonts->Clear();
ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct
artery_font::StdArteryFont<float> arfont {};
artery_font::readFile(arfont, params.font_file.c_str());
SPDLOG_DEBUG("arfont: size: {}x{} {} {}",
arfont.images[0].width, arfont.images[0].height,
arfont.images[0].imageType,
arfont.images[0].encoding);
if (arfont.images.length() < 1)
return;
if (arfont.images[0].imageType != artery_font::IMAGE_MTSDF)
{
SPDLOG_ERROR("Font type is not MTSDF");
return;
}
if (arfont.images[0].encoding != artery_font::ImageEncoding::IMAGE_RAW_BINARY)
{
SPDLOG_ERROR("Font image encoding is not raw binary");
return;
}
const auto& v = arfont.variants[0];
const auto& img = arfont.images[0];
io.Fonts->ClearInputData();
ImFontAtlasBuildInit(io.Fonts);
// Clear atlas
io.Fonts->TexID = (ImTextureID)NULL;
io.Fonts->TexWidth = img.width;
io.Fonts->TexHeight = img.height;
io.Fonts->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
io.Fonts->TexHeight += IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1 + 32; // give space for custom stuff
io.Fonts->TexHeight = (io.Fonts->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (io.Fonts->TexHeight + 1) : ImUpperPowerOfTwo(io.Fonts->TexHeight);
if (io.Fonts->TexWidth < 256)
io.Fonts->TexWidth = 256; // give space for custom stuff
io.Fonts->TexUvScale = ImVec2(1.0f / io.Fonts->TexWidth, 1.0f / io.Fonts->TexHeight);
ImFontConfig font_cfg;
io.Fonts->Fonts.push_back(IM_NEW(ImFont));
ImFont* font = io.Fonts->Fonts.back();
font->Ascent = v.metrics.ascender * v.metrics.fontSize;
font->Descent = v.metrics.descender * v.metrics.fontSize;
font->FontSize = v.metrics.fontSize; // * v.metrics.lineHeight;
font->Scale = 1.0f;
font->ContainerAtlas = io.Fonts;
font_cfg.DstFont = font;
strncpy(font_cfg.Name, (const char *)v.name, min(40, v.name.length()));
io.Fonts->ConfigData.push_back(font_cfg);
font->ConfigData = &io.Fonts->ConfigData.back();
font->ConfigDataCount = 1;
io.Fonts->ClearTexData(); // invalidate texture data
for (const auto& g : v.glyphs.vector)
{
float u0 = g.imageBounds.l * io.Fonts->TexUvScale.x;
float v0 = (img.height - g.imageBounds.t) * io.Fonts->TexUvScale.y;
float u1 = g.imageBounds.r * io.Fonts->TexUvScale.x;
float v1 = (img.height - g.imageBounds.b) * io.Fonts->TexUvScale.y;
font->AddGlyph(nullptr, (ImWchar)g.codepoint,
g.planeBounds.l * v.metrics.fontSize,
(1.0f - g.planeBounds.t) * v.metrics.fontSize,
g.planeBounds.r * v.metrics.fontSize,
(1.0f - g.planeBounds.b) * v.metrics.fontSize,
u0, v0, u1, v1,
g.advance.h * v.metrics.fontSize);
}
// Allocate texture
io.Fonts->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(io.Fonts->TexWidth * io.Fonts->TexHeight * 4);
unsigned char* src = (unsigned char*)arfont.images[0].data;
memset(io.Fonts->TexPixelsRGBA32, 0, io.Fonts->TexHeight * io.Fonts->TexWidth * 4);
for (size_t y = 0; y < img.height; y++) //TODO check orientation
memcpy(io.Fonts->TexPixelsRGBA32 + (img.height - y - 1) * io.Fonts->TexWidth, src + y * img.width * 4, img.width * 4);
// Add custom rects somewhere on texture
uint16_t max_h = 0;
uint16_t curr_x = 0;
uint16_t curr_y = img.height;
for (auto& r : io.Fonts->CustomRects)
{
if (curr_x + r.Width > io.Fonts->TexWidth)
{
curr_x = 0;
curr_y += max_h;
max_h = 0;
}
r.X = curr_x;
r.Y = curr_y;
curr_x += r.Width;
max_h = max(max_h, r.Height);
}
ImFontAtlasBuildFinish(io.Fonts);
io.Fonts->Fonts.push_back(IM_NEW(ImFont));
ImFont* tmp = io.Fonts->Fonts.back();
*tmp = *io.Fonts->Fonts[0];
tmp->Scale *= 0.55f;
small_font = io.Fonts->Fonts.back();
text_font = io.Fonts->Fonts[0];
}
void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font)
{
if (ends_with(params.font_file, ".arfont") && file_exists(params.font_file))
{
create_font_from_mtsdf(params, small_font, text_font);
return;
}
auto& io = ImGui::GetIO();
io.Fonts->Clear();
ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct
@ -27,7 +154,10 @@ void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& te
// Load Icon file and merge to exisitng font
ImFontConfig config;
config.MergeMode = true;
static const ImWchar icon_ranges[] = { ICON_MIN_FK, ICON_MAX_FK, 0 };
static const ImWchar icon_ranges[] = {
0xf240, 0xf244, // battery icons
0,
};
ImVector<ImWchar> glyph_ranges;
ImFontGlyphRangesBuilder builder;

@ -189,6 +189,7 @@ vklayer_mesa_overlay = shared_library(
dependencies : [
vulkan_wsi_deps,
dearimgui_dep,
artery_font_dep,
spdlog_dep,
dep_libdrm,
dep_libdrm_amdgpu,

@ -471,6 +471,21 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
if((now - logger->last_log_end()) < 12s)
render_benchmark(data, params, window_size, height, now);
}
// ImGui::SetNextWindowBgAlpha(0.5f);
// auto dsz = ImGui::GetIO().DisplaySize;
// //auto atlas = &font_atlas_text;
// auto atlas = ImGui::GetIO().Fonts->Fonts[0]->ContainerAtlas;
// float scale = 1; //atlas->TexHeight/512.f;
// auto img_pos = ImVec2(dsz.x - atlas->TexWidth/scale - 25.f, dsz.y - atlas->TexHeight - 25.f);
// auto img_size = ImVec2(atlas->TexWidth/scale + 20.f, atlas->TexHeight/scale + 20.f);
// ImGui::SetNextWindowPos(img_pos, ImGuiCond_Always);
// ImGui::SetNextWindowSize(img_size, ImGuiCond_Always);
//
// ImGui::Begin("Font Atlas", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoFocusOnAppearing);
// // ImGui::GetWindowDrawList()->AddCallback(nullptr, &render_mode[0]);
// ImGui::Image(atlas->TexID, ImVec2(atlas->TexWidth/scale, atlas->TexHeight/scale));
// ImGui::End();
}
void init_cpu_stats(overlay_params& params)

@ -1,4 +1,5 @@
#version 450 core
layout(location = 0) out vec4 fColor;
layout(set=0, binding=0) uniform sampler2D sTexture;
@ -8,7 +9,28 @@ layout(location = 0) in struct{
vec2 UV;
} In;
vec4 bgColor = vec4(0,0,0,0.0f);
float sdf_aastep(float value) {
float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
return smoothstep(0.5 - afwidth, 0.5 + afwidth, value);
}
float screenPxRange() {
return 2.f;
}
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main()
{
fColor = In.Color * vec4(1, 1, 1, texture(sTexture, In.UV.st).r);
vec4 msd = texture(sTexture, In.UV.st);
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPxRange()*(sd - 0.5);
float opacity = clamp(screenPxDistance/fwidth(screenPxDistance) + 0.5, 0.0, 1.0);
fColor = mix(bgColor, In.Color, opacity);
// fColor = In.Color * texture(sTexture, In.UV.st);
}

@ -714,16 +714,16 @@ static void check_fonts(struct swapchain_data* data)
create_fonts(instance_data->params, data->sw_stats.font1, data->sw_stats.font_text);
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// wait for rendering to complete, if any
device_data->vtable.DeviceWaitIdle(device_data->device);
shutdown_swapchain_font(data);
if (desc_set)
create_image(data, desc_set, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view);
create_image(data, desc_set, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image, data->font_mem, data->font_image_view);
else
desc_set = create_image_with_desc(data, width, height, VK_FORMAT_R8_UNORM, data->font_image, data->font_mem, data->font_image_view);
desc_set = create_image_with_desc(data, width, height, VK_FORMAT_R8G8B8A8_UNORM, data->font_image, data->font_mem, data->font_image_view);
io.Fonts->SetTexID((ImTextureID)desc_set);
data->font_uploaded = false;
@ -746,8 +746,8 @@ static void ensure_swapchain_fonts(struct swapchain_data *data,
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
size_t upload_size = width * height * 1 * sizeof(char);
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
size_t upload_size = width * height * 4 * sizeof(char);
upload_image_data(device_data, command_buffer, pixels, upload_size, width, height, data->upload_font_buffer, data->upload_font_buffer_mem, data->font_image);
}

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

@ -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 &ndash; 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.

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

@ -0,0 +1,13 @@
#pragma once
#include "types.h"
namespace artery_font {
uint32 crc32Init();
uint32 crc32Update(uint32 crc, byte x);
}
#include "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;
}
}

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

@ -0,0 +1,18 @@
#pragma once
#include "types.h"
#include "enums.h"
#include "structures.h"
namespace artery_font {
template <int (*READ)(void *, int, void *), typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool decode(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, void *userData);
template <int (*WRITE)(const void *, int, void *), typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool encode(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, void *userData);
}
#include "serialization.hpp"

@ -0,0 +1,394 @@
#include "serialization.h"
#include <cstring>
#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 <typename REAL>
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 <typename REAL>
uint32 realTypeCode();
template <>
uint32 realTypeCode<float>() {
return 0x14u;
}
template <>
uint32 realTypeCode<double>() {
return 0x18u;
}
inline uint32 paddedLength(uint32 len) {
if (len&0x03u)
len += 0x04u-(len&0x03u);
return len;
}
template <class STRING>
uint32 paddedStringLength(const STRING &str) {
uint32 len = str.length();
return paddedLength(len+(len > 0));
}
}
#ifndef __BIG_ENDIAN__
template <int (*READ)(void *, int, void *), typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool decode(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &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<char> 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<REAL>())
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<FontVariant<REAL, LIST, STRING> >(header.variantCount);
font.images = LIST<Image<BYTE_ARRAY, STRING> >(header.imageCount);
font.appendices = LIST<Appendix<BYTE_ARRAY, STRING> >(header.appendixCount);
variantsLength = header.variantsLength;
imagesLength = header.imagesLength;
appendicesLength = header.appendicesLength;
}
prevLength = totalLength;
// Read variants
for (int i = 0; i < variantCount; ++i) {
FontVariant<REAL, LIST, STRING> &variant = font.variants[i];
internal::FontVariantHeader<REAL> 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<Glyph<REAL> >(header.glyphCount);
variant.kernPairs = LIST<KernPair<REAL> >(header.kernPairCount);
ARTERY_FONT_DECODE_READ((Glyph<REAL> *) variant.glyphs, header.glyphCount*sizeof(Glyph<REAL>));
ARTERY_FONT_DECODE_READ((KernPair<REAL> *) variant.kernPairs, header.kernPairCount*sizeof(KernPair<REAL>));
}
if (totalLength-prevLength != variantsLength)
return false;
prevLength = totalLength;
// Read images
for (int i = 0; i < imageCount; ++i) {
Image<BYTE_ARRAY, STRING> &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<BYTE_ARRAY, STRING> &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 <int (*WRITE)(const void *, int, void *), typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool encode(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &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<REAL>();
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<REAL, LIST, STRING> &variant = font.variants[i];
header.variantsLength += sizeof(internal::FontVariantHeader<REAL>);
header.variantsLength += internal::paddedStringLength(variant.name);
header.variantsLength += internal::paddedStringLength(variant.metadata);
header.variantsLength += variant.glyphs.length()*sizeof(Glyph<REAL>);
header.variantsLength += variant.kernPairs.length()*sizeof(KernPair<REAL>);
}
for (int i = 0; i < imageCount; ++i) {
const Image<BYTE_ARRAY, STRING> &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<BYTE_ARRAY, STRING> &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<REAL, LIST, STRING> &variant = font.variants[i];
internal::FontVariantHeader<REAL> 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<REAL> *) variant.glyphs, header.glyphCount*sizeof(Glyph<REAL>));
ARTERY_FONT_ENCODE_WRITE((const KernPair<REAL> *) variant.kernPairs, header.kernPairCount*sizeof(KernPair<REAL>));
}
// Write images
for (int i = 0; i < imageCount; ++i) {
const Image<BYTE_ARRAY, STRING> &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<BYTE_ARRAY, STRING> &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
}

@ -0,0 +1,47 @@
#pragma once
#include <vector>
#include <string>
#include "artery-font.h"
namespace artery_font {
template <typename T>
class StdList {
public:
std::vector<T> 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<unsigned char> StdByteArray;
template <typename REAL>
using StdArteryFont = ArteryFont<REAL, StdList, StdByteArray, StdString>;
template <typename REAL>
using StdFontVariant = FontVariant<REAL, StdList, StdString>;
using StdImage = Image<StdByteArray, StdString>;
using StdAppendix = Appendix<StdByteArray, StdString>;
}

@ -0,0 +1,23 @@
#pragma once
#include <cstdio>
#include "serialization.h"
namespace artery_font {
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool read(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, FILE *file);
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool write(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, FILE *file);
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool readFile(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, const char *filename);
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool writeFile(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, const char *filename);
}
#include "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 *>(file));
}
inline int fileWrite(const void *buffer, int length, void *file) {
return fwrite(buffer, 1, length, reinterpret_cast<FILE *>(file));
}
}
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool read(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, FILE *file) {
return decode<internal::fileRead>(font, file);
}
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool write(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, FILE *file) {
return encode<internal::fileWrite>(font, file);
}
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool readFile(ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, const char *filename) {
FILE *file = fopen(filename, "rb");
if (!file)
return false;
bool result = read(font, file);
fclose(file);
return result;
}
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
bool writeFile(const ArteryFont<REAL, LIST, BYTE_ARRAY, STRING> &font, const char *filename) {
FILE *file = fopen(filename, "wb");
if (!file)
return false;
bool result = write(font, file);
fclose(file);
return result;
}
}

@ -0,0 +1,87 @@
#pragma once
#include "types.h"
#include "enums.h"
namespace artery_font {
template <typename REAL>
struct Glyph {
uint32 codepoint;
uint32 image;
struct {
REAL l, b, r, t;
} planeBounds, imageBounds;
struct {
REAL h, v;
} advance;
};
template <typename REAL>
struct KernPair {
uint32 codepoint1, codepoint2;
struct {
REAL h, v;
} advance;
};
template <typename REAL, template <typename> 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<Glyph<REAL> > glyphs;
LIST<KernPair<REAL> > kernPairs;
};
template <class BYTE_ARRAY, class STRING>
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 <class BYTE_ARRAY, class STRING>
struct Appendix {
STRING metadata;
BYTE_ARRAY data;
};
template <typename REAL, template <typename> class LIST, class BYTE_ARRAY, class STRING>
struct ArteryFont {
MetadataFormat metadataFormat;
STRING metadata;
LIST<FontVariant<REAL, LIST, STRING> > variants;
LIST<Image<BYTE_ARRAY, STRING> > images;
LIST<Appendix<BYTE_ARRAY, STRING> > appendices;
};
}

@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
namespace artery_font {
typedef unsigned char byte;
typedef int32_t sint32;
typedef uint32_t uint32;
}

@ -0,0 +1,16 @@
project('artery-font', 'cpp',
version: '1.0',
license: 'MIT',
)
include_dirs = include_directories('.')
sources = []
dependencies = []
artery_font = library('artery-font',
sources,
dependencies: dependencies,
include_directories: include_dirs,
)
artery_font_dep = declare_dependency(include_directories: include_dirs)
Loading…
Cancel
Save