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.
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
@@ -3,21 +3,23 @@
3
3
  #include "Texture.hpp"
4
4
  #include <Gosu/Bitmap.hpp>
5
5
  #include <Gosu/Graphics.hpp>
6
+ #include <stdexcept>
7
+
6
8
  using namespace std;
7
9
 
8
10
  void Gosu::TexChunk::set_tex_info()
9
11
  {
10
- double texture_size = texture->size();
12
+ double width = texture->width(), height = texture->height();
11
13
 
12
14
  info.tex_name = texture->tex_name();
13
- info.left = x / texture_size;
14
- info.top = y / texture_size;
15
- info.right = (x + w) / texture_size;
16
- info.bottom = (y + h) / texture_size;
15
+ info.left = x / width;
16
+ info.top = y / height;
17
+ info.right = (x + w) / width;
18
+ info.bottom = (y + h) / height;
17
19
  }
18
20
 
19
21
  Gosu::TexChunk::TexChunk(shared_ptr<Texture> texture, int x, int y, int w, int h, int padding)
20
- : texture(texture), x(x), y(y), w(w), h(h), padding(padding)
22
+ : texture(move(texture)), x(x), y(y), w(w), h(h), padding(padding)
21
23
  {
22
24
  set_tex_info();
23
25
  }
@@ -1,371 +1,84 @@
1
- #include "FormattedString.hpp"
1
+ #include "MarkupParser.hpp"
2
+ #include "TextBuilder.hpp"
2
3
  #include "GraphicsImpl.hpp"
3
- #include <Gosu/Bitmap.hpp>
4
- #include <Gosu/Graphics.hpp>
5
- #include <Gosu/Image.hpp>
6
- #include <Gosu/Math.hpp>
4
+ #include "TrueTypeFont.hpp"
7
5
  #include <Gosu/Text.hpp>
8
- #include <Gosu/Utility.hpp>
9
6
  #include <cassert>
10
7
  #include <cmath>
11
8
  #include <algorithm>
12
- #include <map>
13
9
  #include <vector>
14
10
  using namespace std;
15
11
 
