gosu 0.14.0.pre2 → 0.14.0

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.
data/src/Text.cpp CHANGED
@@ -12,6 +12,9 @@ using namespace std;
12
12
  double Gosu::text_width(const u32string& text,
13
13
  const string& font_name, double font_height, unsigned font_flags)
14
14
  {
15
+ if (font_height <= 0) throw invalid_argument("font_height must be > 0");
16
+ if (font_flags >= FF_COMBINATIONS) throw invalid_argument("Invalid font_flags");
17
+
15
18
  auto& font = font_by_name(font_name, font_flags);
16
19
  return font.draw_text(text, font_height, nullptr, 0, 0, Gosu::Color::NONE);
17
20
  }
@@ -19,66 +22,90 @@ double Gosu::text_width(const u32string& text,
19
22
  double Gosu::draw_text(Bitmap& bitmap, double x, double y, Color c, const u32string& text,
20
23
  const string& font_name, double font_height, unsigned font_flags)
21
24
  {
25
+ if (font_height <= 0) throw invalid_argument("font_height must be > 0");
26
+ if (font_flags >= FF_COMBINATIONS) throw invalid_argument("Invalid font_flags");
27
+
22
28
  auto& font = font_by_name(font_name, font_flags);
23
29
  return font.draw_text(text, font_height, &bitmap, x, y, c);
24
30
  }
25
31
 
26
- Gosu::Bitmap Gosu::create_text(const string& text, const string& font_name, double font_height,
27
- double line_spacing, int width, Alignment align, unsigned font_flags)
32
+ Gosu::Bitmap Gosu::layout_text(const string& text, const string& font_name,
33
+ double font_height, double line_spacing,
34
+ int width, Alignment align, unsigned font_flags)
28
35
  {
29
- if (font_height <= 0) throw invalid_argument("font_height must be > 0");
30
- if (width <= 0) throw invalid_argument("width must be > 0");
31
- if (line_spacing < -font_height) throw invalid_argument("line_spacing must be ≥ -font_height");
32
-
33
- TextBuilder text_builder(font_name, font_height, line_spacing, width, align);
34
-
35
- // Feed all formatted substrings to the TextBuilder, which will construct the resulting bitmap.
36
- // Split the input string into words here, because this method implements word-wrapping.
37
- MarkupParser(text.c_str(), font_flags, true, [&text_builder](vector<FormattedString> word) {
38
- text_builder.feed_word(move(word));
39
- }).parse();
40
-
41
- return text_builder.move_into_bitmap();
36
+ return layout_markup(escape_markup(text), font_name,
37
+ font_height, line_spacing,
38
+ width, align, font_flags);
42
39
  }
43
40
 
44
- Gosu::Bitmap Gosu::create_text(const string& text, const string& font_name, double font_height,
45
- unsigned font_flags)
41
+ Gosu::Bitmap Gosu::layout_markup(const string& markup, const string& font_name,
42
+ double font_height, double line_spacing,
43
+ int width, Alignment align, unsigned font_flags)
46
44
  {
47
- if (font_height <= 0) throw invalid_argument("font_height must be > 0");
48
-
49
- vector<vector<FormattedString>> lines;
50
-
51
- // Split the text into lines (split_words = false) since this method does not break lines.
52
- MarkupParser(text.c_str(), font_flags, false, [&lines](vector<FormattedString>&& line) {
53
- // Remove trailing \n characters from each line to avoid errors from Gosu::text_width().
54
- if (line.back().text.back() == '\n') {
55
- line.back().text.pop_back();
56
- }
57
-
58
- lines.emplace_back(line);
59
- }).parse();
45
+ if (font_height <= 0) throw invalid_argument("font_height must be > 0");
46
+ if (line_spacing < -font_height) throw invalid_argument("line_spacing must be ≥ -font_height");
47
+ if (font_flags >= FF_COMBINATIONS) throw invalid_argument("Invalid font_flags");
60
48
 
61
- // Measure every part of every line.
62
- int width = 0;
63
- for (auto& line : lines) {
64
- int line_width = 0;
65
- for (auto& part : line) {
66
- line_width += text_width(part.text, font_name, font_height, part.flags);
67
- }
68
- width = max(width, line_width);
49
+ if (width >= 0) {
50
+ TextBuilder text_builder(font_name, font_height, line_spacing, width, align);
51
+
52
+ // Feed all formatted substrings to the TextBuilder, which will construct the result.
53
+ // Split the input string into words, because this method implements word-wrapping.
54
+ MarkupParser parser(font_flags, true, [&text_builder](vector<FormattedString> word) {
55
+ text_builder.feed_word(move(word));
56
+ });
57
+ parser.parse(markup);
58
+
59
+ return text_builder.move_into_bitmap();
69
60
  }
70
-
71
- Bitmap result(width, static_cast<int>(ceil(lines.size() * font_height)));
72
-
73
- // Render every part of every line.
74
- int y = 0;
75
- for (auto& line : lines) {
76
- int x = 0;
77
- for (auto& part : line) {
78
- x += draw_text(result, x, y, part.color, part.text, font_name, font_height);
61
+ else {
62
+ vector<vector<FormattedString>> lines;
63
+
64
+ // Split the text into lines (split_words = false) since this method does not wrap lines.
65
+ MarkupParser parser(font_flags, false, [&lines](vector<FormattedString>&& line) {
66
+ // Remove trailing \n characters from each line to avoid errors from Gosu::text_width().
67
+ if (line.back().text.back() == '\n') {
68
+ line.back().text.pop_back();
69
+ }
70
+
71
+ lines.emplace_back(line);
72
+ });
73
+ parser.parse(markup);
74
+
75
+ if (lines.empty()) return Bitmap();
76
+
77
+ // Measure every part of every line.
78
+ vector<double> line_widths;
79
+ double max_width = 0;
80
+ for (auto& line : lines) {
81
+ line_widths.push_back(0);
82
+ for (auto& part : line) {
83
+ line_widths.back() += text_width(part.text, font_name, font_height, part.flags);
84
+ }
85
+ max_width = max(max_width, line_widths.back());
79
86
  }
80
- y += font_height;
87
+
88
+ double height = lines.size() * font_height + (lines.size() - 1) * line_spacing;
89
+ Bitmap result(ceil(max_width), ceil(height));
90
+
91
+ // Render every part of every line.
92
+ double y = 0;
93
+ for (int i = 0; i < lines.size(); ++i) {
94
+ double x = 0;
95
+ if (align == AL_CENTER) {
96
+ x = (result.width() - line_widths[i]) / 2;
97
+ }
98
+ else if (align == AL_RIGHT) {
99
+ x = result.width() - line_widths[i];
100
+ }
101
+
102
+ for (auto& part : lines[i]) {
103
+ x = draw_text(result, x, y, part.color, part.text,
104
+ font_name, font_height, part.flags);
105
+ }
106
+ y += (font_height + line_spacing);
107
+ }
108
+
109
+ return result;
81
110
  }
82
-
83
- return result;
84
111
  }
data/src/TextBuilder.cpp CHANGED
@@ -21,7 +21,7 @@ Gosu::WordInfo::WordInfo(const string& font_name, double font_height, vector<For
21
21
 
22
22
  width = 0;
23
23
  for (const auto& part : parts) {
24
- assert (! part.text.empty());
24
+ assert (is_end_of_line || ! part.text.empty());
25
25
 
26
26
  width += text_width(part.text, font_name, font_height, part.flags);
27
27
  }
data/src/TimingUnix.cpp CHANGED
@@ -17,7 +17,7 @@ unsigned long Gosu::milliseconds()
17
17
  timeval tp;
18
18
  gettimeofday(&tp, nullptr);
19
19
 
20
- if (!start) {
20
+ if (start == 0) {
21
21
  start = tp.tv_usec / 1000UL + tp.tv_sec * 1000UL;
22
22
  }
23
23
 
data/src/TrueTypeFont.cpp CHANGED
@@ -70,16 +70,8 @@ struct Gosu::TrueTypeFont::Impl
70
70
  // Missing characters often come in clusters, so build a substring of
71
71
  // codepoints that this font doesn't contain and then defer to the fallback
72
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
- }
73
+ u32string fallback_string = string_of_missing_glyphs(text, index);
74
+ index += fallback_string.length();
83
75
  x = fallback->pimpl->draw_text(fallback_string, index == text.size(), height,
84
76
  bitmap, x, y, c);
85
77
  last_glyph = 0;
@@ -130,6 +122,21 @@ struct Gosu::TrueTypeFont::Impl
130
122
  return max<double>(0, x);
131
123
  }
132
124
 
125
+ u32string string_of_missing_glyphs(const u32string& text, u32string::size_type from_index)
126
+ {
127
+ u32string result;
128
+
129
+ for (u32string::size_type index = from_index; index < text.size(); ++index) {
130
+ auto codepoint = text[index];
131
+ // Stop as soon as a glyph (except control characters) is available in the current font.
132
+ if (codepoint >= ' ' && stbtt_FindGlyphIndex(&info, codepoint) != 0) break;
133
+
134
+ result.push_back(codepoint);
135
+ }
136
+
137
+ return result;
138
+ }
139
+
133
140
  void draw_glyph(Bitmap& bitmap, double fx, double fy, Color c, int glyph, double scale)
134
141
  {
135
142
  int x = static_cast<int>(fx);
@@ -146,15 +153,13 @@ struct Gosu::TrueTypeFont::Impl
146
153
  free(pixels);
147
154
  }
148
155
 
149
- // This implements the "over" alpha compositing operator, see:
150
- // https://en.wikipedia.org/wiki/Alpha_compositing
151
156
  void blend_into_bitmap(Bitmap& bitmap, const unsigned char* pixels, int x, int y, int w, int h,
152
157
  Color c)
153
158
  {
154
159
  int stride = w;
155
160
 
156
161
  // 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.
162
+ // these values because Bitmap::blend_pixel does not perform bounds checking.
158
163
 
159
164
  int src_x = 0;
160
165
  if (x < 0) {
@@ -175,18 +180,9 @@ struct Gosu::TrueTypeFont::Impl
175
180
 
176
181
  for (int rel_y = 0; rel_y < h; ++rel_y) {
177
182
  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);
183
+ int pixel = pixels[(src_y + rel_y) * stride + src_x + rel_x];
184
+ Color c_with_alpha(pixel * c.alpha() / 255, c.red(), c.green(), c.blue());
185
+ bitmap.blend_pixel(x + rel_x, y + rel_y, c_with_alpha);
190
186
  }
191
187
  }
192
188
  }
@@ -203,9 +199,12 @@ double Gosu::TrueTypeFont::draw_text(const u32string &text, double height,
203
199
  return pimpl->draw_text(text, true, height, bitmap, x, y, c);
204
200
  }
205
201
 
206
- bool Gosu::TrueTypeFont::verify_font_name(const unsigned char* ttf_data, const string& font_name)
202
+ bool Gosu::TrueTypeFont::verify_font_name(const unsigned char* ttf_data, const string& font_name, unsigned font_flags)
207
203
  {
208
- return stbtt_FindMatchingFont(ttf_data, font_name.c_str(), STBTT_MACSTYLE_NONE) >= 0 ||
204
+ // Gosu's FontFlags enum mostly uses the same values as the STBTT_ macros.
205
+ int flags = (font_flags == 0 ? STBTT_MACSTYLE_NONE : font_flags);
206
+
207
+ return stbtt_FindMatchingFont(ttf_data, font_name.c_str(), font_flags) >= 0 ||
209
208
  stbtt_FindMatchingFont(ttf_data, font_name.c_str(), STBTT_MACSTYLE_DONTCARE) >= 0;
210
209
  }
211
210
 
data/src/TrueTypeFont.hpp CHANGED
@@ -25,7 +25,8 @@ namespace Gosu
25
25
  Bitmap* bitmap, double x, double y, Color c);
26
26
 
27
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);
28
+ static bool verify_font_name(const unsigned char* ttf_data,
29
+ const std::string& font_name, unsigned font_flags);
29
30
  };
