text:
/*************************************************************************/
/*  tl_font.hpp                                                          */
/*************************************************************************/

#ifndef TL_FONT_FACE_HPP
#define TL_FONT_FACE_HPP

#include "tl_core.hpp"

#ifdef GODOT_MODULE
#include "core/resource.h"
#include "scene/resources/texture.h"
#else
#include <Image.hpp>
#include <ImageTexture.hpp>
#include <RID.hpp>
#endif

#include <vector>

#include <hb.h>

using namespace godot;

//http://font.gohu.org/ (WTFPL) based 5x7 hex number font for char code box drawing
static const unsigned char _hex_box_img_data[167] = {
	0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00, 0x00, 0x00, 0xA5, 0x54, 0x58, 0xA1, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4C, 0x54, 0x45, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xA5, 0xD9, 0x9F, 0xDD, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4E, 0x53, 0x00, 0x40, 0xE6, 0xD8, 0x66, 0x00, 0x00, 0x00, 0x4F, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0x28, 0x94, 0x79, 0x58, 0x7B, 0xBF, 0x78, 0xEE, 0xF3, 0xEA, 0xFF, 0x0C, 0xDD, 0xCA, 0xC2, 0x4E, 0x8C, 0x3D, 0xC9, 0x12, 0xC7, 0x04, 0x18, 0xE6, 0x32, 0x89, 0x56, 0x1F, 0x02, 0x32, 0xDD, 0x04, 0x18, 0x56, 0xB2, 0x64, 0xB2, 0x29, 0x15, 0xFF, 0x7F, 0xE1, 0x7E, 0x8F, 0xE1, 0x24, 0x87, 0x7C, 0x9B, 0x4A, 0x07, 0x58, 0xB4, 0x53, 0x50, 0xD0, 0x0D, 0xC4, 0x04, 0xAA, 0x2D, 0xB4, 0x7B, 0x68, 0x79, 0xA4, 0x78, 0xF1, 0xF3, 0xEA, 0x0F, 0x00, 0x5F, 0x2A, 0x1C, 0xFE, 0x51, 0xD4, 0xC9, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
};

class TLFontFace : public Resource {
	GODOT_CLASS(TLFontFace, Resource);

protected:
	//Ref<TLFontFace> fallback;
	static Ref<ImageTexture> hex_box_img_tex;
	String path;

public:
	TLFontFace();
	virtual ~TLFontFace();

	void _init();

	//Internal methods
	static void initialize_hex_font();
	static void finish_hex_font();

	virtual hb_font_t *get_hb_font(int p_size) const;
	virtual float get_glyph_scale(int p_size) const;

	static void draw_hexbox(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate);
	static void _draw_small_int(RID p_canvas_item, const Point2 p_pos, uint32_t p_int, const Color p_modulate);

	virtual void draw_glyph(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const;
	virtual void _draw_char(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const; //raw char for debug only, do not use
	virtual void draw_glyph_outline(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const;
	virtual Array get_glyph_outline(const Point2 p_pos, uint32_t p_codepoint, int p_size) const;

	Array _unicode_scripts_supported() const;
	virtual std::vector<hb_script_t> unicode_scripts_supported() const;

	//GDNative methods
	virtual bool load(String p_resource_path);

	virtual void set_font_path(String p_resource_path);
	virtual String get_font_path() const;

	virtual double get_ascent(int p_size) const;
	virtual double get_descent(int p_size) const;
	virtual double get_height(int p_size) const;

	virtual int get_base_size() const;

	virtual void set_texture_flags(int p_flags);
	virtual int get_texture_flags() const;

#ifdef GODOT_MODULE
	static void _bind_methods();
#else
	static void _register_methods();
#endif
};

#endif /*TL_FONT_FACE_HPP*/


text:
/*************************************************************************/
/*  tl_dynamic_font.cpp                                                  */
/*************************************************************************/

/*
This file is based in part on Godot's DynamicFont implementation, licensed under MIT license.
For more information, see https://github.com/godotengine/godot/blob/master/LICENSE.txt
For original source, see https://github.com/godotengine/godot/blob/master/scene/resources/dynamic_font.cpp
*/

#ifdef GODOT_MODULE
#include "core/bind/core_bind.h"
#include "servers/visual_server.h"
#define File _File
#else
#include <File.hpp>
#include <GlobalConstants.hpp>
#include <Texture.hpp>
#include <VisualServer.hpp>
#endif

#include "tl_dynamic_font.hpp"
#include FT_STROKER_H

_ALWAYS_INLINE_ unsigned int TLDynamicFontFaceAtSize::_next_power_of_2(unsigned int x) {

	--x;
	x |= x >> 1;
	x |= x >> 2;
	x |= x >> 4;
	x |= x >> 8;
	x |= x >> 16;

	return ++x;
}

unsigned long TLDynamicFontFaceAtSize::ft_stream_io(FT_Stream p_stream, unsigned long p_offset, unsigned char *p_buffer, unsigned long p_count) {

	File *file = reinterpret_cast<File *>(p_stream->descriptor.pointer);

	if ((uint64_t)file->get_position() != p_offset) {
		file->seek(p_offset);
	}

	if (p_count == 0)
		return 0;

	PoolByteArray buffer = file->get_buffer(p_count);
	memcpy(p_buffer, buffer.read().ptr(), buffer.size());

	return buffer.size();
}

void TLDynamicFontFaceAtSize::ft_stream_close(FT_Stream p_stream) {

	File *file = reinterpret_cast<File *>(p_stream->descriptor.pointer);

	file->close();
	memdelete(file);
}

TLDynamicFontFaceAtSize::TLDynamicFontFaceAtSize() {

	hinting = DF_HINTING_NORMAL;
	force_autohinter = false;
	txt_flags = Texture::FLAG_VIDEO_SURFACE;
	scale_color_font = 1.0f;
	oversampling = 1.0f;

	loaded = false;
	ft_size = 0;
	ascent = 0.0f;
	descent = 0.0f;
	height = 0.0f;
	hb_font = NULL;
	os2 = NULL;
}

TLDynamicFontFaceAtSize::~TLDynamicFontFaceAtSize() {

	clear_cache();
	if (hb_font) {
		hb_font_destroy(hb_font);
		hb_font = NULL;
	}
	if (loaded) {
		FT_Done_FreeType(ft_library);
	}
	loaded = false;
}

void TLDynamicFontFaceAtSize::clear_cache() {

	texture_cache.clear();
	glyph_cache.clear();
	glyph_outline_cache.clear();
}

float TLDynamicFontFaceAtSize::get_glyph_scale() const {

	return scale_color_font / oversampling;
}

void TLDynamicFontFaceAtSize::draw_glyph(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate) const {

	if (loaded) {
		if (glyph_cache.count(p_codepoint) == 0)
			const_cast<TLDynamicFontFaceAtSize *>(this)->update_glyph(p_codepoint);

		if (glyph_cache.count(p_codepoint) == 0)
			return;

		const Glyph &gl = glyph_cache.at(p_codepoint);
		if (gl.valid) {

			Color modulate = p_modulate;
			if (FT_HAS_COLOR(ft_face)) {
				modulate.r = modulate.g = modulate.b = 1.0;
			}

			VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(gl.align + p_pos, gl.size * Vector2(scale_color_font, scale_color_font)), texture_cache[gl.id].image->get_rid(), gl.uv, modulate, false, RID(), false);
		}
	}
}

void TLDynamicFontFaceAtSize::_draw_char(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate) const {

	//raw char for debug only, do not use
	if (loaded) {
		draw_glyph(p_canvas_item, p_pos, FT_Get_Char_Index(ft_face, p_codepoint), p_modulate);
	}
}

void TLDynamicFontFaceAtSize::draw_glyph_outline(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate) const {

	if (loaded) {
		if (glyph_outline_cache.count(p_codepoint) == 0)
			const_cast<TLDynamicFontFaceAtSize *>(this)->update_outline_glyph(p_codepoint);

		if (glyph_outline_cache.count(p_codepoint) == 0)
			return;

		const Glyph &gl = glyph_outline_cache.at(p_codepoint);
		if (gl.valid) {

			Color modulate = p_modulate;
			if (FT_HAS_COLOR(ft_face)) {
				modulate.r = modulate.g = modulate.b = 1.0;
			}

			VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(gl.align + p_pos, gl.size * Vector2(scale_color_font, scale_color_font)), texture_cache[gl.id].image->get_rid(), gl.uv, modulate, false, RID(), false);
		}
	}
}

Array TLDynamicFontFaceAtSize::get_glyph_outline(const Point2 p_pos, uint32_t p_codepoint) const {

	Array ret;
	if (loaded) {
		if (FT_Load_Glyph(ft_face, p_codepoint, FT_LOAD_NO_BITMAP) != 0)
			return ret;

		FT_Glyph ft_glyph;

		if (FT_Get_Glyph(ft_face->glyph, &ft_glyph) != 0)
			return ret;

		if (ft_glyph->format != FT_GLYPH_FORMAT_OUTLINE)
			return ret;

		FT_Outline ol = ((FT_OutlineGlyph)ft_glyph)->outline;

		int prev = 0;
		for (int i = 0; i < ol.n_contours; i++) {
			PoolVector3Array contour;
			contour.resize(ol.contours[i] - prev);
			PoolVector3Array::Write w = contour.write();

			for (int j = 0; j < ol.contours[i] - prev; j++) {
				w[j] = Vector3(p_pos.x + ol.points[prev + 1 + j].x, p_pos.y + ol.points[prev + 1 + j].x, ol.tags[prev + 1 + j]);
			}
			prev = ol.contours[i];
			ret.push_back(contour);
		}

		FT_Done_Glyph(ft_glyph);
	}
	return ret;
}

TLDynamicFontFaceAtSize::TexturePosition TLDynamicFontFaceAtSize::find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
	TexturePosition ret;
	ret.index = -1;
	ret.x = 0;
	ret.y = 0;