16
- namespace Gosu
12
+ double Gosu::text_width(const u32string& text,
13
+ const string& font_name, double font_height, unsigned font_flags)
17
14
  {
18
- namespace
19
- {
20
- // Just a very simple heuristic that should make Chinese and Japanese text working in most
21
- // of the cases.
22
- bool is_breaking_asian_glyph(wchar_t ch)
23
- {
24
- return (ch >= 0x3040 && ch <= 0x3096) || // Hiragana
25
- (ch >= 0x30a0 && ch <= 0x30fa) || // Katakana
26
- (ch >= 0x4e00 && ch <= 0x9fff) || // CJK Unfied Ideographs
27
- (ch >= 0x3400 && ch <= 0x4db5); // CJK Unified Ideographs Extension A
28
- }
29
-
30
- struct WordInfo
31
- {
32
- FormattedString text;
33
- unsigned width;
34
- unsigned space_width;
35
- };
36
- typedef vector<WordInfo> Words;
37
-
38
- // Local helper class which manages building the bitmap from the
39
- // collected words.
40
- class TextBlockBuilder
41
- {
42
- Bitmap bmp;
43
- unsigned used_lines, allocated_lines;
44
-
45
- string font_name;
46
- unsigned font_height;
47
- int line_spacing;
48
- Alignment align;
49
-
50
- unsigned space_width_;
51
-
52
- void alloc_next_line()
53
- {
54
- ++used_lines;
55
- if (used_lines == allocated_lines) {
56
- allocated_lines += 10;
57
- bmp.resize(bmp.width(),
58
- font_height * allocated_lines + line_spacing * (allocated_lines - 1),
59
- 0x00ffffff);
60
- }
61
- }
62
-
63
- public:
64
- TextBlockBuilder(const string& font_name, unsigned font_height, int line_spacing,
65
- unsigned width, Alignment align)
66
- {
67
- used_lines = 0;
68
- allocated_lines = 10;
69
-
70
- bmp.resize(width, (line_spacing + font_height) * allocated_lines, 0x00ffffff);
71
-
72
- this->font_name = font_name;
73
- this->font_height = font_height;
74
- this->line_spacing = line_spacing;
75
- this->align = align;
76
-
77
- space_width_ = text_width(FormattedString(L" ", 0));
78
- }
79
-
80
- unsigned width() const
81
- {
82
- return bmp.width();
83
- }
84
-
85
- unsigned text_width(const FormattedString& text) const
86
- {
87
- if (text.length() == 0) {
88
- return 0;
89
- }
90
-
91
- if (text.entity_at(0)) {
92
- return entity_bitmap(text.entity_at(0)).width();
93
- }
94
-
95
- vector<FormattedString> parts = text.split_parts();
96
- unsigned result = 0;
97
- for (auto& part : parts) {
98
- string text = wstring_to_utf8(part.unformat());
99
- result += Gosu::text_width(text, font_name, font_height, part.flags_at(0));
100
- }
101
- return result;
102
- }
103
-
104
- void add_line(Words::const_iterator begin, Words::const_iterator end,
105
- unsigned words_width, bool override_align)
106
- {
107
- alloc_next_line();
108
-
109
- auto words = end - begin;
110
-
111
- unsigned total_spacing = 0;
112
- if (begin < end) {
113
- for (auto i = begin; i != end - 1; ++i) {
114
- total_spacing += i->space_width;
115
- }
116
- }
117
-
118
- // Where does the line start? (y)
119
- int top = (used_lines - 1) * (font_height + line_spacing);
120
-
121
- // Where does the line start? (x)
122
- int pos;
123
- switch (align) {
124
- // Start so that the text touches the right border.
125
- case AL_RIGHT:
126
- pos = bmp.width() - words_width - total_spacing;
127
- break;
128
-
129
- // Start so that the text is centered.
130
- case AL_CENTER:
131
- pos = bmp.width() - words_width - total_spacing;
132
- pos /= 2;
133
- break;
134
-
135
- // Just start at the left border.
136
- default:
137
- pos = 0;
138
- }
139
-
140
- for (auto cur = begin; cur != end; ++cur) {
141
- vector<FormattedString> parts = cur->text.split_parts();
142
- int x = 0;
143
- for (auto& part : parts) {
144
- if (part.entity_at(0)) {
145
- Gosu::Bitmap entity = entity_bitmap(part.entity_at(0));
146
- multiply_bitmap_alpha(entity, part.color_at(0).alpha());
147
- bmp.insert(entity, pos + x, top);
148
- x += entity.width();
149
- continue;
150
- }
151
-
152
- string unformatted_part = wstring_to_utf8(part.unformat());
153
- draw_text(bmp, unformatted_part, pos + x, top,
154
- part.color_at(0), font_name, font_height, part.flags_at(0));
155
-
156
- x += Gosu::text_width(unformatted_part, font_name, font_height,
157
- part.flags_at(0));
158
- }
159
-
160
- if (align == AL_JUSTIFY && !override_align) {
161
- pos += cur->width + 1.0 * (width() - words_width) / (words - 1);
162
- }
163
- else {
164
- pos += cur->width + cur->space_width;
165
- }
166
- }
167
- }
168
-
169
- void add_empty_line()
170
- {
171
- alloc_next_line();
172
- }
173
-
174
- Bitmap result() const
175
- {
176
- Bitmap result = bmp;
177
- result.resize(result.width(),
178
- font_height * used_lines + line_spacing * (used_lines - 1),
179
- 0x00ffffff);
180
- return result;
181
- }
182
-
183
- unsigned space_width() const
184
- {
185
- return space_width_;
186
- }
187
- };
188
-
189
- void process_words(TextBlockBuilder& builder, const Words& words)
190
- {
191
- if (words.empty()) return builder.add_empty_line();
192
-
193
- // Index into words to the first word in the current line.
194
- auto line_begin = words.begin();
195
-
196
- // Used width, in pixels, of the words [line_begin..w[.
197
- unsigned words_width = 0;
198
-
199
- // Used width of the spaces between (w-line_begin) words.
200
- unsigned spaces_width = 0;
201
-
202
- for (auto w = words.begin(); w != words.end(); ++w) {
203
- unsigned new_words_width = words_width + w->width;
204
-
205
- if (new_words_width + spaces_width <= builder.width()) {
206
- // There's enough space for the words [line_begin..w] plus
207
- // the spaces between them: Proceed with the next word.
208
- words_width = new_words_width;
209
- spaces_width += w->space_width;
210
- }
211
- else {
212
- // No, this word wouldn't fit into the current line: Draw
213
- // the current line, then start a new line with the current
214
- // word.
215
- builder.add_line(line_begin, w, words_width, false);
216
-
217
- line_begin = w;
218
- words_width = w->width;
219
- spaces_width = w->space_width;
220
- }
221
- }
222
-
223
- // Draw the last line as well.
224
- if (words.empty() || line_begin != words.end()) {
225
- builder.add_line(line_begin, words.end(), words_width, true);
226
- }
227
- }
228
-
229
- void process_paragraph(TextBlockBuilder& builder, const FormattedString& paragraph)
230
- {
231
- Words collected_words;
232
-
233
- unsigned begin_of_word = 0;
234
-
235
- for (unsigned cur = 0; cur < paragraph.length(); ++cur) {
236
- WordInfo new_word;
237
-
238
- if (paragraph.char_at(cur) == L' ') {
239
- // Whitespace:
240
- // Add last word to list if existent
241
- if (begin_of_word != cur) {
242
- new_word.text = paragraph.range(begin_of_word, cur);
243
- new_word.width = builder.text_width(new_word.text);
244
- new_word.space_width = builder.space_width();
245
- collected_words.push_back(new_word);
246
- }
247
- begin_of_word = cur + 1;
248
- }
249
- else if (is_breaking_asian_glyph(paragraph.char_at(cur))) {
250
- // Asian glyph (treat as single word):
251
- // Add last word to list if existent
252
- if (begin_of_word != cur) {
253
- new_word.text = paragraph.range(begin_of_word, cur);
254
- new_word.width = builder.text_width(new_word.text);
255
- new_word.space_width = 0;
256
- collected_words.push_back(new_word);
257
- }
258
- // Add glyph as a single "word"
259
- new_word.text = paragraph.range(cur, cur + 1);
260
- new_word.width = builder.text_width(new_word.text);
261
- new_word.space_width = 0;
262
- collected_words.push_back(new_word);
263
- begin_of_word = cur + 1;
264
- }
265
- }
266
- if (begin_of_word < paragraph.length()) {
267
- WordInfo last_word;
268
- last_word.text = paragraph.range(begin_of_word, paragraph.length());
269
- last_word.width = builder.text_width(last_word.text);
270
- last_word.space_width = 0;
271
- collected_words.push_back(last_word);
272
- }
273
-
274
- process_words(builder, collected_words);
275
- }
15
+ auto& font = font_by_name(font_name, font_flags);
16
+ return font.draw_text(text, font_height, nullptr, 0, 0, Gosu::Color::NONE);
17
+ }
276
18
 
277
- void process_text(TextBlockBuilder& builder, const FormattedString& text)
278
- {
279
- vector<FormattedString> paragraphs = text.split_lines();
280
- for (auto& paragraph : paragraphs) {
281
- process_paragraph(builder, paragraph);
282
- }
283
- }
284
- }
19
+ double Gosu::draw_text(Bitmap& bitmap, double x, double y, Color c, const u32string& text,
20
+ const string& font_name, double font_height, unsigned font_flags)
21
+ {
22
+ auto& font = font_by_name(font_name, font_flags);
23
+ return font.draw_text(text, font_height, &bitmap, x, y, c);
285
24
  }