30
31
 
31
32
  TrueTypeFont& font_by_name(const std::string& font_name, unsigned font_flags);
@@ -80,7 +80,7 @@ const unsigned char* Gosu::ttf_fallback_data()
80
80
  static const unsigned char* unifont = ttf_data_by_name("Unifont", 0);
81
81
  if (unifont) return unifont;
82
82
 
83
- return ttf_data_from_file("/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", 0);
83
+ return ttf_data_from_file("/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf");
84
84
  }
85
85
 
86
86
  string Gosu::default_font_name()
@@ -27,16 +27,16 @@ const unsigned char* Gosu::ttf_data_by_name(const string& font_name, unsigned fo
27
27
  if (buffer_ptr) return static_cast<const unsigned char*>(buffer_ptr->data());
28
28
 
29
29
  LOGFONT logfont = {
30
- 20 /* arbitrary font height */, 0, 0, 0,
30
+ 0, 0, 0, 0,
31
31
  (font_flags & Gosu::FF_BOLD) ? FW_BOLD : FW_NORMAL,
32
32
  (font_flags & Gosu::FF_ITALIC) ? TRUE : FALSE,
33
33
  (font_flags & Gosu::FF_UNDERLINE) ? TRUE : FALSE,
34
34
  FALSE /* no strikethrough */,
35
- DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
35
+ ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
36
36
  DEFAULT_PITCH | FF_DONTCARE
37
37
  };
38
38
 
39
- wstring wfont_name = utf8_to_wstring(font_name);
39
+ wstring wfont_name = utf8_to_utf16(font_name);
40
40
  wcsncpy(logfont.lfFaceName, wfont_name.c_str(), LF_FACESIZE);
41
41
  logfont.lfFaceName[LF_FACESIZE - 1] = 0;
42
42
 
@@ -49,7 +49,7 @@ const unsigned char* Gosu::ttf_data_by_name(const string& font_name, unsigned fo
49
49
  buffer->resize(ttf_buffer_size);
50
50
  if (GetFontData(hdc, 0, 0, buffer->data(), buffer->size()) != GDI_ERROR) {
51
51
  auto data = static_cast<const unsigned char*>(buffer->data());
52
- if (TrueTypeFont::verify_font_name(data, font_name)) {
52
+ if (TrueTypeFont::verify_font_name(data, font_name, font_flags)) {
53
53
  buffer_ptr = buffer;
54
54
  }
55
55
  }
data/src/Utility.cpp CHANGED
@@ -3,64 +3,10 @@
3
3
 
4
4
  #include "utf8proc.h"
5
5
 
6
- #include <cstddef>
7
- #include <cstdlib>
8
6
  #include <cstring>
9
- #include <cwchar>
10
- #include <cwctype>
11
- #include <algorithm>
12
7
  #include <stdexcept>
13
- #include <vector>
14
8
  using namespace std;
15
9
 
16
- #ifndef GOSU_IS_IPHONE
17
-
18
- #ifndef GOSU_IS_WIN
19
- #include "Iconv.hpp"
20
- #endif
21
- using namespace std;
22
-
23
- #ifndef GOSU_IS_WIN
24
- namespace
25
- {
26
- extern const char UTF_8[] = "UTF-8";
27
- #ifdef __BIG_ENDIAN__
28
- extern const char UCS_4_INTERNAL[] = "UCS-4BE";
29
- #else
30
- extern const char UCS_4_INTERNAL[] = "UCS-4LE";
31
- #endif
32
- }
33
-
34
- wstring Gosu::utf8_to_wstring(const string& s)
35
- {
36
- return iconvert<wstring, UCS_4_INTERNAL, UTF_8>(s);
37
- }
38
- string Gosu::wstring_to_utf8(const wstring& ws)
39
- {
40
- return iconvert<string, UTF_8, UCS_4_INTERNAL>(ws);
41
- }
42
-
43
- #else
44
- #ifndef NOMINMAX
45
- #define NOMINMAX
46
- #endif
47
- #include <windows.h>
48
- wstring Gosu::utf8_to_wstring(const string& utf8)
49
- {
50
- vector<wchar_t> buffer(utf8.size() + 1);
51
- MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), utf8.size() + 1, &buffer[0], buffer.size());
52
- return &buffer[0];
53
- }
54
- string Gosu::wstring_to_utf8(const wstring& ws)
55
- {
56
- unsigned size = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), 0, 0, 0, 0);
57
- vector<char> buffer(size + 1);
58
- WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), &buffer[0], buffer.size(), 0, 0);
59
- return &buffer[0];
60
- }
61
- #endif
62
- #endif
63
-
64
10
  u32string Gosu::utf8_to_composed_utc4(const string& utf8)