	int mw = p_width;
	int mh = p_height;

	for (size_t i = 0; i < texture_cache.size(); i++) {

		const GlyphTexture &gl = texture_cache[i];

		if (gl.image->get_format() != p_image_format)
			continue;

		if (mw > gl.texture_size || mh > gl.texture_size) //too big for this texture
			continue;

		ret.y = 0x7FFFFFFF;
		ret.x = 0;

		for (int j = 0; j < gl.texture_size - mw; j++) {

			int max_y = 0;

			for (int k = j; k < j + mw; k++) {

				int y = gl.offsets[k];
				if (y > max_y)
					max_y = y;
			}

			if (max_y < ret.y) {
				ret.y = max_y;
				ret.x = j;
			}
		}

		if (ret.y == 0x7FFFFFFF || ret.y + mh > gl.texture_size)
			continue; //fail, could not fit it here

		ret.index = i;
		break;
	}

	if (ret.index == -1) {
		//could not find texture to fit, create one
		ret.x = 0;
		ret.y = 0;

		int texsize = MAX(ft_size * oversampling * 12, 512);
		if (mw > texsize)
			texsize = mw; //special case, adapt to it?
		if (mh > texsize)
			texsize = mh; //special case, adapt to it?

		texsize = _next_power_of_2(texsize);
		texsize = MIN(texsize, 4096);

		GlyphTexture tex;
		tex.texture_size = texsize;
		tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha

		//zero texture
		PoolByteArray::Write w = tex.imgdata.write();
		ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
		for (int i = 0; i < texsize * texsize * p_color_size; i++) {
			w[i] = 0;
		}

		tex.offsets.resize(texsize);
		for (int i = 0; i < texsize; i++) //zero offsets
			tex.offsets[i] = 0;

		texture_cache.push_back(tex);
		ret.index = texture_cache.size() - 1;
	}

	return ret;
}

TLDynamicFontFaceAtSize::Glyph TLDynamicFontFaceAtSize::bitmap_to_glyph(FT_Bitmap p_bitmap, int p_yofs, int p_xofs) {

	int rect_margin = 1; //const

	int w = p_bitmap.width;
	int h = p_bitmap.rows;

	int mw = w + rect_margin * 2;
	int mh = h + rect_margin * 2;

	ERR_FAIL_COND_V(mw > 4096, Glyph());
	ERR_FAIL_COND_V(mh > 4096, Glyph());

	int color_size = p_bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
	Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;

	TexturePosition tex_pos = find_texture_pos_for_glyph(color_size, require_format, mw, mh);
	ERR_FAIL_COND_V(tex_pos.index < 0, Glyph());

	//fit character in char texture

	GlyphTexture &tex = texture_cache[tex_pos.index];

	{
		PoolByteArray::Write wr = tex.imgdata.write();

		for (int i = 0; i < h; i++) {
			for (int j = 0; j < w; j++) {

				int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
				ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Glyph());
				switch (p_bitmap.pixel_mode) {
					case FT_PIXEL_MODE_MONO: {
						int byte = i * p_bitmap.pitch + (j >> 3);
						int bit = 1 << (7 - (j % 8));
						wr[ofs + 0] = 255; //grayscale as 1
						wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0;
					} break;
					case FT_PIXEL_MODE_GRAY:
						wr[ofs + 0] = 255; //grayscale as 1
						wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j];
						break;
					case FT_PIXEL_MODE_BGRA: {
						int ofs_color = i * p_bitmap.pitch + (j << 2);
						wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
						wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
						wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];
						wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3];
					} break;
					// TODO: FT_PIXEL_MODE_LCD
					default:
						ERR_PRINTS("Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode));
						ERR_FAIL_V(Glyph());
						break;
				}
			}
		}
	}

	//blit to image and texture
	{

#ifdef GODOT_MODULE
		Ref<Image> image = memnew(Image(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata));
#else
		Ref<Image> image;
		image.instance();
		image->create_from_data(tex.texture_size, tex.texture_size, 0, require_format, tex.imgdata);
#endif
		tex.image->create_from_image(image, txt_flags);
	}

	// update height array

	for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
		tex.offsets[k] = tex_pos.y + mh;
	}

	Glyph gl;
	gl.align = Point2(p_xofs * scale_color_font / oversampling, ascent - p_yofs * scale_color_font / oversampling);
	gl.id = tex_pos.index;
	gl.valid = true;
	gl.uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
	gl.size = gl.uv.size / oversampling;

	return gl;
}

void TLDynamicFontFaceAtSize::update_glyph(uint32_t p_codepoint) {

	if (loaded) {
		if (glyph_cache.count(p_codepoint) > 0)
			return;

		int ft_hinting;

		switch (hinting) {
			case DF_HINTING_NONE:
				ft_hinting = FT_LOAD_NO_HINTING;
				break;
			case DF_HINTING_LIGHT:
				ft_hinting = FT_LOAD_TARGET_LIGHT;
				break;
			default:
				ft_hinting = FT_LOAD_TARGET_NORMAL;
				break;
		}

		FT_GlyphSlot slot = ft_face->glyph;
		if (FT_Load_Glyph(ft_face, p_codepoint, FT_HAS_COLOR(ft_face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting) != 0) {
			return;
		}
		if (FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_NORMAL) != 0) {
			return;
		}
		glyph_cache[p_codepoint] = bitmap_to_glyph(slot->bitmap, slot->bitmap_top, slot->bitmap_left);
	}
}