286
25
 
287
- Gosu::Bitmap Gosu::create_text(const string& text, const string& font_name, unsigned font_height,
288
- int line_spacing, unsigned width, Alignment align, unsigned font_flags)
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)
289
28
  {
290
- if (line_spacing <= -static_cast<int>(font_height)) {
291
- throw logic_error("negative line spacing of more than line height impossible");
292
- }
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");
293
32
 
294
- wstring wtext = utf8_to_wstring(text);
295
- FormattedString fs(wtext.c_str(), font_flags);
296
- if (fs.length() == 0) {
297
- return Bitmap(width, font_height);
298
- }
33
+ TextBuilder text_builder(font_name, font_height, line_spacing, width, align);
299
34
 
300
- // Set up the builder object which will manage all the drawing and
301
- // conversions for us.
302
- TextBlockBuilder builder(font_name, font_height, line_spacing, width, align);
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();
303
40
 
304
- // Let the process* functions draw everything.
305
- process_text(builder, fs);
306
-
307
- // Done!
308
- return builder.result();
41
+ return text_builder.move_into_bitmap();
309
42
  }
310
43
 
311
- // Very easy special case.
312
- Gosu::Bitmap Gosu::create_text(const string& text, const string& font_name, unsigned font_height,
313
- unsigned font_flags)
44
+ Gosu::Bitmap Gosu::create_text(const string& text, const string& font_name, double font_height,
45
+ unsigned font_flags)
314
46
  {
315
- wstring wtext = utf8_to_wstring(text);
316
- FormattedString fs(wtext.c_str(), font_flags);
317
- if (fs.length() == 0) {
318
- return Bitmap(1, font_height);
319
- }
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();
320
60
 
321
- vector<FormattedString> lines = fs.split_lines();
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);
69
+ }
322
70
 
