1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

#include "fsCFont.h"

#include <fstream>
#include <vector>

#include <fsCore/src/fsCResourceName.h>
#include <fsCore/src/debug/fsAssert.h>

// Static so these implementation copies stay local to this translation unit
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb/stb_rect_pack.h>
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb/stb_truetype.h>

namespace
{
	// Same coverage the raylib backend requests: ASCII printable, Latin-1
	// supplement (fr/de locales) plus common typographic marks used in UI copy.
	std::vector<int> defaultCodepointsGet()
	{
		std::vector<int> codepoints;
		for (int codepoint = 32; codepoint <= 126; ++codepoint)
		{
			codepoints.push_back(codepoint);
		}
		for (int codepoint = 160; codepoint <= 255; ++codepoint)
		{
			codepoints.push_back(codepoint);
		}
		const int extras[] = { 0x2022, 0x25CF, 0x2013, 0x2014, 0x2018, 0x2019, 0x201C, 0x201D };
		for (int codepoint : extras)
		{
			codepoints.push_back(codepoint);<--- Consider using std::copy algorithm instead of a raw loop.
		}
		return codepoints;
	}
}

const fsCFont::fsSGlyph* fsCFont::glyphGet(fsS32 pCodepoint) const
{
	auto found = mGlyphs.find(pCodepoint);
	if (found == mGlyphs.end())
	{
		found = mGlyphs.find('?');
	}
	return found != mGlyphs.end() ? &found->second : nullptr;
}

void fsCFont::initialiseDo(const fsSFontParams& pParams)
{
	const fsF32 fontSize = pParams.mTrueFontSize ? static_cast<fsF32>(pParams.mTrueFontSize) : static_cast<fsF32>(pParams.mFontSize) * 2.6f;
	mPointSize = fontSize;

	std::ifstream file(pParams.mFileName.nameGet().utf8Get(), std::ios::binary);
	fsAssert(file.is_open(), "Font %s could not be read.\n", pParams.mFileName.nameGet().utf8Get());
	if (!file.is_open())
	{
		return;
	}
	std::vector<unsigned char> fontData((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());

	mAtlasWidth = 1024;
	mAtlasHeight = 1024;
	std::vector<unsigned char> coverage(mAtlasWidth * mAtlasHeight, 0);

	std::vector<int> codepoints = defaultCodepointsGet();
	std::vector<stbtt_packedchar> packedChars(codepoints.size());

	stbtt_pack_context packContext;
	stbtt_PackBegin(&packContext, coverage.data(), mAtlasWidth, mAtlasHeight, 0, 1, nullptr);
	stbtt_PackSetOversampling(&packContext, 1, 1);

	stbtt_pack_range range = {};
	range.font_size = fontSize;
	range.array_of_unicode_codepoints = codepoints.data();
	range.num_chars = static_cast<int>(codepoints.size());
	range.chardata_for_range = packedChars.data();
	stbtt_PackFontRanges(&packContext, fontData.data(), 0, &range, 1);

	stbtt_PackEnd(&packContext);

	for (std::size_t i = 0; i < codepoints.size(); ++i)
	{
		const stbtt_packedchar& packed = packedChars[i];
		fsSGlyph glyph;
		glyph.mX0 = packed.x0;
		glyph.mY0 = packed.y0;
		glyph.mX1 = packed.x1;
		glyph.mY1 = packed.y1;
		glyph.mXOff = packed.xoff;
		glyph.mYOff = packed.yoff;
		glyph.mXAdvance = packed.xadvance;
		mGlyphs[codepoints[i]] = glyph;
	}

	stbtt_fontinfo fontInfo;
	if (stbtt_InitFont(&fontInfo, fontData.data(), 0))
	{
		int ascent = 0;
		int descent = 0;
		int lineGap = 0;
		stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
		const fsF32 scale = stbtt_ScaleForPixelHeight(&fontInfo, fontSize);
		mAscent = ascent * scale;
		mLineHeight = (ascent - descent + lineGap) * scale;
	}
	else
	{
		mAscent = fontSize * 0.8f;
		mLineHeight = fontSize;
	}

	// The shared 2d shader multiplies texture by vertex colour, so expand the
	// 8-bit coverage atlas to white RGBA with coverage in alpha
	std::vector<fsU8> rgba(coverage.size() * 4);
	for (std::size_t i = 0; i < coverage.size(); ++i)
	{
		rgba[i * 4 + 0] = 0xff;
		rgba[i * 4 + 1] = 0xff;
		rgba[i * 4 + 2] = 0xff;
		rgba[i * 4 + 3] = coverage[i];
	}

	mTexture = bgfx::createTexture2D(
		static_cast<uint16_t>(mAtlasWidth),
		static_cast<uint16_t>(mAtlasHeight),
		false, 1, bgfx::TextureFormat::RGBA8, 0,
		bgfx::copy(rgba.data(), static_cast<uint32_t>(rgba.size())));
}

fsCFont::fsCFont()
{
}

fsCFont::~fsCFont()
{
	if (bgfx::isValid(mTexture))
	{
		bgfx::destroy(mTexture);
		mTexture = BGFX_INVALID_HANDLE;
	}
}