void TLDynamicFontFaceAtSize::update_outline_glyph(uint32_t p_codepoint) {

	if (loaded) {
		if (glyph_outline_cache.count(p_codepoint) > 0)
			return;

		int ft_hinting;

		switch (hinting) {
			case DF_HINTING_NONE:
				ft_hinting = FT_LOAD_NO_HINTING;
				break;
			case DF_HINTING_LIGHT:
				ft_hinting = FT_LOAD_TARGET_LIGHT;
				break;
			default:
				ft_hinting = FT_LOAD_TARGET_NORMAL;
				break;
		}

		if (FT_Load_Glyph(ft_face, p_codepoint, FT_LOAD_NO_BITMAP | (force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting) != 0)
			return;

		FT_Stroker ft_stroker;
		if (FT_Stroker_New(ft_library, &ft_stroker) != 0)
			return;

		FT_Stroker_Set(ft_stroker, ft_size * oversampling, FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
		FT_Glyph ft_glyph;
		FT_BitmapGlyph ft_glyph_bitmap;

		if (FT_Get_Glyph(ft_face->glyph, &ft_glyph) != 0)
			goto cleanup_stroker;
		if (FT_Glyph_Stroke(&ft_glyph, ft_stroker, 1) != 0)
			goto cleanup_glyph;
		if (FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1) != 0)
			goto cleanup_glyph;

		ft_glyph_bitmap = (FT_BitmapGlyph)ft_glyph;
		glyph_outline_cache[p_codepoint] = bitmap_to_glyph(ft_glyph_bitmap->bitmap, ft_glyph_bitmap->top, ft_glyph_bitmap->left);

	cleanup_glyph:
		FT_Done_Glyph(ft_glyph);
	cleanup_stroker:
		FT_Stroker_Done(ft_stroker);
	}
}

hb_font_t *TLDynamicFontFaceAtSize::get_hb_font() const {

	return loaded ? hb_font : NULL;
}

bool TLDynamicFontFaceAtSize::load(String p_resource_path, int p_size, const uint8_t *p_font_mem, int p_font_mem_size) {

	if (loaded) {
		//unload existing
		clear_cache();
		if (hb_font) {
			hb_font_destroy(hb_font);
			hb_font = NULL;
		}
		FT_Done_FreeType(ft_library);

		loaded = false;
	}

	if (p_size <= 0)
		p_size = 1;

	if (FT_Init_FreeType(&ft_library) != 0) {
		ERR_PRINTS("Error initializing FreeType library");
		clear_cache();
		return false;
	}

	memset(&ft_stream, 0, sizeof(FT_StreamRec));
	FT_Open_Args ft_args;
	memset(&ft_args, 0, sizeof(FT_Open_Args));

	if (p_font_mem != NULL && p_font_mem_size != 0) {
		ft_stream.base = (unsigned char *)p_font_mem;
		ft_stream.size = p_font_mem_size;
		ft_stream.pos = 0;

		ft_args.memory_base = (unsigned char *)p_font_mem;
		ft_args.memory_size = p_font_mem_size;
		ft_args.flags = FT_OPEN_MEMORY;
		ft_args.stream = &ft_stream;
	} else {
		File *file = memnew(File);
		if (file->open(p_resource_path, File::READ) != Error::OK) {
			ERR_PRINTS("Can't open dynamic font file: \"" + p_resource_path + "\"");
			clear_cache();
			FT_Done_FreeType(ft_library);
			return false;
		}

		ft_stream.base = NULL;
		ft_stream.size = file->get_len();
		ft_stream.pos = 0;
		ft_stream.descriptor.pointer = file;
		ft_stream.read = ft_stream_io;
		ft_stream.close = ft_stream_close;

		ft_args.flags = FT_OPEN_STREAM;
		ft_args.stream = &ft_stream;
	}

	if (FT_Open_Face(ft_library, &ft_args, 0, &ft_face) != 0) {
		ERR_PRINTS("Error loading dynamic font: \"" + p_resource_path + "\" - FreeType uninitialized");
		clear_cache();
		FT_Done_FreeType(ft_library);
		return false;
	}

	ft_size = p_size;

	if (FT_HAS_COLOR(ft_face)) {
		int best_match = 0;
		int diff = abs(p_size - ft_face->available_sizes[0].width);
		scale_color_font = (float)p_size / ft_face->available_sizes[0].width;
		for (int i = 1; i < ft_face->num_fixed_sizes; i++) {
			int ndiff = abs(p_size - ft_face->available_sizes[i].width);
			if (ndiff < diff) {
				best_match = i;
				diff = ndiff;
				scale_color_font = (float)p_size / ft_face->available_sizes[i].width;
			}
		}
		FT_Select_Size(ft_face, best_match);
		ascent = (ft_face->size->metrics.ascender / 64.0f) * scale_color_font;
		descent = (-ft_face->size->metrics.descender / 64.0f) * scale_color_font;
		height = (ft_face->size->metrics.height / 64.0f) * scale_color_font;
	} else {
		scale_color_font = 1.0f;
		FT_Set_Pixel_Sizes(ft_face, 0, p_size * oversampling);
		ascent = (ft_face->size->metrics.ascender / 64.0f) / oversampling;
		descent = (-ft_face->size->metrics.descender / 64.0f) / oversampling;
		height = (ft_face->size->metrics.height / 64.0f) / oversampling;
	}

	FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE);

	hb_font = hb_ft_font_create(ft_face, NULL);
	if (!hb_font) {
		ERR_PRINTS("Error loading dynamic font: \"" + p_resource_path + "\" - HarfBuzz uninitialized");
		clear_cache();
		FT_Done_FreeType(ft_library);
		return false;
	}

	//Load os2 TTF pable
	os2 = (TT_OS2 *)FT_Get_Sfnt_Table(ft_face, FT_SFNT_OS2);

	loaded = true;

	return loaded;
}