323
- Bitmap bmp(1, static_cast<int>(lines.size() * font_height));
71
+ Bitmap result(width, static_cast<int>(ceil(lines.size() * font_height)));
324
72
 
325
- for (int i = 0; i < lines.size(); ++i) {
326
- if (lines[i].length() == 0) continue;
327
-
328
- unsigned x = 0;
329
- vector<FormattedString> parts = lines[i].split_parts();
330
- for (auto& part : parts) {
331
- if (part.length() == 1 && part.entity_at(0)) {
332
- Gosu::Bitmap entity = entity_bitmap(part.entity_at(0));
333
- multiply_bitmap_alpha(entity, part.color_at(0).alpha());
334
- bmp.resize(max(bmp.width(), x + entity.width()), bmp.height(), 0x00ffffff);
335
- bmp.insert(entity, x, i * font_height);
336
- x += entity.width();
337
- continue;
338
- }
339
-
340
- assert (part.length() > 0);
341
- string unformatted_text = wstring_to_utf8(part.unformat());
342
- unsigned part_width = text_width(unformatted_text, font_name, font_height,
343
- part.flags_at(0));
344
- bmp.resize(max(bmp.width(), x + part_width), bmp.height(), 0x00ffffff);
345
- draw_text(bmp, unformatted_text, x, i * font_height, part.color_at(0), font_name,
346
- font_height, part.flags_at(0));
347
- x += part_width;
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);
348
79
  }
80
+ y += font_height;
349
81
  }
350
82
 
351
- return bmp;
352
- }
353
-
354
- static map<string, shared_ptr<Gosu::Bitmap>> entities;
355
-
356
- void Gosu::register_entity(const string& name, const Gosu::Bitmap& replacement)
357
- {
358
- entities[name].reset(new Bitmap(replacement));
359
- }
360
-
361
- bool Gosu::is_entity(const string& name)
362
- {
363
- return entities[name].get();
364
- }
365
-
366
- const Gosu::Bitmap& Gosu::entity_bitmap(const string& name)
367
- {
368
- shared_ptr<Gosu::Bitmap>& ptr = entities[name];
369
- if (!ptr) throw runtime_error("Unknown entity: " + name);
370
- return *ptr;
83
+ return result;
371
84
  }