1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 /**
22  * @file Font.h
23  * Declares Font, class for manipulating images serving as fonts
24  *
25  * Font uses an "Atlas" instead of individual sprites for characters.
26  * The TTF plugin will dynamically add to its atlas as characters are needed
27  * to avoid building a ton of worthless pages. Additionally, fonts (especially
28  * BAM based ones) use "alias" entries to avoid making duplicate glyphs.
29  * @author The GemRB Project
30  */
31 
32 #ifndef FONT_H
33 #define FONT_H
34 
35 #include "globals.h"
36 #include "exports.h"
37 
38 #include "SpriteSheet.h"
39 
40 #include <deque>
41 #include <map>
42 
43 namespace GemRB {
44 
45 enum FontStyle {
46 	NORMAL = 0x00,
47 	BOLD = 0x01,
48 	ITALIC = 0x02,
49 	UNDERLINE = 0x04
50 };
51 
52 class Palette;
53 using PaletteHolder = Holder<Palette>;
54 
55 #define IE_FONT_ALIGN_LEFT   0x00
56 #define IE_FONT_ALIGN_CENTER 0x01
57 #define IE_FONT_ALIGN_RIGHT  0x02
58 #define IE_FONT_ALIGN_BOTTOM 0x04
59 #define IE_FONT_ALIGN_TOP    0x10
60 #define IE_FONT_ALIGN_MIDDLE 0x20
61 // optimization to make printing faster when text should be on a single line
62 #define IE_FONT_SINGLE_LINE  0x40
63 // work around to use the region passed to Font::Print as if it were the actual print size when the text cant actually fit the region
64 #define IE_FONT_NO_CALC		 0x80
65 
66 struct Glyph {
67 	const Size size;
68 	const Point pos;
69 
70 	const ieWord pitch;
71 	const ieByte* pixels;
72 
GlyphGlyph73 	Glyph(const Size& size, Point pos, ieByte* pixels, ieWord pitch)
74 	: size(size), pos(pos), pitch(pitch), pixels(pixels) {};
75 };
76 
77 /**
78  * @class Font
79  * Class for using and manipulating images serving as fonts
80  */
81 
82 class GEM_EXPORT Font {
83 	/*
84 	 we cannot assume direct pixel access to a Sprite2D (opengl, BAM, etc), but we need raw pixels for rendering text
85 	 However, some clients still want to blit text directly to the screen, in which case we *must* use a Sprite2D.
86 	 Using a single sprite for each glyph is inefficient for OpenGL so we want large sprites composed of many glyphs.
87 	 However, we would also like to avoid creating many unused pages (for TTF fonts, etc) so we cannot simply create a single large page.
88 	 Furthermore, a single page may grow too large to be used as a texture in OpenGL (esp for mobile devices).
89 	 Therefore we generate pages of glyphs (1024 x LineHeight + descent) as sprites suitable for segmented blitting to the screen.
90 	 The pixel data will be safely shared between the Sprite2D objects and the Glyph objects when the video driver is using software rendering
91 	 so this isn't horribly wasteful useage of memory. Hardware acceleration will force the font to keep its own copy of pixel data.
92 
93 	 One odd quirk in this implementation is that, since TTF fonts cannot be pregenerated (efficiently), we must defer creation of a page sprite
94 	 until either the page is full, or until a call to a print method is made.
95 	 To avoid generating more than one partial page, subsequent calls to add new glyphs will destroy the partial Sprite (not the source pixels of course)
96 	*/
97 public:
98 	struct PrintColors {
99 		Color fg;
100 		Color bg;
101 	};
102 
103 	struct StringSizeMetrics {
104 		// specifiy behavior of StringSize based on values of struct
105 		// StringSize will modify the struct with the results
106 		Size size;		// maximum size allowed; updated with actual size <= initial value
107 		size_t numChars; // maximum characters to size (0 implies no limit); updated with the count of characters that fit within size <= initial value
108 		uint32_t numLines; // maximum number of lines to allow (use 0 for unlimited); updated with the actual number of lines that were used
109 		bool forceBreak;// whether or not a break can occur without whitespace; updated to false if initially true and no force break occured
110 	};
111 
112 private:
113 	class GlyphAtlasPage : public SpriteSheet<ieWord> {
114 		private:
115 			using GlyphMap = std::map<ieWord, Glyph>;
116 			GlyphMap glyphs;
117 			ieByte* pageData; // current raw page being built
118 			int pageXPos; // current position on building page
119 			Font* font;
120 			Holder<Sprite2D> invertedSheet;
121 		public:
122 			GlyphAtlasPage(Size pageSize, Font* font);
123 
~GlyphAtlasPage()124 			~GlyphAtlasPage() override {
125 				if (Sheet == NULL) {
126 					free(pageData);
127 				}
128 			}
129 		bool AddGlyph(ieWord chr, const Glyph& g);
130 		const Glyph& GlyphForChr(ieWord chr) const;
131 
132 		// we need a non-const version of Draw here that will call the base const version
133 		using SpriteSheet<ieWord>::Draw;
134 		void Draw(ieWord chr, const Region& dest, const PrintColors* colors);
135 		void DumpToScreen(const Region&);
136 	};
137 
138 	struct GlyphIndexEntry {
139 		ieWord chr;
140 		ieWord pageIdx;
141 		const Glyph* glyph;
142 
GlyphIndexEntryGlyphIndexEntry143 		GlyphIndexEntry() : chr(0), pageIdx(-1), glyph(NULL) {}
GlyphIndexEntryGlyphIndexEntry144 		GlyphIndexEntry(ieWord c, ieWord p, const Glyph* g) : chr(c), pageIdx(p), glyph(g) {}
145 	};
146 
147 	using GlyphIndex = std::vector<GlyphIndexEntry>;
148 	// TODO: Unfortunately, we have no smart pointer suitable for an STL container...
149 	// if we ever transition to C++11 we can use one here
150 	using GlyphAtlas = std::deque<GlyphAtlasPage*>;
151 
152 	GlyphAtlasPage* CurrentAtlasPage;
153 	GlyphIndex AtlasIndex;
154 	GlyphAtlas Atlas;
155 
156 protected:
157 	PaletteHolder palette;
158 	bool background = false;
159 
160 public:
161 	const int LineHeight;
162 	const int Baseline;
163 
164 private:
165 	void CreateGlyphIndex(ieWord chr, ieWord pageIdx, const Glyph*);
166 	// Blit to the sprite or screen if canvas is NULL
167 	size_t RenderText(const String&, Region&, ieByte alignment, const PrintColors*,
168 					  Point* = NULL, ieByte** canvas = NULL, bool grow = false) const;
169 	// render a single line of text. called by RenderText()
170 	size_t RenderLine(const String& string, const Region& rgn,
171 					  Point& dp, const PrintColors*, ieByte** canvas = NULL) const;
172 
173 	size_t Print(Region rgn, const String& string, ieByte Alignment, const PrintColors* colors, Point* point = nullptr) const;
174 
175 public:
176 	Font(PaletteHolder, ieWord lineheight, ieWord baseline, bool background);
177 	virtual ~Font();
178 
179 	const Glyph& CreateGlyphForCharSprite(ieWord chr, Holder<Sprite2D>);
180 	// BAM fonts use alisases a lot so this saves quite a bit of space
181 	// Aliases are 2 glyphs that share identical frames such as 'ā' and 'a'
182 	void CreateAliasForChar(ieWord chr, ieWord alias);
183 
184 	//allow reading but not setting glyphs
185 	virtual const Glyph& GetGlyph(ieWord chr) const;
186 
GetKerningOffset(ieWord,ieWord)187 	virtual int GetKerningOffset(ieWord /*leftChr*/, ieWord /*rightChr*/) const {return 0;};
188 
189 	Holder<Sprite2D> RenderTextAsSprite(const String& string, const Size& size, ieByte alignment,
190 								 size_t* numPrinted = NULL, Point* = NULL) const;
191 
192 	// return the number of glyphs printed
193 	// the "point" parameter can be passed with a start point for rendering
194 	// it will be filled with the point inside 'rgn' where the string ends upon return
195 
196 	size_t Print(const Region& rgn, const String& string, ieByte Alignment, Point* point = nullptr) const;
197 	size_t Print(const Region& rgn, const String& string, ieByte Alignment, const PrintColors& colors, Point* point = nullptr) const;
198 
199 	size_t Print(Region rgn, const String& string,
200 				 PaletteHolder hicolor, ieByte Alignment, Point* point = nullptr) const;
201 
202 	/** Returns size of the string rendered in this font in pixels */
203 	Size StringSize(const String&, StringSizeMetrics* metrics = NULL) const;
204 
205 	// like StringSize, but single line and doens't take whitespace into consideration
206 	size_t StringSizeWidth(const String&, size_t width, size_t* numChars = NULL) const;
207 };
208 
209 }
210 
211 #endif
212