aboutsummaryrefslogtreecommitdiff
path: root/source/30-game/Font.hpp
blob: cc64c0ce1f378c27ee6df7e639d39accbd57648f (plain)
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
#pragma once

#include "Color.hpp"
#include "CommonVertexIndex.hpp"
#include "RcPtr.hpp"

#include <glad/glad.h>
#include <cstddef>
#include <cstdint>
#include <glm/glm.hpp>
#include <memory>
#include <span>
#include <string_view>
#include <vector>

enum class FontType {
	Regular,
	Italic,
	Bold,
	BoldItalic,
};

struct GlyphVariant {
	int horizontalAdvance;
	float x0, y0;
	float x1, y1;
	float u0, v0;
	float u1, v1;
};

// TODO optimize for memory when most glyphs don't have a non-regular variant
// such as CJK characters rarely use bold or italics
struct GlyphInfo {
	char32_t codepoint;
	GlyphVariant regular;
	GlyphVariant italic;
	GlyphVariant bold;
	GlyphVariant boldItalic;
};

class Font : public RefCounted {
public:
	static const char32_t* GetGlyphRangesDefault();
	static const char32_t* GetGlyphRangesKorean();
	static const char32_t* GetGlyphRangesJapanese();
	static const char32_t* GetGlyphRangesChineseFull();
	static const char32_t* GetGlyphRangesChineseSimplifiedCommon();
	static const char32_t* GetGlyphRangesCyrillic();
	static const char32_t* GetGlyphRangesThai();
	static const char32_t* GetGlyphRangesVietnamese();

private:
	std::vector<GlyphInfo> mGlyphs;
	std::vector<int> mGlyphLookup;
	RcPtr<Texture> mAtlas;
	float mFontHeight;
	int mFallbackGlyphIdx;

public:
	struct LoadingCandidate {
		// No std::string_view here because iostreams only support null termianted string
		const char* ttfPath;
		const char32_t* glyphRange;
		FontType type = FontType::Regular;
	};

	Font() = default;
	Font(const Font&) = delete;
	Font& operator=(const Font&) = delete;
	Font(Font&&) = default;
	Font& operator=(Font&&) = default;

	enum ErrorCode {
		EC_Success,
		EC_FileIOFailed,
		EC_FontLoadingFailed,
	};
	struct InitResult {
		ErrorCode errorCode;
		int failedItemIdx;
	};

	/// Initialize this font object from a list of ttf files. Each ttf covers a range of unicode characters,
	/// and a specific font type, which are combined together into an atlas texture.
	///
	/// \see FontType
	/// \see Font::LoadingCandidate
	InitResult Init(std::span<LoadingCandidate> candidates, float fontHeight, char32_t fallbackCodepoint = '?', int oversampleH = 1, int oversampleV = 1);

	float GetFontHeight() const;
	int HorizontalAdvance(char32_t c, FontType type = FontType::Regular) const;
	int HorizontalAdvance(std::string_view str, FontType type = FontType::Regular) const;

	int GetGlyphCount() const;
	const GlyphInfo& GetFallbackGlyph() const;

	/// Find the glyph corresponding to the given codepoint. If none is present, return the fallback glyph
	/// specified in Init().
	const GlyphInfo& FindGlyphFallback(char32_t codepoint) const;
	/// Find the glyph corresponding to the given codepoint. If none is present, `nullptr` is returned.
	const GlyphInfo* FindGlyph(char32_t codepoint) const;

	struct DrawTargetPointer {
		// Draw target info
		Vertex_PTC* vertices;
		uint32_t* indices;
		uint32_t initialVertexIdx;

		// Text info
		glm::vec3 pos;
		FontType type = FontType::Regular;
		RgbaColor color = RgbaColor{};

		// Output info, written to by DrawTo()
		int glyphsRendered;
		float horizontalAdvance;
	};

	/// Populate the target with quads, with first character's origin at `pos` and every vertex colored `color`.
	/// User must allocate enough space for all characers beforehand (one way is to reserve the # of bytes).
	/// Suitable for raw array allocations or something like PodVector<T> that doesn't do zero-initialization on
	/// resize.
	///
	/// \see Font::DrawTargetPointer
	void DrawTo(std::string_view text, DrawTargetPointer& t) const;
	void DrawTo(std::u32string_view text, DrawTargetPointer& t) const;

	/// OpenGL texture containing the font atlas. Format is in GL_RED because we don't need the other channels, due to the
	/// font atlas being black and white (in other words, opacity only).
	const Texture& GetGlyphAtlas() const;
};