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