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.
- 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
|