std::vector<hb_script_t> TLDynamicFontFaceAtSize::unicode_scripts_supported() const {
	//https://freetype.org/freetype2/docs/reference/ft2-truetype_tables.html#tt_ucr_xxx

	std::vector<hb_script_t> ret;
	if (!os2)
		return ret;

	if ((os2->ulUnicodeRange1 & 1L << 0) || (os2->ulUnicodeRange1 & 1L << 1) || (os2->ulUnicodeRange1 & 1L << 2) || (os2->ulUnicodeRange1 & 1L << 3) || (os2->ulUnicodeRange1 & 1L << 29)) {
		ret.push_back(HB_SCRIPT_LATIN);
	}
	if ((os2->ulUnicodeRange1 & 1L << 4) || (os2->ulUnicodeRange1 & 1L << 5) || (os2->ulUnicodeRange1 & 1L << 6) || (os2->ulUnicodeRange1 & 1L << 31) || (os2->ulUnicodeRange2 & 1L << 0) || (os2->ulUnicodeRange2 & 1L << 1) || (os2->ulUnicodeRange2 & 1L << 2) || (os2->ulUnicodeRange2 & 1L << 3) || (os2->ulUnicodeRange2 & 1L << 4) || (os2->ulUnicodeRange2 & 1L << 5) || (os2->ulUnicodeRange2 & 1L << 6) || (os2->ulUnicodeRange2 & 1L << 7) || (os2->ulUnicodeRange2 & 1L << 8) || (os2->ulUnicodeRange2 & 1L << 9) || (os2->ulUnicodeRange2 & 1L << 10) || (os2->ulUnicodeRange2 & 1L << 11) || (os2->ulUnicodeRange2 & 1L << 12) || (os2->ulUnicodeRange2 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 14) || (os2->ulUnicodeRange2 & 1L << 15) || (os2->ulUnicodeRange2 & 1L << 30) || (os2->ulUnicodeRange3 & 1L << 0) || (os2->ulUnicodeRange3 & 1L << 1) || (os2->ulUnicodeRange3 & 1L << 2) || (os2->ulUnicodeRange3 & 1L << 4) || (os2->ulUnicodeRange3 & 1L << 5) || (os2->ulUnicodeRange3 & 1L << 18) || (os2->ulUnicodeRange3 & 1L << 24) || (os2->ulUnicodeRange3 & 1L << 25) || (os2->ulUnicodeRange3 & 1L << 26) || (os2->ulUnicodeRange3 & 1L << 27) || (os2->ulUnicodeRange3 & 1L << 28) || (os2->ulUnicodeRange4 & 1L << 3) || (os2->ulUnicodeRange4 & 1L << 6) || (os2->ulUnicodeRange4 & 1L << 15) || (os2->ulUnicodeRange4 & 1L << 23) || (os2->ulUnicodeRange4 & 1L << 24) || (os2->ulUnicodeRange4 & 1L << 26)) {
		//TODO: add more script sorting
		ret.push_back(HB_SCRIPT_COMMON);
	}
	if ((os2->ulUnicodeRange1 & 1L << 7) || (os2->ulUnicodeRange1 & 1L << 30)) {
		ret.push_back(HB_SCRIPT_GREEK);
	}
	if ((os2->ulUnicodeRange1 & 1L << 8)) {
		ret.push_back(HB_SCRIPT_COPTIC);
	}
	if ((os2->ulUnicodeRange1 & 1L << 9)) {
		ret.push_back(HB_SCRIPT_CYRILLIC);
	}
	if ((os2->ulUnicodeRange1 & 1L << 10)) {
		ret.push_back(HB_SCRIPT_ARMENIAN);
	}
	if ((os2->ulUnicodeRange1 & 1L << 11)) {
		ret.push_back(HB_SCRIPT_HEBREW);
	}
	if ((os2->ulUnicodeRange1 & 1L << 12)) {
		ret.push_back(HB_SCRIPT_VAI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 31) || (os2->ulUnicodeRange3 & 1L << 3)) {
		ret.push_back(HB_SCRIPT_ARABIC);
	}
	if ((os2->ulUnicodeRange1 & 1L << 14)) {
		ret.push_back(HB_SCRIPT_NKO);
	}
	if ((os2->ulUnicodeRange1 & 1L << 15)) {
		ret.push_back(HB_SCRIPT_DEVANAGARI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 16)) {
		ret.push_back(HB_SCRIPT_BENGALI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 17)) {
		ret.push_back(HB_SCRIPT_GURMUKHI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 18)) {
		ret.push_back(HB_SCRIPT_GUJARATI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 19)) {
		ret.push_back(HB_SCRIPT_ORIYA);
	}
	if ((os2->ulUnicodeRange1 & 1L << 20)) {
		ret.push_back(HB_SCRIPT_TAMIL);
	}
	if ((os2->ulUnicodeRange1 & 1L << 21)) {
		ret.push_back(HB_SCRIPT_TELUGU);
	}
	if ((os2->ulUnicodeRange1 & 1L << 22)) {
		ret.push_back(HB_SCRIPT_KANNADA);
	}
	if ((os2->ulUnicodeRange1 & 1L << 23)) {
		ret.push_back(HB_SCRIPT_MALAYALAM);
	}
	if ((os2->ulUnicodeRange1 & 1L << 24)) {
		ret.push_back(HB_SCRIPT_THAI);
	}
	if ((os2->ulUnicodeRange1 & 1L << 25)) {
		ret.push_back(HB_SCRIPT_LAO);
	}
	if ((os2->ulUnicodeRange1 & 1L << 26)) {
		ret.push_back(HB_SCRIPT_GEORGIAN);
	}
	if ((os2->ulUnicodeRange1 & 1L << 27)) {
		ret.push_back(HB_SCRIPT_BALINESE);
	}
	if ((os2->ulUnicodeRange1 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 24)) {
		//HANG and JAMO
		ret.push_back(HB_SCRIPT_HANGUL);
	}
	if ((os2->ulUnicodeRange2 & 1L << 16) || (os2->ulUnicodeRange2 & 1L << 21) || (os2->ulUnicodeRange2 & 1L << 22) || (os2->ulUnicodeRange2 & 1L << 23) || (os2->ulUnicodeRange2 & 1L << 26) || (os2->ulUnicodeRange2 & 1L << 27) || (os2->ulUnicodeRange2 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 29)) {
		ret.push_back(HB_SCRIPT_HAN);
	}
	if ((os2->ulUnicodeRange2 & 1L << 17)) {
		ret.push_back(HB_SCRIPT_HIRAGANA);
	}
	if ((os2->ulUnicodeRange2 & 1L << 18)) {
		ret.push_back(HB_SCRIPT_KATAKANA);
	}
	if ((os2->ulUnicodeRange2 & 1L << 19)) {
		ret.push_back(HB_SCRIPT_BOPOMOFO);
	}
	//Bit 25 - Surrogates - Ignore
	if ((os2->ulUnicodeRange3 & 1L << 6)) {
		ret.push_back(HB_SCRIPT_TIBETAN);
	}
	if ((os2->ulUnicodeRange3 & 1L << 7)) {
		ret.push_back(HB_SCRIPT_SYRIAC);
	}
	if ((os2->ulUnicodeRange3 & 1L << 8)) {
		ret.push_back(HB_SCRIPT_THAANA);
	}
	if ((os2->ulUnicodeRange3 & 1L << 9)) {
		ret.push_back(HB_SCRIPT_SINHALA);
	}
	if ((os2->ulUnicodeRange3 & 1L << 10)) {
		ret.push_back(HB_SCRIPT_MYANMAR);
	}
	if ((os2->ulUnicodeRange3 & 1L << 11)) {
		ret.push_back(HB_SCRIPT_ETHIOPIC);
	}
	if ((os2->ulUnicodeRange3 & 1L << 12)) {
		ret.push_back(HB_SCRIPT_CHEROKEE);
	}
	if ((os2->ulUnicodeRange3 & 1L << 13)) {
		ret.push_back(HB_SCRIPT_CANADIAN_SYLLABICS);
	}
	if ((os2->ulUnicodeRange3 & 1L << 14)) {
		ret.push_back(HB_SCRIPT_OGHAM);
	}
	if ((os2->ulUnicodeRange3 & 1L << 15)) {
		ret.push_back(HB_SCRIPT_RUNIC);
	}
	if ((os2->ulUnicodeRange3 & 1L << 16)) {
		ret.push_back(HB_SCRIPT_KHMER);
	}
	if ((os2->ulUnicodeRange3 & 1L << 17)) {
		ret.push_back(HB_SCRIPT_MONGOLIAN);
	}
	if ((os2->ulUnicodeRange3 & 1L << 19)) {
		ret.push_back(HB_SCRIPT_YI);
	}
	if ((os2->ulUnicodeRange3 & 1L << 20)) {
		ret.push_back(HB_SCRIPT_TAGALOG);
		ret.push_back(HB_SCRIPT_HANUNOO);
		ret.push_back(HB_SCRIPT_TAGBANWA);
		ret.push_back(HB_SCRIPT_BUHID);
	}
	if ((os2->ulUnicodeRange3 & 1L << 21)) {
		ret.push_back(HB_SCRIPT_OLD_ITALIC);
	}
	if ((os2->ulUnicodeRange3 & 1L << 22)) {
		ret.push_back(HB_SCRIPT_GOTHIC);
	}
	if ((os2->ulUnicodeRange3 & 1L << 23)) {
		ret.push_back(HB_SCRIPT_DESERET);
	}
	if ((os2->ulUnicodeRange3 & 1L << 29)) {
		ret.push_back(HB_SCRIPT_LIMBU);
	}
	if ((os2->ulUnicodeRange3 & 1L << 30)) {
		ret.push_back(HB_SCRIPT_TAI_LE);
	}
	if ((os2->ulUnicodeRange3 & 1L << 31)) {
		ret.push_back(HB_SCRIPT_NEW_TAI_LUE);
	}
	if ((os2->ulUnicodeRange4 & 1L << 0)) {
		ret.push_back(HB_SCRIPT_BUGINESE);
	}
	if ((os2->ulUnicodeRange4 & 1L << 1)) {
		ret.push_back(HB_SCRIPT_GLAGOLITIC);
	}
	if ((os2->ulUnicodeRange4 & 1L << 2)) {
		ret.push_back(HB_SCRIPT_TIFINAGH);
	}
	if ((os2->ulUnicodeRange4 & 1L << 4)) {
		ret.push_back(HB_SCRIPT_SYLOTI_NAGRI);
	}
	if ((os2->ulUnicodeRange4 & 1L << 5)) {
		ret.push_back(HB_SCRIPT_LINEAR_B);
	}
	if ((os2->ulUnicodeRange4 & 1L << 7)) {
		ret.push_back(HB_SCRIPT_UGARITIC);
	}
	if ((os2->ulUnicodeRange4 & 1L << 8)) {
		ret.push_back(HB_SCRIPT_OLD_PERSIAN);
	}
	if ((os2->ulUnicodeRange4 & 1L << 9)) {
		ret.push_back(HB_SCRIPT_SHAVIAN);
	}
	if ((os2->ulUnicodeRange4 & 1L << 10)) {
		ret.push_back(HB_SCRIPT_OSMANYA);
	}
	if ((os2->ulUnicodeRange4 & 1L << 11)) {
		ret.push_back(HB_SCRIPT_CYPRIOT);
	}
	if ((os2->ulUnicodeRange4 & 1L << 12)) {
		ret.push_back(HB_SCRIPT_KHAROSHTHI);
	}
	if ((os2->ulUnicodeRange4 & 1L << 13)) {
		ret.push_back(HB_SCRIPT_TAI_VIET);
	}
	if ((os2->ulUnicodeRange4 & 1L << 14)) {
		ret.push_back(HB_SCRIPT_CUNEIFORM);
	}
	if ((os2->ulUnicodeRange4 & 1L << 16)) {
		ret.push_back(HB_SCRIPT_SUNDANESE);
	}
	if ((os2->ulUnicodeRange4 & 1L << 17)) {
		ret.push_back(HB_SCRIPT_LEPCHA);
	}
	if ((os2->ulUnicodeRange4 & 1L << 18)) {
		ret.push_back(HB_SCRIPT_OL_CHIKI);
	}
	if ((os2->ulUnicodeRange4 & 1L << 19)) {
		ret.push_back(HB_SCRIPT_SAURASHTRA);
	}
	if ((os2->ulUnicodeRange4 & 1L << 20)) {
		ret.push_back(HB_SCRIPT_KAYAH_LI);
	}
	if ((os2->ulUnicodeRange4 & 1L << 21)) {
		ret.push_back(HB_SCRIPT_REJANG);
	}
	if ((os2->ulUnicodeRange4 & 1L << 22)) {
		ret.push_back(HB_SCRIPT_CHAM);
	}
	if ((os2->ulUnicodeRange4 & 1L << 25)) {
		ret.push_back(HB_SCRIPT_ANATOLIAN_HIEROGLYPHS);
	}
	if (ret.size() == 0) {
		ret.push_back(HB_SCRIPT_COMMON);
	}
	//Bits 27...31 - Reserved

	return ret;
}

double TLDynamicFontFaceAtSize::get_ascent() const {

	return loaded ? ascent : 0.0f;
}

double TLDynamicFontFaceAtSize::get_descent() const {

	return loaded ? descent : 0.0f;
}

double TLDynamicFontFaceAtSize::get_height() const {

	return loaded ? height : 0.0f;
}

