gosu 0.13.3 → 0.14.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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