MSDF Rewrite #1
|
@ -46,6 +46,7 @@ add_library(Wellspring
|
||||||
#Public header
|
#Public header
|
||||||
include/Wellspring.h
|
include/Wellspring.h
|
||||||
#Source
|
#Source
|
||||||
|
lib/json.h
|
||||||
lib/stb_rect_pack.h
|
lib/stb_rect_pack.h
|
||||||
lib/stb_truetype.h
|
lib/stb_truetype.h
|
||||||
src/Wellspring.c
|
src/Wellspring.c
|
||||||
|
|
18
README.md
18
README.md
|
@ -1,11 +1,21 @@
|
||||||
This is Wellspring, an immediate mode font rendering system in C.
|
This is Wellspring, an immediate mode multiple-channel signed distance field font rendering system in C.
|
||||||
|
|
||||||
About Wellspring
|
About Wellspring
|
||||||
----------------
|
----------------
|
||||||
Wellspring is inspired by the design of Dear ImGui.
|
Wellspring is inspired by the design of Dear ImGui.
|
||||||
It outputs pixel data that you can upload to a texture and vertex buffers that you can render anytime in your 3D application.
|
It outputs buffer data that you can upload and render anytime in your 3D application.
|
||||||
This means that you can integrate it easily using the graphics library of your choice.
|
This means that you can integrate it easily using the graphics library of your choice.
|
||||||
Wellspring uses stb_truetype to rasterize and pack fonts quickly.
|
|
||||||
|
Wellspring uses JSON output from [msdf-atlas-gen](https://github.com/Chlumsky/msdf-atlas-gen) to generate buffers. It also uses [stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) for additional kerning support. At render time, bind the image data from msdf-atlas-gen with buffers generated by Wellspring for beautiful MSDF font rendering.
|
||||||
|
|
||||||
|
Using msdf-atlas-gen
|
||||||
|
--------------
|
||||||
|
A full explanation of msdf-atlas-gen is beyond the scope of this project, but note that Wellspring only accepts MSDF atlas types with JSON output.
|
||||||
|
|
||||||
|
Your atlas generation might look like this:
|
||||||
|
```sh
|
||||||
|
msdf-atlas-gen -yorigin top -font ~/mygame/myfont.otf -imageout ~/mygame/content/forgotten_dream.png -json ~/mygame/content/forgotten_dream.json
|
||||||
|
```
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
------------
|
------------
|
||||||
|
@ -20,7 +30,7 @@ For *nix platforms, use CMake:
|
||||||
$ cmake ../
|
$ cmake ../
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
For Windows, see the 'visualc/' directory.
|
For Windows, you can use cmake-gui to generate a Visual Studio solution or use VSCode with the CMake and C/C++ Tools extensions.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -63,7 +63,6 @@ WELLSPRINGAPI uint32_t Wellspring_LinkedVersion(void);
|
||||||
/* Type definitions */
|
/* Type definitions */
|
||||||
|
|
||||||
typedef struct Wellspring_Font Wellspring_Font;
|
typedef struct Wellspring_Font Wellspring_Font;
|
||||||
typedef struct Wellspring_Packer Wellspring_Packer;
|
|
||||||
typedef struct Wellspring_TextBatch Wellspring_TextBatch;
|
typedef struct Wellspring_TextBatch Wellspring_TextBatch;
|
||||||
|
|
||||||
typedef struct Wellspring_FontRange
|
typedef struct Wellspring_FontRange
|
||||||
|
@ -113,46 +112,25 @@ typedef enum Wellspring_VerticalAlignment
|
||||||
|
|
||||||
WELLSPRINGAPI Wellspring_Font* Wellspring_CreateFont(
|
WELLSPRINGAPI Wellspring_Font* Wellspring_CreateFont(
|
||||||
const uint8_t *fontBytes,
|
const uint8_t *fontBytes,
|
||||||
uint32_t fontBytesLength
|
uint32_t fontBytesLength,
|
||||||
);
|
const uint8_t *atlasJsonBytes,
|
||||||
|
uint32_t atlasJsonBytesLength,
|
||||||
WELLSPRINGAPI Wellspring_Packer* Wellspring_CreatePacker(
|
float *pPixelsPerEm,
|
||||||
Wellspring_Font *font,
|
float *pDistanceRange
|
||||||
float fontSize,
|
|
||||||
uint32_t width,
|
|
||||||
uint32_t height,
|
|
||||||
uint32_t strideInBytes, /* 0 means the buffer is tightly packed. */
|
|
||||||
uint32_t padding /* A sensible value here is 1 to allow bilinear filtering. */
|
|
||||||
);
|
|
||||||
|
|
||||||
WELLSPRINGAPI uint32_t Wellspring_PackFontRanges(
|
|
||||||
Wellspring_Packer *packer,
|
|
||||||
Wellspring_FontRange *ranges,
|
|
||||||
uint32_t numRanges
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Returns a pointer to an array of rasterized pixels of the packed font.
|
|
||||||
* This data must be uploaded to a texture before you render!
|
|
||||||
* The pixel data becomes outdated if you call PackFontRanges.
|
|
||||||
* Length is width * height.
|
|
||||||
*/
|
|
||||||
WELLSPRINGAPI uint8_t* Wellspring_GetPixelDataPointer(
|
|
||||||
Wellspring_Packer *packer
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Batches are not thread-safe, recommend one batch per thread. */
|
/* Batches are not thread-safe, recommend one batch per thread. */
|
||||||
WELLSPRINGAPI Wellspring_TextBatch* Wellspring_CreateTextBatch();
|
WELLSPRINGAPI Wellspring_TextBatch* Wellspring_CreateTextBatch(void);
|
||||||
|
|
||||||
/* Also restarts the batch */
|
/* Also restarts the batch */
|
||||||
WELLSPRINGAPI void Wellspring_StartTextBatch(
|
WELLSPRINGAPI void Wellspring_StartTextBatch(
|
||||||
Wellspring_TextBatch *textBatch,
|
Wellspring_TextBatch *textBatch,
|
||||||
Wellspring_Packer *packer
|
Wellspring_Font *font
|
||||||
);
|
);
|
||||||
|
|
||||||
WELLSPRINGAPI uint8_t Wellspring_TextBounds(
|
WELLSPRINGAPI uint8_t Wellspring_TextBounds(
|
||||||
Wellspring_Packer* packer,
|
Wellspring_Font *font,
|
||||||
float x,
|
int pixelSize,
|
||||||
float y,
|
|
||||||
Wellspring_HorizontalAlignment horizontalAlignment,
|
Wellspring_HorizontalAlignment horizontalAlignment,
|
||||||
Wellspring_VerticalAlignment verticalAlignment,
|
Wellspring_VerticalAlignment verticalAlignment,
|
||||||
const uint8_t *strBytes,
|
const uint8_t *strBytes,
|
||||||
|
@ -160,11 +138,9 @@ WELLSPRINGAPI uint8_t Wellspring_TextBounds(
|
||||||
Wellspring_Rectangle *pRectangle
|
Wellspring_Rectangle *pRectangle
|
||||||
);
|
);
|
||||||
|
|
||||||
WELLSPRINGAPI uint8_t Wellspring_Draw(
|
WELLSPRINGAPI uint8_t Wellspring_AddToTextBatch(
|
||||||
Wellspring_TextBatch *textBatch,
|
Wellspring_TextBatch *textBatch,
|
||||||
float x,
|
int pixelSize,
|
||||||
float y,
|
|
||||||
float depth,
|
|
||||||
Wellspring_Color *color,
|
Wellspring_Color *color,
|
||||||
Wellspring_HorizontalAlignment horizontalAlignment,
|
Wellspring_HorizontalAlignment horizontalAlignment,
|
||||||
Wellspring_VerticalAlignment verticalAlignment,
|
Wellspring_VerticalAlignment verticalAlignment,
|
||||||
|
@ -182,7 +158,6 @@ WELLSPRINGAPI void Wellspring_GetBufferData(
|
||||||
);
|
);
|
||||||
|
|
||||||
WELLSPRINGAPI void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch);
|
WELLSPRINGAPI void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch);
|
||||||
WELLSPRINGAPI void Wellspring_DestroyPacker(Wellspring_Packer *packer);
|
|
||||||
WELLSPRINGAPI void Wellspring_DestroyFont(Wellspring_Font *font);
|
WELLSPRINGAPI void Wellspring_DestroyFont(Wellspring_Font *font);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
605
src/Wellspring.c
605
src/Wellspring.c
|
@ -95,6 +95,9 @@
|
||||||
#define STBRP_SORT Wellspring_sort
|
#define STBRP_SORT Wellspring_sort
|
||||||
#define STBRP_ASSERT Wellspring_assert
|
#define STBRP_ASSERT Wellspring_assert
|
||||||
|
|
||||||
|
#define SHEREDOM_JSON_H_malloc Wellspring_malloc
|
||||||
|
#define SHEREDOM_JSON_H_free Wellspring_free
|
||||||
|
|
||||||
typedef uint8_t stbtt_uint8;
|
typedef uint8_t stbtt_uint8;
|
||||||
typedef int8_t stbtt_int8;
|
typedef int8_t stbtt_int8;
|
||||||
typedef uint16_t stbtt_uint16;
|
typedef uint16_t stbtt_uint16;
|
||||||
|
@ -112,48 +115,54 @@ typedef int32_t stbtt_int32;
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
#include "stb_truetype.h"
|
#include "stb_truetype.h"
|
||||||
|
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
#pragma GCC diagnostic warning "-Wunused-function"
|
#pragma GCC diagnostic warning "-Wunused-function"
|
||||||
|
|
||||||
#define INITIAL_QUAD_CAPACITY 128
|
#define INITIAL_QUAD_CAPACITY 128
|
||||||
|
|
||||||
/* Structs */
|
/* Structs */
|
||||||
|
|
||||||
|
typedef struct PackedChar
|
||||||
|
{
|
||||||
|
float atlasLeft, atlasTop, atlasRight, atlasBottom;
|
||||||
|
float planeLeft, planeTop, planeRight, planeBottom;
|
||||||
|
float xAdvance;
|
||||||
|
} PackedChar;
|
||||||
|
|
||||||
|
typedef struct CharRange
|
||||||
|
{
|
||||||
|
PackedChar *data;
|
||||||
|
uint32_t firstCodepoint;
|
||||||
|
uint32_t charCount;
|
||||||
|
} CharRange;
|
||||||
|
|
||||||
|
typedef struct Packer
|
||||||
|
{
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
|
||||||
|
CharRange *ranges;
|
||||||
|
uint32_t rangeCount;
|
||||||
|
} Packer;
|
||||||
|
|
||||||
typedef struct Font
|
typedef struct Font
|
||||||
{
|
{
|
||||||
uint8_t *fontBytes;
|
uint8_t *fontBytes;
|
||||||
stbtt_fontinfo fontInfo;
|
stbtt_fontinfo fontInfo;
|
||||||
|
|
||||||
int32_t ascent;
|
float ascender;
|
||||||
int32_t descent;
|
float descender;
|
||||||
int32_t lineGap;
|
float lineHeight;
|
||||||
|
float pixelsPerEm;
|
||||||
|
float distanceRange;
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
float kerningScale; // kerning values from stb_tt are in a different scale
|
||||||
|
|
||||||
|
Packer packer;
|
||||||
} Font;
|
} Font;
|
||||||
|
|
||||||
typedef struct CharRange
|
|
||||||
{
|
|
||||||
stbtt_packedchar *data;
|
|
||||||
uint32_t firstCodepoint;
|
|
||||||
uint32_t charCount;
|
|
||||||
float fontSize;
|
|
||||||
} CharRange;
|
|
||||||
|
|
||||||
typedef struct Packer
|
|
||||||
{
|
|
||||||
Font *font;
|
|
||||||
float fontSize;
|
|
||||||
|
|
||||||
stbtt_pack_context *context;
|
|
||||||
uint8_t *pixels;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
uint32_t strideInBytes;
|
|
||||||
uint32_t padding;
|
|
||||||
|
|
||||||
float scale; /* precomputed at init */
|
|
||||||
|
|
||||||
CharRange *ranges;
|
|
||||||
uint32_t rangeCount;
|
|
||||||
} Packer;
|
|
||||||
|
|
||||||
typedef struct Batch
|
typedef struct Batch
|
||||||
{
|
{
|
||||||
Wellspring_Vertex *vertices;
|
Wellspring_Vertex *vertices;
|
||||||
|
@ -164,9 +173,15 @@ typedef struct Batch
|
||||||
uint32_t indexCount;
|
uint32_t indexCount;
|
||||||
uint32_t indexCapacity;
|
uint32_t indexCapacity;
|
||||||
|
|
||||||
Packer *currentPacker;
|
Font *currentFont;
|
||||||
} Batch;
|
} Batch;
|
||||||
|
|
||||||
|
typedef struct Quad
|
||||||
|
{
|
||||||
|
float x0,y0,s0,t0; // top-left
|
||||||
|
float x1,y1,s1,t1; // bottom-right
|
||||||
|
} Quad;
|
||||||
|
|
||||||
/* UTF-8 Decoder */
|
/* UTF-8 Decoder */
|
||||||
|
|
||||||
/* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
/* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
@ -205,6 +220,126 @@ decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
|
||||||
return *state;
|
return *state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* JSON helpers */
|
||||||
|
|
||||||
|
static uint8_t json_object_has_key(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *currentElement = object->start;
|
||||||
|
const char* currentName = currentElement->name->string;
|
||||||
|
|
||||||
|
while (SDL_strcmp(currentName, name) != 0)
|
||||||
|
{
|
||||||
|
if (currentElement->next == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentElement = currentElement->next;
|
||||||
|
currentName = currentElement->name->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_object_element_t* json_object_get_element_by_name(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *currentElement = object->start;
|
||||||
|
const char* currentName = currentElement->name->string;
|
||||||
|
|
||||||
|
while (SDL_strcmp(currentName, name) != 0)
|
||||||
|
{
|
||||||
|
if (currentElement->next == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Key %s not found in JSON!", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentElement = currentElement->next;
|
||||||
|
currentName = currentElement->name->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_object_t* json_object_get_object(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *element = json_object_get_element_by_name(object, name);
|
||||||
|
|
||||||
|
if (element == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_t *obj = json_value_as_object(element->value);
|
||||||
|
|
||||||
|
if (obj == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Value with key %s was not an object!", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* json_object_get_string(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *element = json_object_get_element_by_name(object, name);
|
||||||
|
|
||||||
|
if (element == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_string_t *str = json_value_as_string(element->value);
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Value with key %s was not a string!", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str->string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t json_object_get_uint(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *element = json_object_get_element_by_name(object, name);
|
||||||
|
|
||||||
|
if (element == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_number_t *num = json_value_as_number(element->value);
|
||||||
|
|
||||||
|
if (num == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Value with key %s was not a number!", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint32_t) SDL_strtoul(num->number, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double json_object_get_double(const json_object_t *object, const char* name)
|
||||||
|
{
|
||||||
|
json_object_element_t *element = json_object_get_element_by_name(object, name);
|
||||||
|
|
||||||
|
if (element == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_number_t *num = json_value_as_number(element->value);
|
||||||
|
|
||||||
|
if (num == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Value with key %s was not a string!", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_atof(num->number);
|
||||||
|
}
|
||||||
|
|
||||||
/* API */
|
/* API */
|
||||||
|
|
||||||
uint32_t Wellspring_LinkedVersion(void)
|
uint32_t Wellspring_LinkedVersion(void)
|
||||||
|
@ -214,107 +349,146 @@ uint32_t Wellspring_LinkedVersion(void)
|
||||||
|
|
||||||
Wellspring_Font* Wellspring_CreateFont(
|
Wellspring_Font* Wellspring_CreateFont(
|
||||||
const uint8_t* fontBytes,
|
const uint8_t* fontBytes,
|
||||||
uint32_t fontBytesLength
|
uint32_t fontBytesLength,
|
||||||
|
const uint8_t *atlasJsonBytes,
|
||||||
|
uint32_t atlasJsonBytesLength,
|
||||||
|
float *pPixelsPerEm,
|
||||||
|
float *pDistanceRange
|
||||||
) {
|
) {
|
||||||
Font *font = Wellspring_malloc(sizeof(Font));
|
Font *font = Wellspring_malloc(sizeof(Font));
|
||||||
|
|
||||||
font->fontBytes = Wellspring_malloc(fontBytesLength);
|
font->fontBytes = Wellspring_malloc(fontBytesLength);
|
||||||
Wellspring_memcpy(font->fontBytes, fontBytes, fontBytesLength);
|
Wellspring_memcpy(font->fontBytes, fontBytes, fontBytesLength);
|
||||||
stbtt_InitFont(&font->fontInfo, font->fontBytes, 0);
|
stbtt_InitFont(&font->fontInfo, font->fontBytes, 0);
|
||||||
stbtt_GetFontVMetrics(&font->fontInfo, &font->ascent, &font->descent, &font->lineGap);
|
int stbAscender, stbDescender, stbLineHeight;
|
||||||
|
stbtt_GetFontVMetrics(&font->fontInfo, &stbAscender, &stbDescender, &stbLineHeight);
|
||||||
|
|
||||||
|
json_value_t *jsonRoot = json_parse(atlasJsonBytes, atlasJsonBytesLength);
|
||||||
|
json_object_t *jsonObject = jsonRoot->payload;
|
||||||
|
|
||||||
|
if (jsonObject == NULL)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Atlas JSON is invalid! Bailing!");
|
||||||
|
Wellspring_free(font->fontBytes);
|
||||||
|
Wellspring_free(font);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDL_strcmp(jsonObject->start->name->string, "atlas") != 0)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Atlas JSON is invalid! Bailing!");
|
||||||
|
Wellspring_free(jsonRoot);
|
||||||
|
Wellspring_free(font->fontBytes);
|
||||||
|
Wellspring_free(font);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_t *atlasObject = json_value_as_object(jsonObject->start->value);
|
||||||
|
json_object_t *metricsObject = json_value_as_object(jsonObject->start->next->value);
|
||||||
|
json_array_t *glyphsArray = json_value_as_array(jsonObject->start->next->next->value);
|
||||||
|
|
||||||
|
const char* atlasType = json_object_get_string(atlasObject, "type");
|
||||||
|
|
||||||
|
if (SDL_strcmp(atlasType, "msdf") != 0)
|
||||||
|
{
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Atlas is not MSDF! Bailing!");
|
||||||
|
Wellspring_free(jsonRoot);
|
||||||
|
Wellspring_free(font->fontBytes);
|
||||||
|
Wellspring_free(font);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
font->packer.width = json_object_get_uint(atlasObject, "width");
|
||||||
|
font->packer.height = json_object_get_uint(atlasObject, "height");
|
||||||
|
font->pixelsPerEm = json_object_get_double(atlasObject, "size");
|
||||||
|
font->distanceRange = json_object_get_double(atlasObject, "distanceRange");
|
||||||
|
|
||||||
|
font->ascender = json_object_get_double(metricsObject, "ascender");
|
||||||
|
font->descender = json_object_get_double(metricsObject, "descender");
|
||||||
|
font->lineHeight = json_object_get_double(metricsObject, "lineHeight");
|
||||||
|
|
||||||
|
font->scale = font->pixelsPerEm * 4 / 3; // converting from "points" (dpi) to pixels
|
||||||
|
|
||||||
|
/* Pack unicode ranges */
|
||||||
|
|
||||||
|
font->packer.ranges = Wellspring_malloc(sizeof(CharRange));
|
||||||
|
font->packer.rangeCount = 1;
|
||||||
|
font->packer.ranges[0].data = NULL;
|
||||||
|
font->packer.ranges[0].charCount = 0;
|
||||||
|
|
||||||
|
int32_t charRangeIndex = 0;
|
||||||
|
|
||||||
|
json_array_element_t *currentGlyphElement = glyphsArray->start;
|
||||||
|
while (currentGlyphElement != NULL)
|
||||||
|
{
|
||||||
|
json_object_t *currentGlyphObject = json_value_as_object(currentGlyphElement->value);
|
||||||
|
|
||||||
|
uint32_t codepoint = json_object_get_uint(currentGlyphObject, "unicode");
|
||||||
|
|
||||||
|
if (font->packer.ranges[charRangeIndex].charCount == 0)
|
||||||
|
{
|
||||||
|
// first codepoint on first range
|
||||||
|
font->packer.ranges[charRangeIndex].firstCodepoint = codepoint;
|
||||||
|
}
|
||||||
|
else if (codepoint != font->packer.ranges[charRangeIndex].firstCodepoint + font->packer.ranges[charRangeIndex].charCount)
|
||||||
|
{
|
||||||
|
// codepoint is not continuous, start a new range
|
||||||
|
charRangeIndex += 1;
|
||||||
|
font->packer.rangeCount += 1;
|
||||||
|
|
||||||
|
font->packer.ranges = Wellspring_realloc(font->packer.ranges, sizeof(PackedChar) * (charRangeIndex + 1));
|
||||||
|
font->packer.ranges[charRangeIndex].firstCodepoint = codepoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
font->packer.ranges[charRangeIndex].charCount += 1;
|
||||||
|
font->packer.ranges[charRangeIndex].data = Wellspring_realloc(font->packer.ranges[charRangeIndex].data, sizeof(PackedChar) * font->packer.ranges[charRangeIndex].charCount);
|
||||||
|
|
||||||
|
PackedChar *packedChar = &font->packer.ranges[charRangeIndex].data[font->packer.ranges[charRangeIndex].charCount - 1];
|
||||||
|
packedChar->atlasLeft = 0;
|
||||||
|
packedChar->atlasRight = 0;
|
||||||
|
packedChar->atlasTop = 0;
|
||||||
|
packedChar->atlasBottom = 0;
|
||||||
|
packedChar->planeLeft = 0;
|
||||||
|
packedChar->planeRight = 0;
|
||||||
|
packedChar->planeTop = 0;
|
||||||
|
packedChar->planeBottom = 0;
|
||||||
|
|
||||||
|
packedChar->xAdvance = json_object_get_double(currentGlyphObject, "advance");
|
||||||
|
|
||||||
|
if (json_object_has_key(currentGlyphObject, "atlasBounds"))
|
||||||
|
{
|
||||||
|
json_object_t *boundsObject = json_object_get_object(currentGlyphObject, "atlasBounds");
|
||||||
|
|
||||||
|
packedChar->atlasLeft = json_object_get_double(boundsObject, "left");
|
||||||
|
packedChar->atlasRight = json_object_get_double(boundsObject, "right");
|
||||||
|
packedChar->atlasTop = json_object_get_double(boundsObject, "top");
|
||||||
|
packedChar->atlasBottom = json_object_get_double(boundsObject, "bottom");
|
||||||
|
|
||||||
|
json_object_t *planeObject = json_object_get_object(currentGlyphObject, "planeBounds");
|
||||||
|
|
||||||
|
packedChar->planeLeft = json_object_get_double(planeObject, "left");
|
||||||
|
packedChar->planeRight = json_object_get_double(planeObject, "right");
|
||||||
|
packedChar->planeTop = json_object_get_double(planeObject, "top");
|
||||||
|
packedChar->planeBottom = json_object_get_double(planeObject, "bottom");
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGlyphElement = currentGlyphElement->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int advanceWidth, bearing;
|
||||||
|
stbtt_GetCodepointHMetrics(&font->fontInfo, font->packer.ranges[0].firstCodepoint, &advanceWidth, &bearing);
|
||||||
|
|
||||||
|
font->kerningScale = font->packer.ranges[0].data[0].xAdvance / advanceWidth;
|
||||||
|
|
||||||
|
Wellspring_free(jsonRoot);
|
||||||
|
|
||||||
|
*pPixelsPerEm = font->pixelsPerEm;
|
||||||
|
*pDistanceRange = font->distanceRange;
|
||||||
|
|
||||||
return (Wellspring_Font*) font;
|
return (Wellspring_Font*) font;
|
||||||
}
|
}
|
||||||
|
|
||||||
Wellspring_Packer* Wellspring_CreatePacker(
|
Wellspring_TextBatch* Wellspring_CreateTextBatch(void)
|
||||||
Wellspring_Font *font,
|
|
||||||
float fontSize,
|
|
||||||
uint32_t width,
|
|
||||||
uint32_t height,
|
|
||||||
uint32_t strideInBytes,
|
|
||||||
uint32_t padding
|
|
||||||
) {
|
|
||||||
Packer *packer = Wellspring_malloc(sizeof(Packer));
|
|
||||||
|
|
||||||
packer->font = (Font*) font;
|
|
||||||
packer->fontSize = fontSize;
|
|
||||||
|
|
||||||
packer->context = Wellspring_malloc(sizeof(stbtt_pack_context));
|
|
||||||
packer->pixels = Wellspring_malloc(sizeof(uint8_t) * width * height);
|
|
||||||
|
|
||||||
packer->width = width;
|
|
||||||
packer->height = height;
|
|
||||||
packer->strideInBytes = strideInBytes;
|
|
||||||
packer->padding = padding;
|
|
||||||
|
|
||||||
packer->ranges = NULL;
|
|
||||||
packer->rangeCount = 0;
|
|
||||||
|
|
||||||
packer->scale = stbtt_ScaleForPixelHeight(&packer->font->fontInfo, fontSize);
|
|
||||||
|
|
||||||
stbtt_PackBegin(packer->context, packer->pixels, width, height, strideInBytes, padding, NULL);
|
|
||||||
|
|
||||||
return (Wellspring_Packer*) packer;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Wellspring_PackFontRanges(
|
|
||||||
Wellspring_Packer *packer,
|
|
||||||
Wellspring_FontRange *ranges,
|
|
||||||
uint32_t numRanges
|
|
||||||
) {
|
|
||||||
Packer *myPacker = (Packer*) packer;
|
|
||||||
Wellspring_FontRange *currentFontRange;
|
|
||||||
stbtt_pack_range* stbPackRanges = Wellspring_malloc(sizeof(stbtt_pack_range) * numRanges);
|
|
||||||
CharRange *currentCharRange;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < numRanges; i += 1)
|
|
||||||
{
|
|
||||||
currentFontRange = &ranges[i];
|
|
||||||
stbPackRanges[i].font_size = myPacker->fontSize;
|
|
||||||
stbPackRanges[i].first_unicode_codepoint_in_range = currentFontRange->firstCodepoint;
|
|
||||||
stbPackRanges[i].array_of_unicode_codepoints = NULL;
|
|
||||||
stbPackRanges[i].num_chars = currentFontRange->numChars;
|
|
||||||
stbPackRanges[i].h_oversample = currentFontRange->oversampleH;
|
|
||||||
stbPackRanges[i].v_oversample = currentFontRange->oversampleV;
|
|
||||||
stbPackRanges[i].chardata_for_range = Wellspring_malloc(sizeof(stbtt_packedchar) * currentFontRange->numChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stbtt_PackFontRanges(myPacker->context, myPacker->font->fontBytes, 0, stbPackRanges, numRanges))
|
|
||||||
{
|
|
||||||
/* Font packing failed, time to bail */
|
|
||||||
for (i = 0; i < numRanges; i += 1)
|
|
||||||
{
|
|
||||||
Wellspring_free(stbPackRanges[i].chardata_for_range);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
myPacker->ranges = Wellspring_realloc(myPacker->ranges, sizeof(CharRange) * (myPacker->rangeCount + numRanges));
|
|
||||||
|
|
||||||
for (i = 0; i < numRanges; i += 1)
|
|
||||||
{
|
|
||||||
currentCharRange = &myPacker->ranges[myPacker->rangeCount + i];
|
|
||||||
currentCharRange->data = stbPackRanges[i].chardata_for_range;
|
|
||||||
currentCharRange->firstCodepoint = stbPackRanges[i].first_unicode_codepoint_in_range;
|
|
||||||
currentCharRange->charCount = stbPackRanges[i].num_chars;
|
|
||||||
currentCharRange->fontSize = stbPackRanges[i].font_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
myPacker->rangeCount += numRanges;
|
|
||||||
|
|
||||||
Wellspring_free(stbPackRanges);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Wellspring_GetPixelDataPointer(
|
|
||||||
Wellspring_Packer *packer
|
|
||||||
) {
|
|
||||||
Packer* myPacker = (Packer*) packer;
|
|
||||||
return myPacker->pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
Wellspring_TextBatch* Wellspring_CreateTextBatch()
|
|
||||||
{
|
{
|
||||||
Batch *batch = Wellspring_malloc(sizeof(Batch));
|
Batch *batch = Wellspring_malloc(sizeof(Batch));
|
||||||
|
|
||||||
|
@ -331,10 +505,10 @@ Wellspring_TextBatch* Wellspring_CreateTextBatch()
|
||||||
|
|
||||||
void Wellspring_StartTextBatch(
|
void Wellspring_StartTextBatch(
|
||||||
Wellspring_TextBatch *textBatch,
|
Wellspring_TextBatch *textBatch,
|
||||||
Wellspring_Packer *packer
|
Wellspring_Font *font
|
||||||
) {
|
) {
|
||||||
Batch *batch = (Batch*) textBatch;
|
Batch *batch = (Batch*) textBatch;
|
||||||
batch->currentPacker = (Packer*) packer;
|
batch->currentFont = (Font*) font;
|
||||||
batch->vertexCount = 0;
|
batch->vertexCount = 0;
|
||||||
batch->indexCount = 0;
|
batch->indexCount = 0;
|
||||||
}
|
}
|
||||||
|
@ -350,15 +524,15 @@ static float Wellspring_INTERNAL_GetVerticalAlignOffset(
|
||||||
}
|
}
|
||||||
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_TOP)
|
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_TOP)
|
||||||
{
|
{
|
||||||
return scale * font->ascent;
|
return scale * font->ascender;
|
||||||
}
|
}
|
||||||
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_MIDDLE)
|
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_MIDDLE)
|
||||||
{
|
{
|
||||||
return scale * (font->ascent + font->descent) / 2.0f;
|
return scale * (font->ascender + font->descender) / 2.0f;
|
||||||
}
|
}
|
||||||
else /* BOTTOM */
|
else /* BOTTOM */
|
||||||
{
|
{
|
||||||
return scale * font->descent;
|
return scale * font->descender;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,10 +558,40 @@ static inline uint32_t IsWhitespace(uint32_t codepoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void GetPackedQuad(PackedChar *charData, float scale, int packerWidth, int packerHeight, int charIndex, float *xPos, float *yPos, Quad *q)
|
||||||
|
{
|
||||||
|
float texelWidth = 1.0f / packerWidth, texelHeight = 1.0f / packerHeight;
|
||||||
|
PackedChar *b = charData + charIndex;
|
||||||
|
|
||||||
|
float pl, pb, pr, pt;
|
||||||
|
float il, ib, ir, it;
|
||||||
|
|
||||||
|
pl = *xPos + b->planeLeft * scale;
|
||||||
|
pb = *yPos + b->planeBottom * scale;
|
||||||
|
pr = *xPos + b->planeRight * scale;
|
||||||
|
pt = *yPos + b->planeTop * scale;
|
||||||
|
|
||||||
|
il = b->atlasLeft * texelWidth;
|
||||||
|
ib = b->atlasBottom * texelHeight;
|
||||||
|
ir = b->atlasRight * texelWidth;
|
||||||
|
it = b->atlasTop * texelHeight;
|
||||||
|
|
||||||
|
q->x0 = pl;
|
||||||
|
q->y0 = pt;
|
||||||
|
q->x1 = pr;
|
||||||
|
q->y1 = pb;
|
||||||
|
|
||||||
|
q->s0 = il;
|
||||||
|
q->t0 = it;
|
||||||
|
q->s1 = ir;
|
||||||
|
q->t1 = ib;
|
||||||
|
|
||||||
|
*xPos += b->xAdvance * scale;
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t Wellspring_Internal_TextBounds(
|
static uint8_t Wellspring_Internal_TextBounds(
|
||||||
Packer* packer,
|
Font* font,
|
||||||
float x,
|
int pixelSize,
|
||||||
float y,
|
|
||||||
Wellspring_HorizontalAlignment horizontalAlignment,
|
Wellspring_HorizontalAlignment horizontalAlignment,
|
||||||
Wellspring_VerticalAlignment verticalAlignment,
|
Wellspring_VerticalAlignment verticalAlignment,
|
||||||
const uint8_t* strBytes,
|
const uint8_t* strBytes,
|
||||||
|
@ -397,20 +601,21 @@ static uint8_t Wellspring_Internal_TextBounds(
|
||||||
uint32_t decodeState = 0;
|
uint32_t decodeState = 0;
|
||||||
uint32_t codepoint;
|
uint32_t codepoint;
|
||||||
int32_t glyphIndex;
|
int32_t glyphIndex;
|
||||||
int32_t previousGlyphIndex;
|
int32_t previousGlyphIndex = -1;
|
||||||
int32_t rangeIndex;
|
int32_t rangeIndex;
|
||||||
stbtt_packedchar* rangeData;
|
PackedChar* rangeData;
|
||||||
float rangeFontSize;
|
Quad charQuad;
|
||||||
stbtt_aligned_quad charQuad;
|
|
||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
|
float x = 0, y = 0;
|
||||||
float minX = x;
|
float minX = x;
|
||||||
float minY = y;
|
float minY = y;
|
||||||
float maxX = x;
|
float maxX = x;
|
||||||
float maxY = y;
|
float maxY = y;
|
||||||
float startX = x;
|
float startX = x;
|
||||||
float advance = 0;
|
float advance = 0;
|
||||||
|
float sizeFactor = pixelSize / font->pixelsPerEm;
|
||||||
|
|
||||||
y += Wellspring_INTERNAL_GetVerticalAlignOffset(packer->font, verticalAlignment, packer->scale);
|
y -= Wellspring_INTERNAL_GetVerticalAlignOffset(font, verticalAlignment, sizeFactor * font->scale);
|
||||||
|
|
||||||
for (i = 0; i < strLengthInBytes; i += 1)
|
for (i = 0; i < strLengthInBytes; i += 1)
|
||||||
{
|
{
|
||||||
|
@ -425,17 +630,10 @@ static uint8_t Wellspring_Internal_TextBounds(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsWhitespace(codepoint))
|
|
||||||
{
|
|
||||||
int32_t ws_adv, ws_bearing;
|
|
||||||
stbtt_GetCodepointHMetrics(&packer->font->fontInfo, codepoint, &ws_adv, &ws_bearing);
|
|
||||||
x += packer->scale * ws_adv;
|
|
||||||
maxX += packer->scale * ws_adv;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rangeData = NULL;
|
rangeData = NULL;
|
||||||
|
|
||||||
|
Packer *packer = &font->packer;
|
||||||
|
|
||||||
/* Find the packed char data */
|
/* Find the packed char data */
|
||||||
for (j = 0; j < packer->rangeCount; j += 1)
|
for (j = 0; j < packer->rangeCount; j += 1)
|
||||||
{
|
{
|
||||||
|
@ -445,7 +643,6 @@ static uint8_t Wellspring_Internal_TextBounds(
|
||||||
) {
|
) {
|
||||||
rangeData = packer->ranges[j].data;
|
rangeData = packer->ranges[j].data;
|
||||||
rangeIndex = codepoint - packer->ranges[j].firstCodepoint;
|
rangeIndex = codepoint - packer->ranges[j].firstCodepoint;
|
||||||
rangeFontSize = packer->ranges[j].fontSize;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,22 +653,31 @@ static uint8_t Wellspring_Internal_TextBounds(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
glyphIndex = stbtt_FindGlyphIndex(&packer->font->fontInfo, codepoint);
|
if (IsWhitespace(codepoint))
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
{
|
{
|
||||||
x += packer->scale * stbtt_GetGlyphKernAdvance(&packer->font->fontInfo, previousGlyphIndex, glyphIndex);
|
PackedChar *packedChar = rangeData + rangeIndex;
|
||||||
|
x += sizeFactor * font->scale * packedChar->xAdvance;
|
||||||
|
maxX += sizeFactor * font->scale * packedChar->xAdvance;
|
||||||
|
previousGlyphIndex = -1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
stbtt_GetPackedQuad(
|
glyphIndex = stbtt_FindGlyphIndex(&font->fontInfo, codepoint);
|
||||||
|
|
||||||
|
if (previousGlyphIndex != -1)
|
||||||
|
{
|
||||||
|
x += sizeFactor * font->kerningScale * font->scale * stbtt_GetGlyphKernAdvance(&font->fontInfo, previousGlyphIndex, glyphIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPackedQuad(
|
||||||
rangeData,
|
rangeData,
|
||||||
|
sizeFactor * font->scale,
|
||||||
packer->width,
|
packer->width,
|
||||||
packer->height,
|
packer->height,
|
||||||
rangeIndex,
|
rangeIndex,
|
||||||
&x,
|
&x,
|
||||||
&y,
|
&y,
|
||||||
&charQuad,
|
&charQuad
|
||||||
0
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (charQuad.x0 < minX) { minX = charQuad.x0; }
|
if (charQuad.x0 < minX) { minX = charQuad.x0; }
|
||||||
|
@ -504,9 +710,8 @@ static uint8_t Wellspring_Internal_TextBounds(
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Wellspring_TextBounds(
|
uint8_t Wellspring_TextBounds(
|
||||||
Wellspring_Packer* packer,
|
Wellspring_Font *font,
|
||||||
float x,
|
int pixelSize,
|
||||||
float y,
|
|
||||||
Wellspring_HorizontalAlignment horizontalAlignment,
|
Wellspring_HorizontalAlignment horizontalAlignment,
|
||||||
Wellspring_VerticalAlignment verticalAlignment,
|
Wellspring_VerticalAlignment verticalAlignment,
|
||||||
const uint8_t* strBytes,
|
const uint8_t* strBytes,
|
||||||
|
@ -514,9 +719,8 @@ uint8_t Wellspring_TextBounds(
|
||||||
Wellspring_Rectangle* pRectangle
|
Wellspring_Rectangle* pRectangle
|
||||||
) {
|
) {
|
||||||
return Wellspring_Internal_TextBounds(
|
return Wellspring_Internal_TextBounds(
|
||||||
(Packer*) packer,
|
(Font*) font,
|
||||||
x,
|
pixelSize,
|
||||||
y,
|
|
||||||
horizontalAlignment,
|
horizontalAlignment,
|
||||||
verticalAlignment,
|
verticalAlignment,
|
||||||
strBytes,
|
strBytes,
|
||||||
|
@ -525,11 +729,9 @@ uint8_t Wellspring_TextBounds(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Wellspring_Draw(
|
uint8_t Wellspring_AddToTextBatch(
|
||||||
Wellspring_TextBatch *textBatch,
|
Wellspring_TextBatch *textBatch,
|
||||||
float x,
|
int pixelSize,
|
||||||
float y,
|
|
||||||
float depth,
|
|
||||||
Wellspring_Color *color,
|
Wellspring_Color *color,
|
||||||
Wellspring_HorizontalAlignment horizontalAlignment,
|
Wellspring_HorizontalAlignment horizontalAlignment,
|
||||||
Wellspring_VerticalAlignment verticalAlignment,
|
Wellspring_VerticalAlignment verticalAlignment,
|
||||||
|
@ -537,26 +739,28 @@ uint8_t Wellspring_Draw(
|
||||||
uint32_t strLengthInBytes
|
uint32_t strLengthInBytes
|
||||||
) {
|
) {
|
||||||
Batch *batch = (Batch*) textBatch;
|
Batch *batch = (Batch*) textBatch;
|
||||||
Packer *myPacker = batch->currentPacker;
|
Font *font = batch->currentFont;
|
||||||
|
Packer *myPacker = &font->packer;
|
||||||
uint32_t decodeState = 0;
|
uint32_t decodeState = 0;
|
||||||
uint32_t codepoint;
|
uint32_t codepoint;
|
||||||
int32_t glyphIndex;
|
int32_t glyphIndex;
|
||||||
int32_t previousGlyphIndex;
|
int32_t previousGlyphIndex = -1;
|
||||||
int32_t rangeIndex;
|
int32_t rangeIndex;
|
||||||
stbtt_packedchar *rangeData;
|
PackedChar *rangeData;
|
||||||
float rangeFontSize;
|
Quad charQuad;
|
||||||
stbtt_aligned_quad charQuad;
|
|
||||||
uint32_t vertexBufferIndex;
|
uint32_t vertexBufferIndex;
|
||||||
uint32_t indexBufferIndex;
|
uint32_t indexBufferIndex;
|
||||||
Wellspring_Rectangle bounds;
|
Wellspring_Rectangle bounds;
|
||||||
uint32_t i, j;
|
uint32_t i, j;
|
||||||
|
float sizeFactor = pixelSize / font->pixelsPerEm;
|
||||||
|
float x = 0, y = 0;
|
||||||
|
|
||||||
y += Wellspring_INTERNAL_GetVerticalAlignOffset(myPacker->font, verticalAlignment, myPacker->scale);
|
y -= Wellspring_INTERNAL_GetVerticalAlignOffset(font, verticalAlignment, sizeFactor * font->scale);
|
||||||
|
|
||||||
/* FIXME: If we horizontally align, we have to decode and process glyphs twice, very inefficient. */
|
/* FIXME: If we horizontally align, we have to decode and process glyphs twice, very inefficient. */
|
||||||
if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_RIGHT)
|
if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_RIGHT)
|
||||||
{
|
{
|
||||||
if (!Wellspring_Internal_TextBounds(myPacker, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
|
if (!Wellspring_Internal_TextBounds(font, pixelSize, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
|
||||||
{
|
{
|
||||||
/* Something went wrong while calculating bounds. */
|
/* Something went wrong while calculating bounds. */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -566,7 +770,7 @@ uint8_t Wellspring_Draw(
|
||||||
}
|
}
|
||||||
else if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_CENTER)
|
else if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_CENTER)
|
||||||
{
|
{
|
||||||
if (!Wellspring_Internal_TextBounds(myPacker, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
|
if (!Wellspring_Internal_TextBounds(font, pixelSize, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
|
||||||
{
|
{
|
||||||
/* Something went wrong while calculating bounds. */
|
/* Something went wrong while calculating bounds. */
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -588,14 +792,6 @@ uint8_t Wellspring_Draw(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsWhitespace(codepoint))
|
|
||||||
{
|
|
||||||
int32_t ws_adv, ws_bearing;
|
|
||||||
stbtt_GetCodepointHMetrics(&myPacker->font->fontInfo, codepoint, &ws_adv, &ws_bearing);
|
|
||||||
x += myPacker->scale * ws_adv;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rangeData = NULL;
|
rangeData = NULL;
|
||||||
|
|
||||||
/* Find the packed char data */
|
/* Find the packed char data */
|
||||||
|
@ -607,7 +803,6 @@ uint8_t Wellspring_Draw(
|
||||||
) {
|
) {
|
||||||
rangeData = myPacker->ranges[j].data;
|
rangeData = myPacker->ranges[j].data;
|
||||||
rangeIndex = codepoint - myPacker->ranges[j].firstCodepoint;
|
rangeIndex = codepoint - myPacker->ranges[j].firstCodepoint;
|
||||||
rangeFontSize = myPacker->ranges[j].fontSize;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,22 +813,30 @@ uint8_t Wellspring_Draw(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
glyphIndex = stbtt_FindGlyphIndex(&myPacker->font->fontInfo, codepoint);
|
if (IsWhitespace(codepoint))
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
{
|
{
|
||||||
x += myPacker->scale * stbtt_GetGlyphKernAdvance(&myPacker->font->fontInfo, previousGlyphIndex, glyphIndex);
|
PackedChar *packedChar = rangeData + rangeIndex;
|
||||||
|
x += sizeFactor * font->scale * packedChar->xAdvance;
|
||||||
|
previousGlyphIndex = -1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
stbtt_GetPackedQuad(
|
glyphIndex = stbtt_FindGlyphIndex(&font->fontInfo, codepoint);
|
||||||
|
|
||||||
|
if (previousGlyphIndex != -1)
|
||||||
|
{
|
||||||
|
x += sizeFactor * font->kerningScale * font->scale * stbtt_GetGlyphKernAdvance(&font->fontInfo, previousGlyphIndex, glyphIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetPackedQuad(
|
||||||
rangeData,
|
rangeData,
|
||||||
|
sizeFactor * font->scale,
|
||||||
myPacker->width,
|
myPacker->width,
|
||||||
myPacker->height,
|
myPacker->height,
|
||||||
rangeIndex,
|
rangeIndex,
|
||||||
&x,
|
&x,
|
||||||
&y,
|
&y,
|
||||||
&charQuad,
|
&charQuad
|
||||||
0
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (batch->vertexCount >= batch->vertexCapacity)
|
if (batch->vertexCount >= batch->vertexCapacity)
|
||||||
|
@ -648,14 +851,12 @@ uint8_t Wellspring_Draw(
|
||||||
batch->indices = Wellspring_realloc(batch->indices, sizeof(uint32_t) * batch->indexCapacity);
|
batch->indices = Wellspring_realloc(batch->indices, sizeof(uint32_t) * batch->indexCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: kerning and alignment */
|
|
||||||
|
|
||||||
vertexBufferIndex = batch->vertexCount;
|
vertexBufferIndex = batch->vertexCount;
|
||||||
indexBufferIndex = batch->indexCount;
|
indexBufferIndex = batch->indexCount;
|
||||||
|
|
||||||
batch->vertices[vertexBufferIndex].x = charQuad.x0;
|
batch->vertices[vertexBufferIndex].x = charQuad.x0;
|
||||||
batch->vertices[vertexBufferIndex].y = charQuad.y0;
|
batch->vertices[vertexBufferIndex].y = charQuad.y0;
|
||||||
batch->vertices[vertexBufferIndex].z = depth;
|
batch->vertices[vertexBufferIndex].z = 0;
|
||||||
batch->vertices[vertexBufferIndex].u = charQuad.s0;
|
batch->vertices[vertexBufferIndex].u = charQuad.s0;
|
||||||
batch->vertices[vertexBufferIndex].v = charQuad.t0;
|
batch->vertices[vertexBufferIndex].v = charQuad.t0;
|
||||||
batch->vertices[vertexBufferIndex].r = color->r;
|
batch->vertices[vertexBufferIndex].r = color->r;
|
||||||
|
@ -665,7 +866,7 @@ uint8_t Wellspring_Draw(
|
||||||
|
|
||||||
batch->vertices[vertexBufferIndex + 1].x = charQuad.x0;
|
batch->vertices[vertexBufferIndex + 1].x = charQuad.x0;
|
||||||
batch->vertices[vertexBufferIndex + 1].y = charQuad.y1;
|
batch->vertices[vertexBufferIndex + 1].y = charQuad.y1;
|
||||||
batch->vertices[vertexBufferIndex + 1].z = depth;
|
batch->vertices[vertexBufferIndex + 1].z = 0;
|
||||||
batch->vertices[vertexBufferIndex + 1].u = charQuad.s0;
|
batch->vertices[vertexBufferIndex + 1].u = charQuad.s0;
|
||||||
batch->vertices[vertexBufferIndex + 1].v = charQuad.t1;
|
batch->vertices[vertexBufferIndex + 1].v = charQuad.t1;
|
||||||
batch->vertices[vertexBufferIndex + 1].r = color->r;
|
batch->vertices[vertexBufferIndex + 1].r = color->r;
|
||||||
|
@ -675,7 +876,7 @@ uint8_t Wellspring_Draw(
|
||||||
|
|
||||||
batch->vertices[vertexBufferIndex + 2].x = charQuad.x1;
|
batch->vertices[vertexBufferIndex + 2].x = charQuad.x1;
|
||||||
batch->vertices[vertexBufferIndex + 2].y = charQuad.y0;
|
batch->vertices[vertexBufferIndex + 2].y = charQuad.y0;
|
||||||
batch->vertices[vertexBufferIndex + 2].z = depth;
|
batch->vertices[vertexBufferIndex + 2].z = 0;
|
||||||
batch->vertices[vertexBufferIndex + 2].u = charQuad.s1;
|
batch->vertices[vertexBufferIndex + 2].u = charQuad.s1;
|
||||||
batch->vertices[vertexBufferIndex + 2].v = charQuad.t0;
|
batch->vertices[vertexBufferIndex + 2].v = charQuad.t0;
|
||||||
batch->vertices[vertexBufferIndex + 2].r = color->r;
|
batch->vertices[vertexBufferIndex + 2].r = color->r;
|
||||||
|
@ -685,7 +886,7 @@ uint8_t Wellspring_Draw(
|
||||||
|
|
||||||
batch->vertices[vertexBufferIndex + 3].x = charQuad.x1;
|
batch->vertices[vertexBufferIndex + 3].x = charQuad.x1;
|
||||||
batch->vertices[vertexBufferIndex + 3].y = charQuad.y1;
|
batch->vertices[vertexBufferIndex + 3].y = charQuad.y1;
|
||||||
batch->vertices[vertexBufferIndex + 3].z = depth;
|
batch->vertices[vertexBufferIndex + 3].z = 0;
|
||||||
batch->vertices[vertexBufferIndex + 3].u = charQuad.s1;
|
batch->vertices[vertexBufferIndex + 3].u = charQuad.s1;
|
||||||
batch->vertices[vertexBufferIndex + 3].v = charQuad.t1;
|
batch->vertices[vertexBufferIndex + 3].v = charQuad.t1;
|
||||||
batch->vertices[vertexBufferIndex + 3].r = color->r;
|
batch->vertices[vertexBufferIndex + 3].r = color->r;
|
||||||
|
@ -733,27 +934,15 @@ void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch)
|
||||||
Wellspring_free(batch);
|
Wellspring_free(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wellspring_DestroyPacker(Wellspring_Packer *packer)
|
|
||||||
{
|
|
||||||
Packer* myPacker = (Packer*) packer;
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
stbtt_PackEnd(myPacker->context);
|
|
||||||
|
|
||||||
for (i = 0; i < myPacker->rangeCount; i += 1)
|
|
||||||
{
|
|
||||||
Wellspring_free(myPacker->ranges[i].data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Wellspring_free(myPacker->ranges);
|
|
||||||
Wellspring_free(myPacker->context);
|
|
||||||
Wellspring_free(myPacker->pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wellspring_DestroyFont(Wellspring_Font* font)
|
void Wellspring_DestroyFont(Wellspring_Font* font)
|
||||||
{
|
{
|
||||||
Font *myFont = (Font*) font;
|
Font *myFont = (Font*) font;
|
||||||
|
|
||||||
|
for (int i = 0; i < myFont->packer.rangeCount; i += 1)
|
||||||
|
{
|
||||||
|
Wellspring_free(myFont->packer.ranges[i].data);
|
||||||
|
}
|
||||||
|
Wellspring_free(myFont->packer.ranges);
|
||||||
Wellspring_free(myFont->fontBytes);
|
Wellspring_free(myFont->fontBytes);
|
||||||
Wellspring_free(myFont);
|
Wellspring_free(myFont);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.30717.126
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Wellspring", "Wellspring.vcxproj", "{6DB15344-E000-45CB-A48A-1D72F7D6E945}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
MinSizeRel|x64 = MinSizeRel|x64
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
RelWithDebInfo|x64 = RelWithDebInfo|x64
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.Release|x64.Build.0 = Release|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
|
||||||
{6DB15344-E000-45CB-A48A-1D72F7D6E945}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {7B2DB465-0A55-3811-9EF4-A520B47653D2}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
|
@ -1,84 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>{6DB15344-E000-45CB-A48A-1D72F7D6E945}</ProjectGuid>
|
|
||||||
<RootNamespace>FNA3D</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
|
||||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>MultiByte</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<IncludePath>..\lib\;..\..\SDL2\include;..\include;$(IncludePath)</IncludePath>
|
|
||||||
<LibraryPath>..\..\SDL2\lib\$(PlatformShortName);$(LibraryPath)</LibraryPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<PreprocessorDefinitions>USE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<PreprocessorDefinitions>USE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<AdditionalDependencies>SDL2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="..\src\Wellspring.c" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="..\lib\stb_rect_pack.h" />
|
|
||||||
<ClCompile Include="..\lib\stb_truetype.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="..\include\Wellspring.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup />
|
|
||||||
</Project>
|
|
Loading…
Reference in New Issue