- Wellspring is now a MSDF font rendering library, accepting output from msdf-atlas-gen for this purpose - Renamed Wellspring_Draw to Wellspring_AddToTextBatch - TextBounds and AddToTextBatch no longer accept position and depth values - Removed Packer - Removed CreatePacker, PackFontRanges, and GetPixelDataPointer. Font packing is now handled by msdf-atlas-gen - Removed visualc solution in favor of CMake everywhere Reviewed-on: #1main
							parent
							
								
									6e0e4d0fa0
								
							
						
					
					
						commit
						ab238503a3
					
				|  | @ -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