1 /*
2  * This file is part of EasyRPG Player.
3  *
4  * EasyRPG Player is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * EasyRPG Player 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 EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 // Headers
19 #include <map>
20 #include <type_traits>
21 #include <vector>
22 #include <iterator>
23 
24 #include "system.h"
25 #include "game_system.h"
26 #include "main_data.h"
27 
28 #ifdef HAVE_FREETYPE
29 #	include <ft2build.h>
30 #	include FT_FREETYPE_H
31 #	include FT_BITMAP_H
32 #	include FT_MODULE_H
33 #endif
34 
35 #include <lcf/reader_util.h>
36 #include "bitmapfont.h"
37 
38 #include "filefinder.h"
39 #include "output.h"
40 #include "font.h"
41 #include "bitmap.h"
42 #include "utils.h"
43 #include "cache.h"
44 #include "player.h"
45 #include "compiler.h"
46 
47 // Static variables.
48 namespace {
49     template <typename T>
find_glyph(const T & glyphset,char32_t code)50 	BitmapFontGlyph const* find_glyph(const T& glyphset, char32_t code) {
51 		auto iter = std::lower_bound(std::begin(glyphset), std::end(glyphset), code);
52 		if(iter != std::end(glyphset) && iter->code == code) {
53 			return &*iter;
54 		} else {
55 			return NULL;
56 		}
57 	}
58 
59 	// This is the last-resort function for finding a glyph, all the other fonts should fallback on it.
60 	// It tries to display a WenQuanYi glyph, and if it’s not found, returns a replacement glyph.
find_fallback_glyph(char32_t code)61 	BitmapFontGlyph const* find_fallback_glyph(char32_t code) {
62 		auto* wqy = find_glyph(BITMAPFONT_WQY, code);
63 		if (wqy != NULL) {
64 			return wqy;
65 		}
66 		else {
67 			static BitmapFontGlyph const replacement_glyph = { 65533, true, { 96, 240, 504, 924, 1902, 3967, 4031, 1982, 1020, 440, 240, 96 } };
68 			Output::Debug("glyph not found: {:#x}", uint32_t(code));
69 			return &replacement_glyph;
70 		}
71 	}
72 
find_gothic_glyph(char32_t code)73 	BitmapFontGlyph const* find_gothic_glyph(char32_t code) {
74 		auto* gothic = find_glyph(SHINONOME_GOTHIC, code);
75 		return gothic != NULL ? gothic : find_fallback_glyph(code);
76 	}
77 
find_mincho_glyph(char32_t code)78 	BitmapFontGlyph const* find_mincho_glyph(char32_t code) {
79 		auto* mincho = find_glyph(SHINONOME_MINCHO, code);
80 		return mincho == NULL ? find_gothic_glyph(code) : mincho;
81 	}
82 
find_rmg2000_glyph(char32_t code)83 	BitmapFontGlyph const* find_rmg2000_glyph(char32_t code) {
84 		auto* rmg2000 = find_glyph(BITMAPFONT_RMG2000, code);
85 		if (rmg2000 != NULL) {
86 			return rmg2000;
87 		}
88 
89 		auto* ttyp0 = find_glyph(BITMAPFONT_TTYP0, code);
90 		return ttyp0 != NULL ? ttyp0 : find_mincho_glyph(code);
91 	}
92 
find_ttyp0_glyph(char32_t code)93 	BitmapFontGlyph const* find_ttyp0_glyph(char32_t code) {
94 		auto* ttyp0 = find_glyph(BITMAPFONT_TTYP0, code);
95 		return ttyp0 != NULL ? ttyp0 : find_gothic_glyph(code);
96 	}
97 
98 	struct BitmapFont : public Font {
99 		enum { HEIGHT = 12, FULL_WIDTH = HEIGHT, HALF_WIDTH = FULL_WIDTH / 2 };
100 
101 		using function_type = BitmapFontGlyph const*(*)(char32_t);
102 
103 		BitmapFont(const std::string& name, function_type func);
104 
105 		Rect GetSize(StringView txt) const override;
106 		Rect GetSize(char32_t ch) const override;
107 
108 		GlyphRet Glyph(char32_t code) override;
109 
110 	private:
111 		function_type func;
112 		BitmapRef glyph_bm;
113 	}; // class BitmapFont
114 
115 #ifdef HAVE_FREETYPE
116 	typedef std::map<std::string, std::weak_ptr<std::remove_pointer<FT_Face>::type>> face_cache_type;
117 	face_cache_type face_cache;
118 
delete_face(FT_Face f)119 	void delete_face(FT_Face f) {
120 		if(FT_Done_Face(f) != FT_Err_Ok) {
121 			Output::Warning("FT_Face deleting error.");
122 		}
123 	}
124 
delete_library(FT_Library f)125 	void delete_library(FT_Library f) {
126 		if(FT_Done_Library(f) != FT_Err_Ok) {
127 			Output::Warning("FT_Library deleting error.");
128 		}
129 	}
130 
131 	struct FTFont : public Font  {
132 		FTFont(const std::string& name, int size, bool bold, bool italic);
133 
134 		Rect GetSize(StringView txt) const override;
135 		Rect GetSize(char32_t ch) const override;
136 
137 		GlyphRet Glyph(char32_t code) override;
138 
139 	private:
140 		static std::weak_ptr<std::remove_pointer<FT_Library>::type> library_checker_;
141 		std::shared_ptr<std::remove_pointer<FT_Library>::type> library_;
142 		std::shared_ptr<std::remove_pointer<FT_Face>::type> face_;
143 		std::string face_name_;
144 		unsigned current_size_;
145 
146 		bool check_face();
147 	}; // class FTFont
148 #endif
149 
150 	/* Bitmap fonts used for the official Japanese version.
151 	   Compatible with MS Gothic and MS Mincho. Feature a closing quote in place of straight quote,
152 	   double-width Cyrillic letters (unusable for Russian, only useful for smileys and things like that)
153 	   and ellipsis in the middle of the line.
154 	*/
155 	FontRef const gothic = std::make_shared<BitmapFont>("Shinonome Gothic", &find_gothic_glyph);
156 	FontRef const mincho = std::make_shared<BitmapFont>("Shinonome Mincho", &find_mincho_glyph);
157 
158 	/* Bitmap fonts used for non-Japanese games.
159 	   Compatible with RMG2000 and RM2000 shipped with Don Miguel’s unofficial translation.
160 	   Feature a half-width Cyrillic and half-width ellipsis at the bottom of the line.
161 	*/
162 	FontRef const rmg2000 = std::make_shared<BitmapFont>("RMG2000-compatible", &find_rmg2000_glyph);
163 	FontRef const ttyp0 = std::make_shared<BitmapFont>("ttyp0", &find_ttyp0_glyph);
164 
165 	struct ExFont : public Font {
166 		public:
167 			enum { HEIGHT = 12, WIDTH = 12 };
168 			ExFont();
169 			Rect GetSize(StringView txt) const override;
170 			Rect GetSize(char32_t ch) const override;
171 			GlyphRet Glyph(char32_t code) override;
172 		private:
173 			BitmapRef bm;
174 	};
175 } // anonymous namespace
176 
BitmapFont(const std::string & name,function_type func)177 BitmapFont::BitmapFont(const std::string& name, function_type func)
178 	: Font(name, HEIGHT, false, false), func(func)
179 {}
180 
GetSize(char32_t ch) const181 Rect BitmapFont::GetSize(char32_t ch) const {
182 	size_t units = 0;
183 	if (EP_LIKELY(!Utils::IsControlCharacter(ch))) {
184 		auto glyph = func(ch);
185 		units += glyph->is_full? 2 : 1;
186 	}
187 	return Rect(0, 0, units * HALF_WIDTH, HEIGHT);
188 }
189 
GetSize(StringView txt) const190 Rect BitmapFont::GetSize(StringView txt) const {
191 	size_t units = 0;
192 	const auto* iter = txt.data();
193 	const auto* end = txt.data() + txt.size();
194 	while (iter != end) {
195 		auto resp = Utils::UTF8Next(iter, end);
196 		auto ch = resp.ch;
197 		iter = resp.next;
198 		if (EP_LIKELY(!Utils::IsControlCharacter(resp.ch))) {
199 			auto glyph = func(ch);
200 			units += glyph->is_full? 2 : 1;
201 		}
202 	}
203 	return Rect(0, 0, units * HALF_WIDTH, HEIGHT);
204 }
205 
Glyph(char32_t code)206 Font::GlyphRet BitmapFont::Glyph(char32_t code) {
207 	if (EP_UNLIKELY(!glyph_bm)) {
208 		glyph_bm = Bitmap::Create(nullptr, FULL_WIDTH, HEIGHT, 0, DynamicFormat(8,8,0,8,0,8,0,8,0,PF::Alpha));
209 	}
210 	if (EP_UNLIKELY(Utils::IsControlCharacter(code))) {
211 		return { glyph_bm, Rect(0, 0, 0, HEIGHT) };
212 	}
213 	auto glyph = func(code);
214 	auto width = glyph->is_full? FULL_WIDTH : HALF_WIDTH;
215 
216 	glyph_bm->Clear();
217 	uint8_t* data = reinterpret_cast<uint8_t*>(glyph_bm->pixels());
218 	int pitch = glyph_bm->pitch();
219 	for(size_t y_ = 0; y_ < HEIGHT; ++y_)
220 		for(size_t x_ = 0; x_ < width; ++x_)
221 			data[y_*pitch+x_] = (glyph->data[y_] & (0x1 << x_)) ? 255 : 0;
222 
223 	return { glyph_bm, Rect(0, 0, width, HEIGHT) };
224 }
225 
226 #ifdef HAVE_FREETYPE
227 std::weak_ptr<std::remove_pointer<FT_Library>::type> FTFont::library_checker_;
228 
FTFont(const std::string & name,int size,bool bold,bool italic)229 FTFont::FTFont(const std::string& name, int size, bool bold, bool italic)
230 	: Font(name, size, bold, italic), current_size_(0) {}
231 
GetSize(StringView txt) const232 Rect FTFont::GetSize(StringView txt) const {
233 	int const s = Font::Default()->GetSize(txt).width;
234 
235 	if (s == -1) {
236 		Output::Warning("Text contains invalid chars. Is the encoding correct?");
237 
238 		return Rect(0, 0, pixel_size() * txt.length() / 2, pixel_size());
239 	} else {
240 		return Rect(0, 0, s, pixel_size());
241 	}
242 }
243 
GetSize(char32_t ch) const244 Rect FTFont::GetSize(char32_t ch) const {
245 	int const s = Font::Default()->GetSize(ch).width;
246 
247 	if (s == -1) {
248 		Output::Warning("Text contains invalid chars. Is the encoding correct?");
249 
250 		return Rect(0, 0, pixel_size() / 2, pixel_size());
251 	} else {
252 		return Rect(0, 0, s, pixel_size());
253 	}
254 }
255 
256 
257 
Glyph(char32_t glyph)258 Font::GlyphRet FTFont::Glyph(char32_t glyph) {
259 	if(!check_face()) {
260 		return Font::Default()->Glyph(glyph);
261 	}
262 
263 	if (FT_Load_Char(face_.get(), glyph, FT_LOAD_NO_BITMAP) != FT_Err_Ok) {
264 		Output::Error("Couldn't load FreeType character {:#x}", uint32_t(glyph));
265 	}
266 
267 	if (FT_Render_Glyph(face_->glyph, FT_RENDER_MODE_MONO) != FT_Err_Ok) {
268 		Output::Error("Couldn't render FreeType character {:#x}", uint32_t(glyph));
269 	}
270 
271 	FT_Bitmap const& ft_bitmap = face_->glyph->bitmap;
272 	assert(face_->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
273 
274 	size_t const pitch = std::abs(ft_bitmap.pitch);
275 	int const width = ft_bitmap.width;
276 	int const height = ft_bitmap.rows;
277 
278 	BitmapRef bm = Bitmap::Create(nullptr, width, height, 0, DynamicFormat(8,8,0,8,0,8,0,8,0,PF::Alpha));
279 	uint8_t* data = reinterpret_cast<uint8_t*>(bm->pixels());
280 	int dst_pitch = bm->pitch();
281 
282 	for(int row = 0; row < height; ++row) {
283 		for(int col = 0; col < width; ++col) {
284 			unsigned c = ft_bitmap.buffer[pitch * row + (col/8)];
285 			unsigned bit = 7 - (col%8);
286 			data[row * dst_pitch + col] = (c & (0x01 << bit)) ? 255 : 0;
287 		}
288 	}
289 
290 	return { bm, Rect(0, 0, width, height) };
291 }
292 
check_face()293 bool FTFont::check_face() {
294 	if (!library_) {
295 		if (library_checker_.expired()) {
296 			FT_Library lib;
297 			if (FT_Init_FreeType(&lib) != FT_Err_Ok) {
298 				Output::Error("Couldn't initialize FreeType");
299 				return false;
300 			}
301 			library_.reset(lib, delete_library);
302 			library_checker_ = library_;
303 		} else {
304 			library_ = library_checker_.lock();
305 		}
306 	}
307 
308 	if (!face_ || face_name_ != name) {
309 		face_cache_type::const_iterator it = face_cache.find(name);
310 		if (it == face_cache.end() || it->second.expired()) {
311 			std::string const face_path = FileFinder::FindFont(name);
312 			FT_Face face;
313 			if (FT_New_Face(library_.get(), face_path.c_str(), 0, &face) != FT_Err_Ok) {
314 				Output::Error("Couldn't initialize FreeType face: {}({})",
315 					name, face_path);
316 				return false;
317 			}
318 
319 			for (int i = 0; i < face_->num_fixed_sizes; i++) {
320 				FT_Bitmap_Size* size = &face_->available_sizes[i];
321 				Output::Debug("Font Size {}: {} {} {} {} {}", i,
322 					size->width, size->height, size->size / 64.0,
323 					size->x_ppem / 64.0, size->y_ppem / 64.0);
324 			}
325 
326 			face_.reset(face, delete_face);
327 			face_cache[name] = face_;
328 		} else {
329 			face_ = it->second.lock();
330 		}
331 		face_name_ = name;
332 	}
333 
334 	face_->style_flags =
335 		(bold ? FT_STYLE_FLAG_BOLD : 0) |
336 		(italic ? FT_STYLE_FLAG_ITALIC : 0);
337 
338 	if (current_size_ != size) {
339 		int sz, dpi;
340 		if (face_->num_fixed_sizes == 1) {
341 			sz = face_->available_sizes[0].size;
342 			dpi = 96;
343 		} else {
344 			sz = size * 64;
345 			dpi = 72;
346 		}
347 
348 		if (FT_Set_Char_Size(face_.get(), sz, sz, dpi, dpi) != FT_Err_Ok) {
349 			Output::Error("Couldn't set FreeType face size");
350 			return false;
351 		}
352 		current_size_ = size;
353 	}
354 
355 	return true;
356 }
357 #endif
358 
Default()359 FontRef Font::Default() {
360 	const bool mincho = (Main_Data::game_system && Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_mincho);
361 	return Default(mincho);
362 }
363 
Default(bool const m)364 FontRef Font::Default(bool const m) {
365 	if (Player::IsCJK()) {
366 		return m ? mincho : gothic;
367 	}
368 	else {
369 		return m ? rmg2000 : ttyp0;
370 	}
371 }
372 
Create(const std::string & name,int size,bool bold,bool italic)373 FontRef Font::Create(const std::string& name, int size, bool bold, bool italic) {
374 #ifdef HAVE_FREETYPE
375 	return std::make_shared<FTFont>(name, size, bold, italic);
376 #else
377 	(void)name; (void)size; (void)bold; (void)italic;
378 	return Font::Default();
379 #endif
380 }
381 
Dispose()382 void Font::Dispose() {
383 #ifdef HAVE_FREETYPE
384 	for(face_cache_type::const_iterator i = face_cache.begin(); i != face_cache.end(); ++i) {
385 		if(i->second.expired()) { continue; }
386 		Output::Debug("possible leak in cached font face {}", i->first);
387 	}
388 	face_cache.clear();
389 #endif
390 }
391 
392 // Constructor.
Font(const std::string & name,int size,bool bold,bool italic)393 Font::Font(const std::string& name, int size, bool bold, bool italic)
394 	: name(name)
395 	, size(size)
396 	, bold(bold)
397 	, italic(italic)
398 {
399 }
400 
Render(Bitmap & dest,int const x,int const y,const Bitmap & sys,int color,char32_t code)401 Rect Font::Render(Bitmap& dest, int const x, int const y, const Bitmap& sys, int color, char32_t code) {
402 	auto gret = Glyph(code);
403 
404 	auto rect = Rect(x, y, gret.rect.width, gret.rect.height);
405 	if (EP_UNLIKELY(rect.width == 0)) {
406 		return rect;
407 	}
408 
409 	if(color != ColorShadow) {
410 		auto shadow_rect = Rect(x + 1, y + 1, rect.width, rect.height);
411 		dest.MaskedBlit(shadow_rect, *gret.bitmap, 0, 0, sys, 16, 32);
412 	}
413 
414 	unsigned const
415 		src_x = color == ColorShadow? 16 : color % 10 * 16 + 2,
416 		src_y = color == ColorShadow? 32 : color / 10 * 16 + 48 + 16 - gret.bitmap->height();
417 
418 
419 	dest.MaskedBlit(rect, *gret.bitmap, 0, 0, sys, src_x, src_y);
420 
421 	return rect;
422 }
423 
Render(Bitmap & dest,int x,int y,Color const & color,char32_t code)424 Rect Font::Render(Bitmap& dest, int x, int y, Color const& color, char32_t code) {
425 	auto gret = Glyph(code);
426 
427 	auto rect = Rect(x, y, gret.rect.width, gret.rect.height);
428 	dest.MaskedBlit(rect, *gret.bitmap, 0, 0, color);
429 
430 	return rect;
431 }
432 
ExFont()433 ExFont::ExFont() : Font("exfont", 12, false, false) {
434 }
435 
436 FontRef Font::exfont = std::make_shared<ExFont>();
437 
Glyph(char32_t code)438 Font::GlyphRet ExFont::Glyph(char32_t code) {
439 	if (EP_UNLIKELY(!bm)) { bm = Bitmap::Create(WIDTH, HEIGHT, true); }
440 	auto exfont = Cache::Exfont();
441 
442 	Rect const rect((code % 13) * WIDTH, (code / 13) * HEIGHT, WIDTH, HEIGHT);
443 	bm->Clear();
444 	bm->Blit(0, 0, *exfont, rect, Opacity::Opaque());
445 	return { bm, Rect(0, 0, WIDTH, HEIGHT) };
446 }
447 
GetSize(StringView) const448 Rect ExFont::GetSize(StringView) const {
449 	return Rect(0, 0, 12, 12);
450 }
451 
GetSize(char32_t) const452 Rect ExFont::GetSize(char32_t) const {
453 	return Rect(0, 0, 12, 12);
454 }
455