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.
- checksums.yaml +4 -4
- data/Gosu/Audio.hpp +15 -11
- data/Gosu/Font.hpp +24 -20
- data/Gosu/Fwd.hpp +1 -1
- data/Gosu/Graphics.hpp +8 -9
- data/Gosu/ImageData.hpp +1 -1
- data/Gosu/Input.hpp +1 -1
- data/Gosu/Math.hpp +0 -18
- data/Gosu/Text.hpp +22 -30
- data/Gosu/TextInput.hpp +13 -0
- data/Gosu/Utility.hpp +2 -0
- data/Gosu/Window.hpp +3 -3
- data/README.md +3 -4
- data/ext/gosu/extconf.rb +7 -9
- data/lib/gosu/swig_patches.rb +1 -4
- data/rdoc/gosu.rb +34 -9
- data/src/Audio.cpp +6 -6
- data/src/AudioImpl.cpp +2 -2
- data/src/Bitmap.cpp +1 -2
- data/src/BitmapIO.cpp +21 -2
- data/src/BlockAllocator.cpp +1 -1
- data/src/Channel.cpp +7 -1
- data/src/ClipRectStack.hpp +4 -1
- data/src/Color.cpp +2 -1
- data/src/DirectoriesWin.cpp +1 -1
- data/src/DrawOp.hpp +8 -4
- data/src/DrawOpQueue.hpp +13 -24
- data/src/FileUnix.cpp +3 -1
- data/src/Font.cpp +92 -96
- data/src/GosuGLView.cpp +59 -31
- data/src/GosuGLView.hpp +14 -0
- data/src/GosuViewController.cpp +21 -21
- data/src/{GosuViewController.h → GosuViewController.hpp} +2 -4
- data/src/Graphics.cpp +71 -38
- data/src/GraphicsImpl.hpp +12 -29
- data/src/Image.cpp +5 -7
- data/src/Input.cpp +7 -5
- data/src/InputUIKit.cpp +19 -37
- data/src/Macro.cpp +10 -2
- data/src/MarkupParser.cpp +241 -0
- data/src/MarkupParser.hpp +61 -0
- data/src/Math.cpp +1 -1
- data/src/OffScreenTarget.cpp +99 -0
- data/src/OffScreenTarget.hpp +23 -0
- data/src/OggFile.hpp +10 -0
- data/src/RenderState.hpp +0 -2
- data/src/Resolution.cpp +2 -2
- data/src/RubyGosu.cxx +457 -244
- data/src/TexChunk.cpp +8 -6
- data/src/Text.cpp +58 -345
- data/src/TextBuilder.cpp +138 -0
- data/src/TextBuilder.hpp +55 -0
- data/src/TextInput.cpp +27 -10
- data/src/Texture.cpp +22 -17
- data/src/Texture.hpp +19 -20
- data/src/TimingApple.cpp +5 -7
- data/src/TimingUnix.cpp +1 -4
- data/src/TimingWin.cpp +4 -1
- data/src/TrueTypeFont.cpp +282 -0
- data/src/TrueTypeFont.hpp +66 -0
- data/src/TrueTypeFontApple.cpp +65 -0
- data/src/TrueTypeFontUnix.cpp +91 -0
- data/src/TrueTypeFontWin.cpp +82 -0
- data/src/Utility.cpp +40 -0
- data/src/Window.cpp +7 -6
- data/src/WindowUIKit.cpp +9 -4
- data/src/stb_truetype.h +4589 -0
- data/src/utf8proc.c +755 -0
- data/src/utf8proc.h +699 -0
- data/src/utf8proc_data.h +14386 -0
- metadata +23 -16
- data/src/FormattedString.cpp +0 -237
- data/src/FormattedString.hpp +0 -47
- data/src/GosuAppDelegate.cpp +0 -30
- data/src/GosuAppDelegate.h +0 -8
- data/src/GosuGLView.h +0 -8
- data/src/TextApple.cpp +0 -212
- data/src/TextTTFWin.cpp +0 -197
- data/src/TextUnix.cpp +0 -280
- 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
|