void TLDynamicFontFaceAtSize::set_texture_flags(int p_flags) {

	if (txt_flags != p_flags) {
		txt_flags = p_flags;
		if (loaded) {
			for (size_t i = 0; i < texture_cache.size(); i++) {
				Ref<ImageTexture> &tex = texture_cache[i].image;
				if (!tex.is_null())
					tex->set_flags(txt_flags);
			}
		}
	}
}

void TLDynamicFontFaceAtSize::set_hinting(int p_hinting) {

	if (hinting != p_hinting) {
		hinting = (DynamicFaceHinting)p_hinting;
		if (loaded) {
			clear_cache();
		}
	}
}

void TLDynamicFontFaceAtSize::set_force_autohinter(bool p_force) {

	if (force_autohinter != p_force) {
		force_autohinter = p_force;
		if (loaded) {
			clear_cache();
		}
	}
}

void TLDynamicFontFaceAtSize::set_oversampling(float p_oversampling) {

	if (oversampling != p_oversampling) {
		oversampling = p_oversampling;
		if (loaded) {
			clear_cache();
		}
	}
}

/*************************************************************************/
/*  TLDynamicFontFace                                                     */
/*************************************************************************/

TLDynamicFontFace::TLDynamicFontFace() {

#ifdef GODOT_MODULE
	_init();
#endif
}

void TLDynamicFontFace::_init() {

	hinting = DF_HINTING_NORMAL;
	force_autohinter = false;
	txt_flags = Texture::FLAG_VIDEO_SURFACE;
	oversampling = 1.0f;
	font_mem = NULL;
	font_mem_size = 0;
}

TLDynamicFontFace::~TLDynamicFontFace() {

	for (auto it = sizes.begin(); it != sizes.end(); ++it) {
		delete it->second;
	}
	sizes.clear();
}

hb_font_t *TLDynamicFontFace::get_hb_font(int p_size) const {

	if (p_size < 1) return NULL;

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_hb_font();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_hb_font();
		}
	}
	return NULL;
}

float TLDynamicFontFace::get_glyph_scale(int p_size) const {

	if (p_size < 1) return 1.0f;

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_glyph_scale();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_glyph_scale();
		}
	}
	return 1.0f;
}

void TLDynamicFontFace::draw_glyph(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	if (p_size < 1) return;

	if (sizes.count(p_size) > 0) {
		sizes.at(p_size)->draw_glyph(p_canvas_item, p_pos, p_codepoint, p_modulate);
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			f_at_s->draw_glyph(p_canvas_item, p_pos, p_codepoint, p_modulate);
		}
	}
}

void TLDynamicFontFace::_draw_char(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	if (p_size < 1) return;

	//raw char for debug only, do not use
	if (sizes.count(p_size) > 0) {
		sizes.at(p_size)->_draw_char(p_canvas_item, p_pos, p_codepoint, p_modulate);
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			f_at_s->_draw_char(p_canvas_item, p_pos, p_codepoint, p_modulate);
		}
	}
}

void TLDynamicFontFace::draw_glyph_outline(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	if (p_size < 1) return;

	if (sizes.count(p_size) > 0) {
		sizes.at(p_size)->draw_glyph_outline(p_canvas_item, p_pos, p_codepoint, p_modulate);
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			f_at_s->draw_glyph_outline(p_canvas_item, p_pos, p_codepoint, p_modulate);
		}
	}
}

Array TLDynamicFontFace::get_glyph_outline(const Point2 p_pos, uint32_t p_codepoint, int p_size) const {

	if (p_size < 1) return Array();

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_glyph_outline(p_pos, p_codepoint);
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_glyph_outline(p_pos, p_codepoint);
		}
	}
	return Array();
}

void TLDynamicFontFace::set_font_path(String p_resource_path) {

	if (path != p_resource_path) {
		load(p_resource_path);
	}
}

void TLDynamicFontFace::set_font_ptr(const uint8_t *p_font_mem, int p_font_mem_size) {

	if (p_font_mem != font_mem || p_font_mem_size != font_mem_size) {
		path = "[Built-in data]";
		font_mem = p_font_mem;
		font_mem_size = p_font_mem_size;
		for (auto it = sizes.begin(); it != sizes.end(); ++it) {
			delete it->second;
		}
		sizes.clear();
		emit_signal(_CHANGED);
	}
}

bool TLDynamicFontFace::load(String p_resource_path) {

	path = p_resource_path;
	font_mem = NULL;
	font_mem_size = 0;
	for (auto it = sizes.begin(); it != sizes.end(); ++it) {
		delete it->second;
	}
	sizes.clear();
	emit_signal(_CHANGED);
	return true;
}

std::vector<hb_script_t> TLDynamicFontFace::unicode_scripts_supported() const {

	if (sizes.size() > 0) {
		return sizes.begin()->second->unicode_scripts_supported();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, 16, font_mem, font_mem_size)) {
			sizes[16] = f_at_s;
			return f_at_s->unicode_scripts_supported();
		}
	}
	WARN_PRINTS("Font not loaded!");
	return std::vector<hb_script_t>();
}

double TLDynamicFontFace::get_ascent(int p_size) const {

	if (p_size < 1) return 0.0f;

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_ascent();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_ascent();
		}
	}
	WARN_PRINTS("Font not loaded!");
	return 0.0f;
}

double TLDynamicFontFace::get_descent(int p_size) const {

	if (p_size < 1) return 0.0f;

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_descent();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_descent();
		}
	}
	WARN_PRINTS("Font not loaded!");
	return 0.0f;
}

double TLDynamicFontFace::get_height(int p_size) const {

	if (p_size < 1) return 0.0f;

	if (sizes.count(p_size) > 0) {
		return sizes.at(p_size)->get_height();
	} else {
		TLDynamicFontFaceAtSize *f_at_s = new TLDynamicFontFaceAtSize();
		f_at_s->set_texture_flags(txt_flags);
		f_at_s->set_hinting(hinting);
		f_at_s->set_force_autohinter(force_autohinter);
		f_at_s->set_oversampling(oversampling);

		if (f_at_s->load(path, p_size, font_mem, font_mem_size)) {
			sizes[p_size] = f_at_s;
			return f_at_s->get_height();
		}
	}
	WARN_PRINTS("Font not loaded!");
	return 0.0f;
}

void TLDynamicFontFace::set_texture_flags(int p_flags) {

	if (txt_flags != p_flags) {
		txt_flags = p_flags;
		for (auto it = sizes.begin(); it != sizes.end(); ++it) {
			it->second->set_texture_flags(p_flags);
		}
		emit_signal(_CHANGED);
	}
}

int TLDynamicFontFace::get_texture_flags() const {

	return txt_flags;
}

void TLDynamicFontFace::set_hinting(int p_hinting) {

	if (hinting != (DynamicFaceHinting)p_hinting) {
		hinting = (DynamicFaceHinting)p_hinting;
		for (auto it = sizes.begin(); it != sizes.end(); ++it) {
			it->second->set_hinting(p_hinting);
		}
		emit_signal(_CHANGED);
	}
}

int TLDynamicFontFace::get_hinting() const {

	return hinting;
}

void TLDynamicFontFace::set_force_autohinter(bool p_force) {

	if (force_autohinter != p_force) {
		force_autohinter = p_force;
		for (auto it = sizes.begin(); it != sizes.end(); ++it) {
			it->second->set_force_autohinter(p_force);
		}
		emit_signal(_CHANGED);
	}
}

bool TLDynamicFontFace::get_force_autohinter() const {

	return force_autohinter;
}

void TLDynamicFontFace::set_oversampling(float p_oversampling) {

	if (oversampling != p_oversampling) {
		oversampling = p_oversampling;
		for (auto it = sizes.begin(); it != sizes.end(); ++it) {
			it->second->set_oversampling(p_oversampling);
		}
		emit_signal(_CHANGED);
	}
}

float TLDynamicFontFace::get_oversampling() const {

	return oversampling;
}

bool TLDynamicFontFace::has_graphite() const {

#ifdef HAVE_GRAPHITE2
	return true;
#else
	return false;
#endif
}