65
11
  {
66
12
  u32string utc4;
data/src/UtilityApple.cpp CHANGED
@@ -3,43 +3,8 @@
3
3
 
4
4
  #import <Gosu/Utility.hpp>
5
5
  #import <Foundation/Foundation.h>
6
- #import <stdexcept>
7
- #import <vector>
8
6
  using namespace std;
9
7
 
10
- #ifdef GOSU_IS_IPHONE
11
- wstring Gosu::utf8_to_wstring(const string& s)
12
- {
13
- if (s.empty()) return wstring();
14
-
15
- NSString* string = [NSString stringWithUTF8String:s.c_str()];
16
- vector<wchar_t> buffer(s.size());
17
- NSUInteger buffer_size;
18
- if (![string getBytes:&buffer[0]
19
- maxLength:buffer.size() * sizeof(wchar_t)
20
- usedLength:&buffer_size
21
- encoding:NSUTF32LittleEndianStringEncoding
22
- options:0
23
- range:NSMakeRange(0, string.length)
24
- remainingRange:nullptr]) {
25
- throw runtime_error("String " + s + " could not be converted to UTF-32");
26
- }
27
- return wstring(&buffer[0], &buffer[0] + buffer_size / sizeof(wchar_t));
28
- }
29
-
30
- string Gosu::wstring_to_utf8(const wstring& ws)
31
- {
32
- if (ws.empty()) return string();
33
-
34
- @autoreleasepool {
35
- NSString* string = [[NSString alloc] initWithBytes:ws.data()
36
- length:ws.size() * sizeof(wchar_t)
37
- encoding:NSUTF32LittleEndianStringEncoding];
38
- return string.UTF8String ?: "";
39
- }
40
- }
41
- #endif
42
-
43
8
  string Gosu::language()
44
9
  {
45
10
  @autoreleasepool {
data/src/WinUtility.cpp CHANGED
@@ -1,42 +1,61 @@
1
- #include <Gosu/Platform.hpp>
2
- #if defined(GOSU_IS_WIN)
3
-
4
- #include "WinUtility.hpp"
5
- #include <Gosu/Utility.hpp>
6
- #include <stdexcept>
1
+ #include <Gosu/Platform.hpp>
2
+ #if defined(GOSU_IS_WIN)
3
+
4
+ #include "WinUtility.hpp"
5
+ #include <Gosu/Utility.hpp>
6
+ #include <stdexcept>
7
7
  #include <windows.h>
8
- using namespace std;
9
-
10
- void Gosu::throw_last_winapi_error(const string& action)
11
- {
12
- // Obtain error message from Windows.
13
- wchar_t* buffer;
14
-
15
- if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
16
- FORMAT_MESSAGE_FROM_SYSTEM |
17
- FORMAT_MESSAGE_IGNORE_INSERTS, 0, GetLastError(),
18
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &buffer, 0, 0)
19
- || buffer == nullptr) {
20
- throw runtime_error("Unknown error");
21
- }
22
-
23
- // Safely move the message into a string.
24
- string message;
25
- try {
26
- message = wstring_to_utf8(buffer);
27
- }
28
- catch (...) {
29
- LocalFree(buffer);
30
- throw;
31
- }
32
- LocalFree(buffer);
33
-
34
- // Optionally prepend the action.
35
- if (!action.empty()) {
36
- message = "While " + action + ", the following error occured: " + message;
37
- }
38
-
39
- throw runtime_error(message);
40
- }
41
-
42
- #endif
8
+ using namespace std;
9
+
10
+ wstring Gosu::utf8_to_utf16(const string& utf8)
11
+ {
12
+ wstring utf16(utf8.size(), '\0');
13
+ auto len = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), utf8.size(),
14
+ const_cast<wchar_t*>(utf16.data()), utf16.size());
15
+ utf16.resize(len);
16
+ return utf16;
17
+ }
18
+
19
+ string Gosu::utf16_to_utf8(const wstring& utf16)
20
+ {
21
+ auto len = WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), utf16.size(),
22
+ nullptr, 0, nullptr, nullptr);
23
+ string utf8(len, '\0');
24
+ WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), utf16.size(),
25
+ const_cast<char*>(utf8.data()), utf8.size(), nullptr, nullptr);
26
+ return utf8;
27
+ }
28
+
29
+ void Gosu::throw_last_winapi_error(const string& action)
30
+ {
31
+ // Obtain error message from Windows.
32
+ wchar_t* buffer;
33
+
34
+ if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
35
+ FORMAT_MESSAGE_FROM_SYSTEM |
36
+ FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(),
37
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &buffer, 0, nullptr)
38
+ || buffer == nullptr) {
39
+ throw runtime_error("Unknown error");
40
+ }
41
+
42
+ // Safely move the message into a string.
43
+ string message;
44
+ try {
45
+ message = utf16_to_utf8(buffer);
46
+ }
47
+ catch (...) {
48
+ LocalFree(buffer);
49
+ throw;
50
+ }
51
+ LocalFree(buffer);
52
+
53
+ // Optionally prepend the action.
54
+ if (!action.empty()) {
55
+ message = "While " + action + ", the following error occured: " + message;
56
+ }
57
+
58
+ throw runtime_error(message);
59
+ }
60
+
61
+ #endif