gosu 0.14.0.pre2 → 0.14.0

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