#ifdef GODOT_MODULE
void TLDynamicFontFace::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &TLDynamicFontFace::set_force_autohinter);
	ClassDB::bind_method(D_METHOD("get_force_autohinter"), &TLDynamicFontFace::get_force_autohinter);

	ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &TLDynamicFontFace::set_hinting);
	ClassDB::bind_method(D_METHOD("get_hinting"), &TLDynamicFontFace::get_hinting);

	ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &TLDynamicFontFace::set_oversampling);
	ClassDB::bind_method(D_METHOD("get_oversampling"), &TLDynamicFontFace::get_oversampling);

	ClassDB::bind_method(D_METHOD("has_graphite"), &TLDynamicFontFace::has_graphite);

	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "get_force_autohinter");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "oversampling"), "set_oversampling", "get_oversampling");

	BIND_ENUM_CONSTANT(DF_HINTING_NONE);
	BIND_ENUM_CONSTANT(DF_HINTING_LIGHT);
	BIND_ENUM_CONSTANT(DF_HINTING_NORMAL);
}
#else
void TLDynamicFontFace::_register_methods() {

	register_method("set_force_autohinter", &TLDynamicFontFace::set_force_autohinter);
	register_method("get_force_autohinter", &TLDynamicFontFace::get_force_autohinter);

	register_method("set_hinting", &TLDynamicFontFace::set_hinting);
	register_method("get_hinting", &TLDynamicFontFace::get_hinting);

	register_method("set_oversampling", &TLDynamicFontFace::set_oversampling);
	register_method("get_oversampling", &TLDynamicFontFace::get_oversampling);

	register_method("has_graphite", &TLDynamicFontFace::has_graphite);

	register_property<TLDynamicFontFace, bool>("force_autohinter", &TLDynamicFontFace::set_force_autohinter, &TLDynamicFontFace::get_force_autohinter, false);
	register_property<TLDynamicFontFace, int>("hinting", &TLDynamicFontFace::set_hinting, &TLDynamicFontFace::get_hinting, DF_HINTING_NORMAL, GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_ENUM, String("None,Light,Normal"));
	register_property<TLDynamicFontFace, float>("oversampling", &TLDynamicFontFace::set_oversampling, &TLDynamicFontFace::get_oversampling, 1.0f);
}

#endif

repo_language: C

mime_type: text/x-c


text:
/*************************************************************************/
/*  tl_icu_data_loader.cpp                                               */
/*************************************************************************/

#include "resources/tl_icu_data_loader.hpp"

#ifdef GODOT_MODULE
#include "core/bind/core_bind.h"
#define File _File
#else
#include <ArrayMesh.hpp>
#include <File.hpp>
#include <Mesh.hpp>
#include <SurfaceTool.hpp>
#endif

uint8_t *TLICUDataLoader::icu_data = NULL;

TLICUDataLoader::TLICUDataLoader() {

#ifdef GODOT_MODULE
	_init();
#endif
}

void TLICUDataLoader::_init() {

	//NOP
}

TLICUDataLoader::~TLICUDataLoader() {

	//NOP
}

bool TLICUDataLoader::load(String p_path) {

	if (!icu_data) {
		Ref<File> file;
		file.instance();
		if (file->open(p_path, File::READ) != Error::OK) {
			ERR_PRINTS("Can't open ICU data file: \"" + p_path + "\"");
			return false;
		}

		UErrorCode err = U_ZERO_ERROR;

		//ICU data found
		size_t len = file->get_len();
		PoolByteArray icu_pool = file->get_buffer(len);
		file->close();

		icu_data = (uint8_t *)std::malloc(len);
		if (!icu_data)
			return false;
		std::memcpy(icu_data, icu_pool.read().ptr(), len);

		udata_setCommonData(icu_data, &err);
		if (U_FAILURE(err)) {
			ERR_PRINTS(u_errorName(err));
			std::free(icu_data);
			icu_data = NULL;
			return false;
		}

		err = U_ZERO_ERROR;
		u_init(&err);
		if (U_FAILURE(err)) {
			ERR_PRINTS(u_errorName(err));
			std::free(icu_data);
			icu_data = NULL;
			return false;
		}
	}

	return true;
}

void TLICUDataLoader::unload() {

	if (icu_data) {
		std::free(icu_data);
		icu_data = NULL;
	}
}

void TLICUDataLoader::set_data_path(String p_resource_path) {

	path = p_resource_path;
	load(path);
}

String TLICUDataLoader::get_data_path() const {

	return path;
}

#ifdef GODOT_MODULE
void TLICUDataLoader::_bind_methods() {

	ClassDB::bind_method(D_METHOD("load", "resource_path"), &TLICUDataLoader::load);
	ClassDB::bind_method(D_METHOD("get_data_path"), &TLICUDataLoader::get_data_path);
	ClassDB::bind_method(D_METHOD("set_data_path", "path"), &TLICUDataLoader::set_data_path);

	ADD_PROPERTY(PropertyInfo(Variant::STRING, "data_path", PROPERTY_HINT_FILE, "*.dat"), "set_data_path", "get_data_path");
}
#else
void TLICUDataLoader::_register_methods() {

	register_method("load", &TLICUDataLoader::load);
	register_method("get_data_path", &TLICUDataLoader::get_data_path);
	register_method("set_data_path", &TLICUDataLoader::set_data_path);

	register_property<TLICUDataLoader, String>("data_path", &TLICUDataLoader::set_data_path, &TLICUDataLoader::get_data_path, String(), GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_FILE, String("*.dat"));
}
#endif


text:
/*************************************************************************/
/*  tl_gd_font_wrapper.cpp                                               */
/*************************************************************************/

#include "tl_gd_font_wrapper.hpp"

#ifdef GODOT_MODULE
void TLGDFontWrapper::_bind_methods() {
	ClassDB::bind_method(D_METHOD("set_base_font", "ref"), &TLGDFontWrapper::set_base_font);
	ClassDB::bind_method(D_METHOD("get_base_font"), &TLGDFontWrapper::get_base_font);

	ClassDB::bind_method(D_METHOD("set_base_font_style", "style"), &TLGDFontWrapper::set_base_font_style);
	ClassDB::bind_method(D_METHOD("get_base_font_style"), &TLGDFontWrapper::get_base_font_style);

	ClassDB::bind_method(D_METHOD("set_base_font_size", "size"), &TLGDFontWrapper::set_base_font_size);
	ClassDB::bind_method(D_METHOD("get_base_font_size"), &TLGDFontWrapper::get_base_font_size);

	ClassDB::bind_method(D_METHOD("set_cache_depth", "cache_depth"), &TLGDFontWrapper::set_cache_depth);
	ClassDB::bind_method(D_METHOD("get_cache_depth"), &TLGDFontWrapper::get_cache_depth);

	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "TLFontFamily"), "set_base_font", "get_base_font");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "base_font_size"), "set_base_font_size", "get_base_font_size");
	ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_font_style"), "set_base_font_style", "get_base_font_style");

	ADD_PROPERTY(PropertyInfo(Variant::INT, "cache_depth"), "set_cache_depth", "get_cache_depth");
}
#else
void TLGDFontWrapper::_register_methods() {
	register_method("set_base_font", &TLGDFontWrapper::set_base_font);
	register_method("get_base_font", &TLGDFontWrapper::get_base_font);

	register_method("set_base_font_style", &TLGDFontWrapper::set_base_font_style);
	register_method("get_base_font_style", &TLGDFontWrapper::get_base_font_style);

	register_method("set_base_font_size", &TLGDFontWrapper::set_base_font_size);
	register_method("get_base_font_size", &TLGDFontWrapper::get_base_font_size);

	register_method("set_cache_depth", &TLGDFontWrapper::set_cache_depth);
	register_method("get_cache_depth", &TLGDFontWrapper::get_cache_depth);

	register_property<TLGDFontWrapper, Ref<TLFontFamily> >("base_font", &TLGDFontWrapper::set_base_font, &TLGDFontWrapper::get_base_font, Ref<TLFontFamily>(), GODOT_METHOD_RPC_MODE_DISABLED, (godot_property_usage_flags)(GODOT_PROPERTY_USAGE_NOEDITOR | GODOT_PROPERTY_USAGE_STORAGE), GODOT_PROPERTY_HINT_RESOURCE_TYPE, String("TLFontFamily"));
	register_property<TLGDFontWrapper, String>("base_font_style", &TLGDFontWrapper::set_base_font_style, &TLGDFontWrapper::get_base_font_style, String("Regular"));
	register_property<TLGDFontWrapper, int>("base_font_size", &TLGDFontWrapper::set_base_font_size, &TLGDFontWrapper::get_base_font_size, 12);

	register_property<TLGDFontWrapper, int>("cache_depth", &TLGDFontWrapper::set_cache_depth, &TLGDFontWrapper::get_cache_depth, 100);
}
#endif

Ref<TLFontFamily> TLGDFontWrapper::get_base_font() const {
	return family;
}

void TLGDFontWrapper::set_base_font(const Ref<TLFontFamily> p_font) {
	if (family != p_font) {
		family = p_font;
		sstr.clear();
		sids.clear();
	}
}

String TLGDFontWrapper::get_base_font_style() const {
	return fstyle;
}

void TLGDFontWrapper::set_base_font_style(const String p_style) {
	if (fstyle != p_style) {
		fstyle = p_style;
		sstr.clear();
		sids.clear();
	}
}

int TLGDFontWrapper::get_base_font_size() const {
	return fsize;
}

