gosu 0.13.3 → 0.14.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Gosu/Audio.hpp +15 -11
  3. data/Gosu/Font.hpp +24 -20
  4. data/Gosu/Fwd.hpp +1 -1
  5. data/Gosu/Graphics.hpp +8 -9
  6. data/Gosu/ImageData.hpp +1 -1
  7. data/Gosu/Input.hpp +1 -1
  8. data/Gosu/Math.hpp +0 -18
  9. data/Gosu/Text.hpp +22 -30
  10. data/Gosu/TextInput.hpp +13 -0
  11. data/Gosu/Utility.hpp +2 -0
  12. data/Gosu/Window.hpp +3 -3
  13. data/README.md +3 -4
  14. data/ext/gosu/extconf.rb +7 -9
  15. data/lib/gosu/swig_patches.rb +1 -4
  16. data/rdoc/gosu.rb +34 -9
  17. data/src/Audio.cpp +6 -6
  18. data/src/AudioImpl.cpp +2 -2
  19. data/src/Bitmap.cpp +1 -2
  20. data/src/BitmapIO.cpp +21 -2
  21. data/src/BlockAllocator.cpp +1 -1
  22. data/src/Channel.cpp +7 -1
  23. data/src/ClipRectStack.hpp +4 -1
  24. data/src/Color.cpp +2 -1
  25. data/src/DirectoriesWin.cpp +1 -1
  26. data/src/DrawOp.hpp +8 -4
  27. data/src/DrawOpQueue.hpp +13 -24
  28. data/src/FileUnix.cpp +3 -1
  29. data/src/Font.cpp +92 -96
  30. data/src/GosuGLView.cpp +59 -31
  31. data/src/GosuGLView.hpp +14 -0
  32. data/src/GosuViewController.cpp +21 -21
  33. data/src/{GosuViewController.h → GosuViewController.hpp} +2 -4
  34. data/src/Graphics.cpp +71 -38
  35. data/src/GraphicsImpl.hpp +12 -29
  36. data/src/Image.cpp +5 -7
  37. data/src/Input.cpp +7 -5
  38. data/src/InputUIKit.cpp +19 -37
  39. data/src/Macro.cpp +10 -2
  40. data/src/MarkupParser.cpp +241 -0
  41. data/src/MarkupParser.hpp +61 -0
  42. data/src/Math.cpp +1 -1
  43. data/src/OffScreenTarget.cpp +99 -0
  44. data/src/OffScreenTarget.hpp +23 -0
  45. data/src/OggFile.hpp +10 -0
  46. data/src/RenderState.hpp +0 -2
  47. data/src/Resolution.cpp +2 -2
  48. data/src/RubyGosu.cxx +457 -244
  49. data/src/TexChunk.cpp +8 -6
  50. data/src/Text.cpp +58 -345
  51. data/src/TextBuilder.cpp +138 -0
  52. data/src/TextBuilder.hpp +55 -0
  53. data/src/TextInput.cpp +27 -10
  54. data/src/Texture.cpp +22 -17
  55. data/src/Texture.hpp +19 -20
  56. data/src/TimingApple.cpp +5 -7
  57. data/src/TimingUnix.cpp +1 -4
  58. data/src/TimingWin.cpp +4 -1
  59. data/src/TrueTypeFont.cpp +282 -0
  60. data/src/TrueTypeFont.hpp +66 -0
  61. data/src/TrueTypeFontApple.cpp +65 -0
  62. data/src/TrueTypeFontUnix.cpp +91 -0
  63. data/src/TrueTypeFontWin.cpp +82 -0
  64. data/src/Utility.cpp +40 -0
  65. data/src/Window.cpp +7 -6
  66. data/src/WindowUIKit.cpp +9 -4
  67. data/src/stb_truetype.h +4589 -0
  68. data/src/utf8proc.c +755 -0
  69. data/src/utf8proc.h +699 -0
  70. data/src/utf8proc_data.h +14386 -0
  71. metadata +23 -16
  72. data/src/FormattedString.cpp +0 -237
  73. data/src/FormattedString.hpp +0 -47
  74. data/src/GosuAppDelegate.cpp +0 -30
  75. data/src/GosuAppDelegate.h +0 -8
  76. data/src/GosuGLView.h +0 -8
  77. data/src/TextApple.cpp +0 -212
  78. data/src/TextTTFWin.cpp +0 -197
  79. data/src/TextUnix.cpp +0 -280
  80. data/src/TextWin.cpp +0 -191