void TLGDFontWrapper::set_base_font_size(int p_size) {
	if (fsize != p_size) {
		fsize = p_size;
		sstr.clear();
		sids.clear();
	}
}

int TLGDFontWrapper::get_cache_depth() const {
	return scdepth;
}

void TLGDFontWrapper::set_cache_depth(int p_cache_depth) {
	if (scdepth != p_cache_depth) {
		scdepth = p_cache_depth;
		while (sids.size() > (size_t)scdepth) {
			sstr.erase(sstr.find(sids.front()));
			sids.pop_front();
		}
	}
}

float TLGDFontWrapper::get_height() const {
	if (family.is_valid()) {
		TLFontFallbackIterator font_iter = family->get_face(fstyle);
		if (font_iter.is_valid()) {
			Ref<TLFontFace> _font = font_iter.value();
			if (_font.is_null()) {
				return 0.0f;
			}
			return _font->get_height(fsize);
		}
	}
	return 15.0f;
}

float TLGDFontWrapper::get_ascent() const {
	if (family.is_valid()) {
		TLFontFallbackIterator font_iter = family->get_face(fstyle);
		if (font_iter.is_valid()) {
			Ref<TLFontFace> _font = font_iter.value();
			if (_font.is_null()) {
				return 0.0f;
			}
			return _font->get_ascent(fsize);
		}
	}
	return 10.0f;
}

float TLGDFontWrapper::get_descent() const {
	if (family.is_valid()) {
		TLFontFallbackIterator font_iter = family->get_face(fstyle);
		if (font_iter.is_valid()) {
			Ref<TLFontFace> _font = font_iter.value();
			if (_font.is_null()) {
				return 0.0f;
			}
			return _font->get_ascent(fsize);
		}
	}
	return 5.0f;
}

Size2 TLGDFontWrapper::get_char_size(CharType p_char, CharType p_next) const {
	CharType ucodestr[2] = { (CharType)p_char, 0 };
	return get_string_size(ucodestr);
}

Size2 TLGDFontWrapper::get_string_size(const String &p_text) const {
	Ref<TLShapedString> str;
	int64_t hash = p_text.hash();
	if (sstr.count(hash) > 0) {
		str = sstr[hash];
	} else {
		str.instance();
		str->set_base_font(family);
		str->set_base_font_style(fstyle);
		str->set_base_font_size(fsize);
		str->set_text(p_text);
		sids.push_back(hash);
		sstr[hash] = str;
		while (sids.size() > (size_t)scdepth) {
			sstr.erase(sstr.find(sids.front()));
			sids.pop_front();
		}
	}
	return Size2(str->get_width(), MAX(str->get_height(), get_height()));
}

void TLGDFontWrapper::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const {
	Ref<TLShapedString> str;
	int64_t hash = p_text.hash();
	if (sstr.count(hash) > 0) {
		str = sstr[hash];
	} else {
		str.instance();
		str->set_base_font(family);
		str->set_base_font_style(fstyle);
		str->set_base_font_size(fsize);
		str->set_text(p_text);
		sids.push_back(hash);
		sstr[hash] = str;
		while (sids.size() > (size_t)scdepth) {
			sstr.erase(sstr.find(sids.front()));
			sids.pop_front();
		}
	}
	float w = 0.f;
	for (int i = 0; i < str->clusters(); i++) {
		if (p_clip_w <= 0.f || (w + str->get_cluster_width(i)) <= p_clip_w) {
			w += str->draw_cluster(p_canvas_item, Point2(p_pos.x + w, p_pos.y), i, p_modulate).x;
		}
	}
}

float TLGDFontWrapper::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
	CharType ucodestr[2] = { (CharType)p_char, 0 };
	draw(p_canvas_item, p_pos, ucodestr, p_modulate);
	return get_string_size(ucodestr).width;
}

bool TLGDFontWrapper::is_distance_field_hint() const {
	return false;
}

void TLGDFontWrapper::_init() {
	fstyle = "Regular";
	fsize = 12;
	scdepth = 100;
}

TLGDFontWrapper::TLGDFontWrapper() {
#ifdef GODOT_MODULE
	_init();
#endif
}

TLGDFontWrapper::~TLGDFontWrapper() {}


text:
/*************************************************************************/
/*  tl_font.cpp                                                          */
/*************************************************************************/

#ifdef GODOT_MODULE
#include "servers/visual_server.h"
#else
#include <GlobalConstants.hpp>
#include <Image.hpp>
#include <Texture.hpp>
#include <VisualServer.hpp>
#endif

#include "tl_font.hpp"

Ref<ImageTexture> TLFontFace::hex_box_img_tex = NULL;

TLFontFace::TLFontFace() {

#ifdef GODOT_MODULE
	_init();
#endif
}

void TLFontFace::_init() {

	//NOP
}

TLFontFace::~TLFontFace() {

	//NOP
}

void TLFontFace::initialize_hex_font() {

	PoolByteArray hex_box_data;
	hex_box_data.resize(167);
	memcpy(hex_box_data.write().ptr(), _hex_box_img_data, 167);

	Ref<Image> image;
	image.instance();
	image->load_png_from_buffer(hex_box_data);

	hex_box_img_tex.instance();
	hex_box_img_tex->create_from_image(image, Texture::FLAG_VIDEO_SURFACE);
}

void TLFontFace::finish_hex_font() {

	hex_box_img_tex.unref();
}

void TLFontFace::_draw_small_int(RID p_canvas_item, const Point2 p_pos, uint32_t p_int, const Color p_modulate) {
	//for debug draw (4 dig)
	uint8_t a = p_int % 10000 / 1000;
	uint8_t b = p_int % 1000 / 100;
	uint8_t c = p_int % 100 / 10;
	uint8_t d = p_int % 10;

	Vector2 off = Vector2();
	if (a != 0) {
		Rect2 dest = Rect2(p_pos + off, Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(Point2(a * 5, 0), dest.size), p_modulate, false, RID(), false);
		off += Point2(6, 0);
	}
	if ((a != 0) || (b != 0)) {
		Rect2 dest = Rect2(p_pos + off, Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(Point2(b * 5, 0), dest.size), p_modulate, false, RID(), false);
		off += Point2(6, 0);
	}
	if ((a != 0) || (b != 0) || (c != 0)) {
		Rect2 dest = Rect2(p_pos + off, Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(Point2(c * 5, 0), dest.size), p_modulate, false, RID(), false);
		off += Point2(6, 0);
	}
	Rect2 dest = Rect2(p_pos + off, Size2(5, 7));
	VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(Point2(d * 5, 0), dest.size), p_modulate, false, RID(), false);
}