@@ -0,0 +1,282 @@
1
+ #include "TrueTypeFont.hpp"
2
+ #include <Gosu/IO.hpp>
3
+ #include <Gosu/Text.hpp>
4
+
5
+ // Disable comma warnings in stb headers.
6
+ #ifdef __GNUC__
7
+ #pragma GCC diagnostic push
8
+ #pragma GCC diagnostic ignored "-Wcomma"
9
+ #endif
10
+
11
+ #define STB_TRUETYPE_IMPLEMENTATION
12
+ #include "stb_truetype.h"
13
+
14
+ #ifdef __GNUC__
15
+ #pragma GCC diagnostic pop
16
+ #endif
17
+
18
+ #include <algorithm>
19
+ #include <map>
20
+ using namespace std;
21
+
22
+ struct Gosu::TrueTypeFont::Impl
23
+ {
24
+ stbtt_fontinfo info;
25
+ shared_ptr<TrueTypeFont> fallback;
26
+
27
+ // The ascent in internal font metrics (an arbitrary integer scale), that is, the part of the
28
+ // font above the baseline, which TrueType considers to be at y = 0.
29
+ int ascent;
30
+
31
+ // Scaling factor from internal font metrics (an arbitrary integer scale) to a font with
32
+ // height = 1px.
33
+ double base_scale;
34
+
35
+ Impl(const unsigned char* ttf_data, shared_ptr<TrueTypeFont> fallback)
36
+ : fallback(move(fallback))
37
+ {
38
+ auto offset = stbtt_GetFontOffsetForIndex(ttf_data, 0);
39
+ int success = stbtt_InitFont(&info, ttf_data, offset);
40
+ if (!success) throw runtime_error("Invalid TrueType font data");
41
+
42
+ // Calculate metrics.
43
+ int descent, lineGap;
44
+ stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap);
45
+ int height = ascent - descent + lineGap;
46
+ base_scale = 1.0f / height;
47
+ }
48
+
49
+ // This method always measures text, and also draws it if (bitmap != nullptr).
50
+ double draw_text(const u32string& text, bool is_end, double height,
51
+ Bitmap* bitmap, double x, double y, Color c)
52
+ {
53
+ if (text.empty()) return 0;
54
+
55
+ // The 'x' parameter is used as the running cursor variable in this method.
56
+
57
+ double scale = base_scale * height;
58
+ int last_glyph = 0;
59
+ int last_advance = 0;
60
+
61
+ for (u32string::size_type index = 0; index < text.size(); ++index) {
62
+ auto codepoint = text[index];
63
+ // Silently skip control characters, including the \r in Windows-style line breaks.
64
+ if (codepoint < ' ') continue;
65
+
66
+ int glyph = stbtt_FindGlyphIndex(&info, codepoint);
67
+ // Handle missing characters in this font...
68
+ if (glyph == 0) {
69
+ if (fallback) {
70
+ // Missing characters often come in clusters, so build a substring of
71
+ // codepoints that this font doesn't contain and then defer to the fallback
72
+ // font.
73
+ u32string fallback_string;
74
+ for (; index < text.size(); ++index) {
75
+ auto codepoint = text[index];
76
+ // Skip control characters.
77
+ if (codepoint < ' ') continue;
78
+ // Stop as soon as a glyph is available in the current font.
79
+ if (stbtt_FindGlyphIndex(&info, codepoint) != 0) break;
80
+
81
+ fallback_string.push_back(codepoint);
82
+ }
83
+ x = fallback->pimpl->draw_text(fallback_string, index == text.size(), height,
84
+ bitmap, x, y, c);
85
+ last_glyph = 0;
86
+ }
87
+ continue;
88
+ }
89
+
90
+ if (last_glyph) {
91
+ x += stbtt_GetGlyphKernAdvance(&info, last_glyph, glyph) * scale;
92
+ }
93
+
94
+ // Now finally draw the glyph (if a bitmap was passed).
95
+ if (bitmap) {
96
+ // Do not take lsb into account when positioning the glyph: It seems to correspond
97
+ // to the 'xoff' value returned by stbtt_GetGlyphBitmapSubpixel, and adding both
98
+ // adds too much spacing between letters.
99
+ // Ref: https://github.com/nothings/stb/issues/281#issuecomment-361264014
100
+ draw_glyph(*bitmap, x, y, c, glyph, scale);
101
+ }
102
+
103
+ int advance;
104
+ stbtt_GetGlyphHMetrics(&info, glyph, &advance, nullptr);
105
+
106
+ x += advance * scale;
107
+ last_glyph = glyph;
108
+ last_advance = advance;
109
+ }
110
+
111
+ // If this is the end of the string, we need to take another look at the last glyph to avoid
112
+ // cutting off some pixels that extend to the right of the character.
113
+ if (is_end && last_glyph) {
114
+ int ix = static_cast<int>(x);
115
+ int last_xoff, last_width;
116
+ // TODO: Don't allocate a buffer just to get metrics!
117
+ free(stbtt_GetGlyphBitmapSubpixel(&info, scale, scale, x - ix, 0, last_glyph,
118
+ &last_width, nullptr, &last_xoff, nullptr));
119
+ // Move the cursor to the right if pixels have been touched by draw_glyph that are
120
+ // to the right of the current cursor.
121
+ // If the last character extends to the right of the cursor, then this prevents the
122
+ // rightmost pixels from being truncated.
123
+ // If the last character was whitespace, then last_width will be 0 (no pixel data)
124
+ // and the cursor is what counts.
125
+ x = max<double>(x, x - last_advance * scale + last_xoff + last_width);
126
+ }
127
+
128
+ // Never return a negative value from this method because it is used to determine bitmap
129
+ // dimensions.
130
+ return max<double>(0, x);
131
+ }
132
+
133
+ void draw_glyph(Bitmap& bitmap, double fx, double fy, Color c, int glyph, double scale)
134
+ {
135
+ int x = static_cast<int>(fx);
136
+ int y = static_cast<int>(fy);
137
+ int w, h, xoff, yoff;
138
+
139
+ // As an optimization, this method/class could try to re-use a buffer for rasterization
140
+ // instead of having stb_truetype allocate a fresh one for each draw_glyph call.
141
+ unsigned char* pixels = stbtt_GetGlyphBitmapSubpixel(&info, scale, scale, fx - x, fy - y,
142
+ glyph, &w, &h, &xoff, &yoff);
143
+
144
+ blend_into_bitmap(bitmap, pixels, x + xoff, y + ascent * scale + yoff, w, h, c);
145
+
146
+ free(pixels);
147
+ }
148
+
149
+ // This implements the "over" alpha compositing operator, see:
150
+ // https://en.wikipedia.org/wiki/Alpha_compositing
151
+ void blend_into_bitmap(Bitmap& bitmap, const unsigned char* pixels, int x, int y, int w, int h,
152
+ Color c)
153
+ {
154
+ int stride = w;
155
+
156
+ // Instead of transferring all pixels in the range [0; w) x [0; h) into the bitmap, clip
157
+ // these values because Bitmap::set_pixel does not perform bounds checking.
158
+
159
+ int src_x = 0;
160
+ if (x < 0) {
161
+ src_x -= x;
162
+ w += x;
163
+ x = 0;
164
+ }
165
+
166
+ int src_y = 0;
167
+ if (y < 0) {
168
+ src_y -= y;
169
+ h += y;
170
+ y = 0;
171
+ }
172
+
173
+ w = min<int>(w, bitmap.width() - x);
174
+ h = min<int>(h, bitmap.height() - y);
175
+
176
+ for (int rel_y = 0; rel_y < h; ++rel_y) {
177
+ for (int rel_x = 0; rel_x < w; ++rel_x) {
178
+ auto src_alpha = pixels[(src_y + rel_y) * stride + (src_x + rel_x)] * c.alpha() / 255;
179
+ auto inv_src_alpha = 255 - src_alpha;
180
+
181
+ if (src_alpha == 0) continue;
182
+
183
+ Color dest = bitmap.get_pixel(x + rel_x, y + rel_y);
184
+ dest.set_alpha(src_alpha + (dest.alpha() * inv_src_alpha) / 255);
185
+ dest.set_red ((c.red() * src_alpha + dest.red() * inv_src_alpha) / src_alpha);
186
+ dest.set_green((c.green() * src_alpha + dest.green() * inv_src_alpha) / src_alpha);
187
+ dest.set_blue ((c.blue() * src_alpha + dest.blue() * inv_src_alpha) / src_alpha);
188
+
189
+ bitmap.set_pixel(x + rel_x, y + rel_y, dest);
190
+ }
191
+ }
192
+ }
193
+ };
194
+
195
+ Gosu::TrueTypeFont::TrueTypeFont(const unsigned char* ttf_data, shared_ptr<TrueTypeFont> fallback)
196
+ : pimpl(new Impl(ttf_data, fallback))
197
+ {
198
+ }
199
+
200
+ double Gosu::TrueTypeFont::draw_text(const u32string &text, double height,
201
+ Bitmap *bitmap, double x, double y, Color c)
202
+ {
203
+ return pimpl->draw_text(text, true, height, bitmap, x, y, c);
204
+ }
205
+
206
+ bool Gosu::TrueTypeFont::verify_font_name(const unsigned char* ttf_data, const string& font_name)
207
+ {
208
+ return stbtt_FindMatchingFont(ttf_data, font_name.c_str(), STBTT_MACSTYLE_NONE) >= 0 ||
209
+ stbtt_FindMatchingFont(ttf_data, font_name.c_str(), STBTT_MACSTYLE_DONTCARE) >= 0;
210
+ }
211
+
212
+ static Gosu::TrueTypeFont& font_with_stack(vector<const unsigned char*> ttf_stack)
213
+ {
214
+ // TODO: Make this cache thread-safe.
215
+ static map<const unsigned char*, shared_ptr<Gosu::TrueTypeFont>> cache_by_data;
216
+
217
+ // Filter out any fonts that could not be found, as well as duplicates.
218
+ auto end = unique(ttf_stack.begin(), ttf_stack.end());
219
+ end = remove(ttf_stack.begin(), end, nullptr);
220
+ ttf_stack.erase(end, ttf_stack.end());
221
+
222
+ // This cannot happen because ttf_stack contains ttf_fallback_data(), which never returns null.
223
+ if (ttf_stack.empty()) throw logic_error("Empty font stack");
224
+
225
+ shared_ptr<Gosu::TrueTypeFont> head_of_stack = nullptr;
226
+ for (const unsigned char* ttf_data : ttf_stack) {
227
+ auto& font_ptr = cache_by_data[ttf_data];
228
+ if (!font_ptr) {
229
+ font_ptr = make_shared<Gosu::TrueTypeFont>(ttf_data, head_of_stack);
230
+ }
231
+ head_of_stack = font_ptr;
232
+ }
233
+ return *head_of_stack;
234
+ }
235
+
236
+ Gosu::TrueTypeFont& Gosu::font_by_name(const string& font_name, unsigned font_flags)
237
+ {
238
+ // TODO: Make this cache thread-safe.
239
+ static map<pair<string, unsigned>, TrueTypeFont*> cache_by_name_and_flags;
240
+
241
+ auto& font_ptr = cache_by_name_and_flags[make_pair(font_name, font_flags)];
242
+ if (!font_ptr) {
243
+ // Build a stack from worst-case fallback to the desired font.
244
+ vector<const unsigned char*> ttf_stack;
245
+ ttf_stack.push_back(ttf_fallback_data());
246
+ ttf_stack.push_back(ttf_data_by_name(default_font_name(), 0));
247
+ ttf_stack.push_back(ttf_data_by_name(default_font_name(), font_flags));
248
+
249
+ if (font_name.find_first_of("./\\") != string::npos) {
250
+ // A filename? Load it and add it to the stack.
251
+ ttf_stack.push_back(ttf_data_from_file(font_name));
252
+ } else if (font_name != default_font_name()) {
253
+ // A font name? Add it to the stack, both with font_flags and without.
254
+ ttf_stack.push_back(ttf_data_by_name(font_name, 0));
255
+ ttf_stack.push_back(ttf_data_by_name(font_name, font_flags));
256
+ }
257
+
258
+ font_ptr = &font_with_stack(move(ttf_stack));
259
+ }
260
+
261
+ return *font_ptr;
262
+ }
263
+
264
+ const unsigned char* Gosu::ttf_data_from_file(const string& filename)
265
+ {
266
+ // TODO: Make this cache thread-safe.
267
+ static map<string, shared_ptr<Buffer>> ttf_file_cache;
268
+
269
+ auto& buffer_ptr = ttf_file_cache[filename];
270
+ if (!buffer_ptr) {
271
+ buffer_ptr = make_shared<Buffer>();
272
+ try {
273
+ load_file(*buffer_ptr, filename);
274
+ }
275
+ catch (...) {
276
+ // Prevent partially loaded files from getting stuck in the cache.
277
+ buffer_ptr = nullptr;
278
+ throw;
279
+ }
280
+ }
281
+ return static_cast<const unsigned char*>(buffer_ptr->data());
282
+ }
@@ -0,0 +1,66 @@
1
+ #pragma once
2
+
3
+ #include <Gosu/Bitmap.hpp>
4
+ #include <Gosu/IO.hpp>
5
+ #include <functional>
6
+ #include <memory>
7
+ #include <string>
8
+ #include <vector>
9
+
10
+ namespace Gosu
11
+ {
12
+ class TrueTypeFont
13
+ {
14
+ struct Impl;
15
+ std::shared_ptr<Impl> pimpl;
16
+
17
+ public:
18
+ //! The caller must ensure that the ttf_data pointer will remain valid indefinitely.
19
+ TrueTypeFont(const unsigned char* ttf_data, std::shared_ptr<TrueTypeFont> fallback);
20
+
21
+ //! Returns the right edge of a string when rendered onto a bitmap at the given position,
22
+ //! and with the given height.
23
+ //! If (bitmap != nullptr), the text is also rendered onto the bitmap.
24
+ double draw_text(const std::u32string& text, double height,
25
+ Bitmap* bitmap, double x, double y, Color c);
26
+
27
+ //! Returns true if the supplied buffer seems to be a font of the given name.
28
+ static bool verify_font_name(const unsigned char* ttf_data, const std::string& font_name);
29
+ };
30
+
31
+ TrueTypeFont& font_by_name(const std::string& font_name, unsigned font_flags);
32
+
33
+ //! Loads the contents of a file into memory and returns a pointer to it.
34
+ //! The pointer is guaranteed to be valid indefinitely.
35
+ //! In case of failure, this method must not return nullptr, but raise an exception.
36
+ //! Note that this method does not accept any font flags, and so it will always load the first
37
+ //! font in a TTC font collection.
38
+ const unsigned char* ttf_data_from_file(const std::string& filename);
39
+
40
+ //! This method loads a TODO
41
+ //! This method has a different implementation on each platform.
42
+ //! In case of failure, this method returns nullptr.
43
+ const unsigned char* ttf_data_by_name(const std::string& font_name, unsigned font_flags);
44
+
45
+ //! This method has a different implementation on each platform.
46
+ //! In case of failure, this method must not return nullptr, but raise an exception.
47
+ const unsigned char* ttf_fallback_data();
48
+
49
+ // TODO still true? ↓
50
+ // These functions do not yet support Gosu::FontFlags. This is fine for system fonts like Arial,
51
+ // where the callers of these methods will typically load the correct file (e.g. ArialBold.ttf).
52
+ // However, games which ship with their own font files (which is a good idea) can't use bold or
53
+ // italic text using <b> or <i> markup because there is no way to associate one TTF file as the
54
+ // "bold variant" of another.
55
+ //
56
+ // Options for the future:
57
+ // 1. Use stbtt_FindMatchingFont. This will only work for TTC font collections, and we will
58
+ // have to patch stb_truetype to look for fonts only based on `int flags`, while ignoring
59
+ // the name of fonts inside a bundle (who wants to deal with strings, anyway).
60
+ // 2. Maybe Gosu should accept filename patterns like "LibreBaskerville-*.ttf" as the font
61
+ // name and then replace the * with "Regular", "Bold", "Italic" etc.?
62
+ // 3. As a last resort, Gosu could implement faux bold and faux italics. I think faux
63
+ // underlines are a must anyway, since no font provides a dedicated TTF file for that.
64
+ // These options are not mutually exclusive.
65
+
66
+ }
@@ -0,0 +1,65 @@
1
+ #include <Gosu/Platform.hpp>
2
+ #if defined(GOSU_IS_MAC)
3
+
4
+ #include "TrueTypeFont.hpp"
5
+
6
+ #include <Gosu/IO.hpp>
7
+ #include <Gosu/Text.hpp>
8
+
9
+ #import <CoreText/CoreText.h>
10
+ #import <Foundation/Foundation.h>
11
+
12
+ #include <map>
13
+ using namespace std;
14
+
15
+ const unsigned char* Gosu::ttf_data_by_name(const string& font_name, unsigned font_flags)
16
+ {
17
+ // TODO: Make this cache thread-safe.
18
+ static map<pair<string, unsigned>, const unsigned char*> ttf_file_cache;
19
+
20
+ auto& ttf_ptr = ttf_file_cache[make_pair(font_name, font_flags)];
21
+ if (ttf_ptr) return ttf_ptr;
22
+
23
+ unsigned symbolic_traits = 0;
24
+ if (font_flags & Gosu::FF_BOLD) symbolic_traits |= kCTFontBoldTrait;
25
+ if (font_flags & Gosu::FF_ITALIC) symbolic_traits |= kCTFontItalicTrait;
26
+
27
+ NSDictionary *attributes = @{
28
+ ((__bridge id) kCTFontNameAttribute): [NSString stringWithUTF8String:font_name.c_str()],
29
+ ((__bridge id) kCTFontTraitsAttribute): @{
30
+ ((__bridge id) kCTFontSymbolicTrait): @(symbolic_traits)
31
+ }
32
+ };
33
+ CTFontDescriptorRef descriptor =
34
+ CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef) attributes);
35
+
36
+ if (descriptor) {
37
+ CTFontRef font = CTFontCreateWithFontDescriptorAndOptions(descriptor, 20, nullptr, 0);
38
+ if (font) {
39
+ NSURL *url = CFBridgingRelease(CTFontCopyAttribute(font, kCTFontURLAttribute));
40
+ if (url && url.fileSystemRepresentation) {
41
+ ttf_ptr = ttf_data_from_file(url.fileSystemRepresentation);
42
+ }
43
+ CFRelease(font);
44
+ }
45
+ CFRelease(descriptor);
46
+ }
47
+
48
+ return ttf_ptr;
49
+ }
50
+
51
+ const unsigned char* Gosu::ttf_fallback_data()
52
+ {
53
+ // Prefer Arial Unicode MS as a fallback because it covers a lot of Unicode.
54
+ static const unsigned char* arial_unicode = ttf_data_by_name("Arial Unicode MS", 0);
55
+ if (arial_unicode) return arial_unicode;
56
+
57
+ return ttf_data_from_file("/Library/Fonts/Arial.ttf");
58
+ }
59
+
60
+ string Gosu::default_font_name()
61
+ {
62
+ return "Arial";
63
+ }
64
+
65
+ #endif
@@ -0,0 +1,91 @@
1
+ #include <Gosu/Platform.hpp>
2
+ #if defined(GOSU_IS_X)
3
+
4
+ #include "TrueTypeFont.hpp"
5
+
6
+ #include <Gosu/IO.hpp>
7
+ #include <Gosu/Text.hpp>
8
+
9
+ #include <fontconfig/fontconfig.h>
10
+
11
+ #include <map>
12
+ using namespace std;
13
+
14
+ const unsigned char* Gosu::ttf_data_by_name(const string& font_name, unsigned font_flags)
15
+ {
16
+ // TODO: Make this cache thread-safe.
17
+ static map<pair<string, unsigned>, const unsigned char*> ttf_file_cache;
18
+
19
+ auto& ttf_ptr = ttf_file_cache[make_pair(font_name, font_flags)];
20
+ if (ttf_ptr) return ttf_ptr;
21
+
22
+ static FcConfig* config = FcInitLoadConfigAndFonts();
23
+ if (config) {
24
+ // Our search pattern does not include weight or slant so that we can compromise on these.
25
+ FcPattern* pattern = FcPatternBuild(nullptr,
26
+ FC_FAMILY, FcTypeString, font_name.c_str(),
27
+ FC_OUTLINE, FcTypeBool, FcTrue, /* no bitmap fonts */
28
+ nullptr);
29
+ FcObjectSet* props = FcObjectSetBuild(FC_FILE, FC_WEIGHT, FC_SLANT, nullptr);
30
+
31
+ if (FcFontSet* fonts = FcFontList(config, pattern, props)) {
32
+ // Among all matching fonts, find the variant that is the best fit for our font_flags.
33
+ string best_filename;
34
+ int best_diff;
35
+
36
+ for (int i = 0; i < fonts->nfont; ++i) {
37
+ int weight, slant;
38
+
39
+ FcPatternGetInteger(fonts->fonts[i], FC_WEIGHT, 0, &weight);
40
+ FcPatternGetInteger(fonts->fonts[i], FC_SLANT, 0, &slant);
41
+
42
+ // Difference between found font weight/slant and desired weight/slant.
43
+ // Lower is better, so find the font with the lowest diff.
44
+ int diff = 0;
45
+ if (font_flags & Gosu::FF_BOLD) {
46
+ diff += abs(weight - 200);
47
+ } else {
48
+ diff += abs(weight - 80);
49
+ }
50
+ if (font_flags & Gosu::FF_ITALIC) {
51
+ diff += abs(slant - 100);
52
+ } else {
53
+ diff += abs(slant - 0);
54
+ }
55
+
56
+ if (best_filename.empty() || diff < best_diff) {
57
+ FcChar8 *file;
58
+ FcPatternGetString(fonts->fonts[i], FC_FILE, 0, &file);
59
+ best_filename = reinterpret_cast<char*>(file);
60
+ best_diff = diff;
61
+ }
62
+ }
63
+ if (!best_filename.empty()) {
64
+ ttf_ptr = ttf_data_from_file(best_filename.c_str());
65
+ }
66
+
67
+ FcFontSetDestroy(fonts);
68
+ }
69
+
70
+ FcObjectSetDestroy(props);
71
+ FcPatternDestroy(pattern);
72
+ }
73
+
74
+ return ttf_ptr;
75
+ }
76
+
77
+ const unsigned char* Gosu::ttf_fallback_data()
78
+ {
79
+ // Prefer Unifont as a fallback because it covers a lot of Unicode.
80
+ static const unsigned char* unifont = ttf_data_by_name("Unifont", 0);
81
+ if (unifont) return unifont;
82
+
83
+ return ttf_data_from_file("/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", 0);
84
+ }
85
+
86
+ string Gosu::default_font_name()
87
+ {
88
+ return "Liberation";
89
+ }
90
+
91
+ #endif