void TLFontFace::draw_hexbox(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate) {

	uint8_t a = p_codepoint & 0x0F;
	uint8_t b = (p_codepoint >> 4) & 0x0F;
	uint8_t c = (p_codepoint >> 8) & 0x0F;
	uint8_t d = (p_codepoint >> 12) & 0x0F;
	uint8_t e = (p_codepoint >> 16) & 0x0F;
	uint8_t f = (p_codepoint >> 20) & 0x0F;

	float w = (p_codepoint <= 0xFF) ? 11 : ((p_codepoint <= 0xFFFF) ? 17 : 23);

	VisualServer::get_singleton()->canvas_item_add_rect(p_canvas_item, Rect2(p_pos + Point2(1, 0), Size2(1, 20)), p_modulate);
	VisualServer::get_singleton()->canvas_item_add_rect(p_canvas_item, Rect2(p_pos + Point2(w, 0), Size2(1, 20)), p_modulate);
	VisualServer::get_singleton()->canvas_item_add_rect(p_canvas_item, Rect2(p_pos + Point2(1, 0), Size2(w, 1)), p_modulate);
	VisualServer::get_singleton()->canvas_item_add_rect(p_canvas_item, Rect2(p_pos + Point2(1, 20), Size2(w, 1)), p_modulate);

	if (p_codepoint <= 0xFF) {
		Rect2 dest = Rect2(p_pos + Point2(4, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(b * 5 - 4, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(4, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(a * 5 - 4, -11), dest.size), p_modulate, false, RID(), false);
	} else if (p_codepoint <= 0xFFFF) {
		Rect2 dest = Rect2(p_pos + Point2(4, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(d * 5 - 4, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(10, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(c * 5 - 10, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(4, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(b * 5 - 4, -11), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(10, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(a * 5 - 10, -11), dest.size), p_modulate, false, RID(), false);
	} else {
		Rect2 dest = Rect2(p_pos + Point2(4, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(f * 5 - 4, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(10, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(e * 5 - 10, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(16, 3), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(d * 5 - 16, -3), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(4, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(c * 5 - 4, -11), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(10, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(b * 5 - 10, -11), dest.size), p_modulate, false, RID(), false);
		dest = Rect2(p_pos + Point2(16, 11), Size2(5, 7));
		VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, dest, hex_box_img_tex->get_rid(), Rect2(dest.position - p_pos + Point2(a * 5 - 16, -11), dest.size), p_modulate, false, RID(), false);
	}
}

void TLFontFace::draw_glyph(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	WARN_PRINTS("Not implemented, pure virtual function call!");
}

void TLFontFace::_draw_char(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	//raw char for debug only, do not use
	WARN_PRINTS("Not implemented, pure virtual function call!");
}

void TLFontFace::draw_glyph_outline(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const {

	WARN_PRINTS("Not implemented, pure virtual function call!");
}

Array TLFontFace::get_glyph_outline(const Point2 p_pos, uint32_t p_codepoint, int p_size) const {
	return Array();
}

float TLFontFace::get_glyph_scale(int p_size) const {

	return 1.0f;
}

hb_font_t *TLFontFace::get_hb_font(int p_size) const {

	return NULL;
}

bool TLFontFace::load(String p_resource_path) {

	WARN_PRINTS("Not implemented, pure virtual function call!");
	return false;
}

Array TLFontFace::_unicode_scripts_supported() const {

	Array ret;

	std::vector<hb_script_t> _sup = unicode_scripts_supported();
	for (size_t i = 0; i < _sup.size(); i++) {
		char tag[5] = "";
		hb_tag_to_string(hb_script_to_iso15924_tag(_sup[i]), tag);
		ret.push_back(String(tag).to_upper());
	}

	return ret;
}

std::vector<hb_script_t> TLFontFace::unicode_scripts_supported() const {

	return std::vector<hb_script_t>();
}

double TLFontFace::get_ascent(int p_size) const {

	return 0.0f;
}

double TLFontFace::get_descent(int p_size) const {

	return 0.0f;
}

double TLFontFace::get_height(int p_size) const {

	return 0.0f;
}

void TLFontFace::set_texture_flags(int p_flags) {

	WARN_PRINTS("Not implemented, pure virtual function call!");
}

int TLFontFace::get_texture_flags() const {

	return 0;
}

int TLFontFace::get_base_size() const {

	return 0;
}

void TLFontFace::set_font_path(String p_resource_path) {

	WARN_PRINTS("Not implemented, pure virtual function call!");
	path = p_resource_path;
}

String TLFontFace::get_font_path() const {

	return path;
}

#ifdef GODOT_MODULE
void TLFontFace::_bind_methods() {

	ClassDB::bind_method(D_METHOD("load", "resource_path"), &TLFontFace::load);

	ClassDB::bind_method(D_METHOD("draw_glyph", "canvas_item", "pos", "codepoint", "modulate", "size"), &TLFontFace::draw_glyph);
	ClassDB::bind_method(D_METHOD("draw_glyph_outline", "canvas_item", "pos", "codepoint", "modulate", "size"), &TLFontFace::draw_glyph_outline);
	ClassDB::bind_method(D_METHOD("get_glyph_outline", "pos", "codepoint", "size"), &TLFontFace::get_glyph_outline);

	ClassDB::bind_method(D_METHOD("get_ascent", "size"), &TLFontFace::get_ascent);
	ClassDB::bind_method(D_METHOD("get_descent", "size"), &TLFontFace::get_descent);
	ClassDB::bind_method(D_METHOD("get_height", "size"), &TLFontFace::get_height);

	ClassDB::bind_method(D_METHOD("get_base_size"), &TLFontFace::get_base_size);

	ClassDB::bind_method(D_METHOD("get_font_path"), &TLFontFace::get_font_path);
	ClassDB::bind_method(D_METHOD("set_font_path", "path"), &TLFontFace::set_font_path);

	ClassDB::bind_method(D_METHOD("set_texture_flags", "flags"), &TLFontFace::set_texture_flags);
	ClassDB::bind_method(D_METHOD("get_texture_flags"), &TLFontFace::get_texture_flags);

	ClassDB::bind_method(D_METHOD("unicode_scripts_supported"), &TLFontFace::_unicode_scripts_supported);

	ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_path", PROPERTY_HINT_FILE, "*.ttf,*.otf"), "set_font_path", "get_font_path");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"), "set_texture_flags", "get_texture_flags");
}

#else

void TLFontFace::_register_methods() {

	register_method("load", &TLFontFace::load);

	register_method("draw_glyph", &TLFontFace::draw_glyph);
	register_method("draw_glyph_outline", &TLFontFace::draw_glyph_outline);
	register_method("get_glyph_outline", &TLFontFace::get_glyph_outline);

	register_method("get_ascent", &TLFontFace::get_ascent);
	register_method("get_descent", &TLFontFace::get_descent);
	register_method("get_height", &TLFontFace::get_height);

	register_method("get_base_size", &TLFontFace::get_base_size);

	register_method("get_font_path", &TLFontFace::get_font_path);
	register_method("set_font_path", &TLFontFace::set_font_path);

	register_method("set_texture_flags", &TLFontFace::set_texture_flags);
	register_method("get_texture_flags", &TLFontFace::get_texture_flags);

	register_method("unicode_scripts_supported", &TLFontFace::_unicode_scripts_supported);

	register_property<TLFontFace, String>("font_path", &TLFontFace::set_font_path, &TLFontFace::get_font_path, String(), GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_FILE, String("*.ttf,*.otf"));
	register_property<TLFontFace, int>("texture_flags", &TLFontFace::set_texture_flags, &TLFontFace::get_texture_flags, Texture::FLAG_VIDEO_SURFACE, GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_FLAGS, String("Mipmaps,Repeat,Filter,Anisotropic Linear,Convert to Linear,Mirrored Repeat,Video Surface"));
}
#endif


text:
/*************************************************************************/
/*  tl_bitmap_font.hpp                                                   */
/*************************************************************************/

#ifndef TL_BITMAP_FONT_FACE_HPP
#define TL_BITMAP_FONT_FACE_HPP

#include "tl_font.hpp"

#include <cmath>
#include <map>
#include <vector>

using namespace godot;

class TLBitmapFontFace;

struct TLBitmapFontFaceAtSize {

	const TLBitmapFontFace *font;
	hb_font_t *hb_font;
	int size;

	TLBitmapFontFaceAtSize();
	~TLBitmapFontFaceAtSize();
};

class TLBitmapFontFace : public TLFontFace {
	GODOT_SUBCLASS(TLBitmapFontFace, TLFontFace);

protected:
	bool loaded;

	float ascent;
	float descent;
	float height;

	int bmp_size;

	struct Glyph {
		Point2 align;
		Rect2 uv;
		size_t id;
		float advance;
	};

	struct KerningPairKey {
		union {
			struct {
				uint32_t A, B;
			};
			uint64_t pair;
		};
		_ALWAYS_INLINE_ bool operator<(const KerningPairKey &p_r) const { return pair < p_r.pair; }
	};

	void clear_cache();

	std::vector<Ref<Texture> > texture_cache;
	std::map<uint32_t, Glyph> glyph_cache;
	std::map<KerningPairKey, int> kerning_map;

	int txt_flags;

	mutable std::map<int, TLBitmapFontFaceAtSize *> sizes;

public:
	TLBitmapFontFace();
	virtual ~TLBitmapFontFace();

	void _init();

	virtual hb_font_t *get_hb_font(int p_size) const;

	virtual void _draw_char(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const; //raw char for debug only, do not use
	virtual void draw_glyph(RID p_canvas_item, const Point2 p_pos, uint32_t p_codepoint, const Color p_modulate, int p_size) const;

	virtual std::vector<hb_script_t> unicode_scripts_supported() const;

	//GDNative methods
	virtual bool load(String p_resource_path);

	virtual void set_font_path(String p_resource_path);

	virtual double get_ascent(int p_size) const;
	virtual double get_descent(int p_size) const;
	virtual double get_height(int p_size) const;

	virtual int get_base_size() const;

	virtual void set_texture_flags(int p_flags);
	virtual int get_texture_flags() const;

	//HB internals
	bool has_glyph(uint32_t p_codepoint) const;
	float get_glyph_advance(uint32_t p_codepoint, int p_size) const;
	Point2 get_glyph_align(uint32_t p_codepoint, int p_size) const;
	Size2 get_glyph_size(uint32_t p_codepoint, int p_size) const;
	float get_kerning_pair(uint32_t p_codepoint_l, uint32_t p_codepoint_r, int p_size) const;
};

HB_BEGIN_DECLS
HB_EXTERN hb_font_t *hb_bmp_font_create(TLBitmapFontFaceAtSize *bm_face, hb_destroy_func_t destroy);
HB_END_DECLS

#endif /*TL_BITMAP_FONT_FACE_HPP*/