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
@@ -1,6 +1,3 @@
1
- # SWIG workarounds
2
- # These are offloaded into a separate file because rb_eval_string() is weird on Ruby 1.8.
3
-
4
1
  # Exceptions in Window callbacks often get lost, this is especially annoying in draw/update.
5
2
  # It is not clear whether this is a SWIG issue or if some stack frame is not exception
6
3
  # compatible, but I just call protected_update etc. in the Ruby wrapper so I can add this
@@ -66,7 +63,7 @@ module Gosu
66
63
  end
67
64
  end
68
65
 
69
- # SWIG won't let me rename my method to '[]='.
66
+ # SWIG will not let me rename my method to '[]=', so use alias here.
70
67
  class Gosu::Font
71
68
  alias []= set_image
72
69
  end
@@ -679,9 +679,20 @@ module Gosu
679
679
  # def input.filter(text_in)
680
680
  # text_in.upcase.gsub(/[^A-Z0-9]/, '')
681
681
  # end
682
- def filter text_in
683
- text_in
684
- end
682
+ def filter; end
683
+
684
+ ##
685
+ # Replaces the current selection (if any) and inserts the given string at the current caret position.
686
+ # The filter method will not be applied before appending the string.
687
+ def insert_text(str); end
688
+
689
+ ##
690
+ # Deletes the current selection, if any, or the next character.
691
+ def delete_forward; end
692
+
693
+ ##
694
+ # Deletes the current selection, if any, or the previous character.
695
+ def delete_backward; end
685
696
  end
686
697
 
687
698
  ##
@@ -1024,21 +1035,35 @@ module Gosu
1024
1035
  def clip_to(x, y, w, h); end
1025
1036
 
1026
1037
  ##
1027
- # Records all drawing operations inside the block as a reusable "image". This method can be used to speed rendering of multiple static images, e.g., a fixed tile map.
1038
+ # Records all drawing operations inside the block as a macro (a special {Gosu::Image}) that can be reused later on.
1039
+ # This is useful for rendering larger groups of images (e.g. a tiled map in a game) and then rendering this group with a single call to {Gosu::Image#draw}.
1028
1040
  #
1029
- # @note Because the returned object is not a true image---it's implemented using vertex buffers and is not backed by a texture---there are restrictions on how it can be used.
1041
+ # @note Because the returned object is not backed by a bitmap texture, there are restrictions on how it can be used. For example, you can not use any color other than {Gosu::Color#WHITE} when drawing the image.
1030
1042
  #
1031
- # @note The width and height of the returned object will be the same values you passed to {record}, regardless of the area you draw on. It is important to pass accurate values if you plan on using {Gosu::Image#draw_as_quad} or {Gosu::Image#draw_rot} with the result later.
1043
+ # @note The width and height of the returned image will be the values you passed to {record}, regardless of the area you draw on.
1044
+ # It is important to pass accurate values if you plan on calling {Gosu::Image#draw_as_quad} or {Gosu::Image#draw_rot} on the result later.
1032
1045
  #
1033
1046
  # @return [Gosu::Image] the recorded drawing operations.
1034
- # @param width [Float] the width of the recorded image.
1035
- # @param height [Float] the height of the recorded image.
1047
+ # @param width [Integer] the width of the recorded image.
1048
+ # @param height [Integer] the height of the recorded image.
1036
1049
  # @yield rendering code.
1037
1050
  #
1038
- # @see Window#draw
1051
+ # @see Window#render
1039
1052
  # @see Gosu::Image
1040
1053
  def record(width, height); end
1041
1054
 
1055
+ ##
1056
+ # Records all drawing operations inside the block and returns the result as a new {Gosu::Image}.
1057
+ #
1058
+ # @return [Gosu::Image] the rendered drawing operations.
1059
+ # @param width [Integer] the width of the recorded image.
1060
+ # @param height [Integer] the height of the recorded image.
1061
+ # @yield rendering code.
1062
+ #
1063
+ # @see Window#record
1064
+ # @see Gosu::Image
1065
+ def render(width, height); end
1066
+
1042
1067
  ##
1043
1068
  # Rotates all drawing operations inside the block.
1044
1069
  #
@@ -45,7 +45,7 @@ static bool cur_song_looping;
45
45
 
46
46
  struct Gosu::Sample::SampleData
47
47
  {
48
- ALuint buffer, source;
48
+ ALuint buffer;
49
49
 
50
50
  SampleData(AudioFile& audio_file)
51
51
  {
@@ -114,7 +114,7 @@ Gosu::Channel Gosu::Sample::play(double volume, double speed, bool looping) cons
114
114
 
115
115
  Gosu::Channel Gosu::Sample::play_pan(double pan, double volume, double speed, bool looping) const
116
116
  {
117
- if (!data) return Channel(NO_CHANNEL, 0);
117
+ if (!data) return Channel();
118
118
 
119
119
  Channel channel = allocate_channel();
120
120
 
@@ -133,8 +133,8 @@ Gosu::Channel Gosu::Sample::play_pan(double pan, double volume, double speed, bo
133
133
 
134
134
  class Gosu::Song::BaseData
135
135
  {
136
- BaseData(const BaseData&);
137
- BaseData& operator=(const BaseData&);
136
+ BaseData(const BaseData&) = delete;
137
+ BaseData& operator=(const BaseData&) = delete;
138
138
 
139
139
  double volume_;
140
140
 
@@ -169,7 +169,7 @@ class Gosu::Song::ModuleData : public BaseData
169
169
  {
170
170
  AVAudioPlayer* player;
171
171
 
172
- void apply_volume()
172
+ void apply_volume() override
173
173
  {
174
174
  player.volume = volume();
175
175
  }
@@ -203,7 +203,7 @@ public:
203
203
  bool paused() const override
204
204
  {
205
205
  return !player.playing;
206
- };
206
+ }
207
207
 
208
208
  void stop() override
209
209
  {
@@ -72,7 +72,7 @@ Gosu::Channel Gosu::allocate_channel()
72
72
  }
73
73
 
74
74
  // No free channel, return an object that is immediately expired.
75
- return Channel(NO_CHANNEL, 0);
75
+ return Channel();
76
76
  }
77
77
 
78
78
  bool Gosu::channel_expired(int channel, int token)
@@ -82,7 +82,7 @@ bool Gosu::channel_expired(int channel, int token)
82
82
 
83
83
  ALuint Gosu::al_source_for_channel(int channel)
84
84
  {
85
- if (channel <= 0 || channel >= CHANNELS) {
85
+ if (channel < 0 || channel >= CHANNELS) {
86
86
  throw invalid_argument("No such channel: " + to_string(channel));
87
87
  }
88
88
  return _sources[channel];
@@ -13,8 +13,7 @@ void Gosu::Bitmap::swap(Bitmap& other)
13
13
 
14
14
  void Gosu::Bitmap::resize(unsigned width, unsigned height, Color c)
15
15
  {
16
- if (width == w && height == h)
17
- return;
16
+ if (width == w && height == h) return;
18
17
 
19
18
  Bitmap temp(width, height, c);
20
19
  temp.insert(*this, 0, 0);
@@ -9,8 +9,18 @@
9
9
  #define STBI_NO_STDIO
10
10
  #define STBI_NO_LINEAR
11
11
 
12
+ // Disable comma warnings in stb headers.
13
+ #ifdef __GNUC__
14
+ #pragma GCC diagnostic push
15
+ #pragma GCC diagnostic ignored "-Wcomma"
16
+ #endif
17
+
12
18
  #include "stb_image.h"
13
19
 
20
+ #ifdef __GNUC__
21
+ #pragma GCC diagnostic pop
22
+ #endif
23
+
14
24
  using namespace std;
15
25
 
16
26
  namespace
@@ -80,9 +90,19 @@ void Gosu::load_image_file(Gosu::Bitmap& bitmap, Reader input)
80
90
  }
81
91
  }
82
92
 
93
+ // Disable comma warnings in stb headers.
94
+ #ifdef __GNUC__
95
+ #pragma GCC diagnostic push
96
+ #pragma GCC diagnostic ignored "-Wcomma"
97
+ #endif
98
+
83
99
  #define STB_IMAGE_WRITE_IMPLEMENTATION
84
100
  #include "stb_image_write.h"
85
101
 
102
+ #ifdef __GNUC__
103
+ #pragma GCC diagnostic pop
104
+ #endif
105
+
86
106
  void Gosu::save_image_file(const Gosu::Bitmap& bitmap, const string& filename)
87
107
  {
88
108
  int ok;
@@ -102,8 +122,7 @@ void Gosu::save_image_file(const Gosu::Bitmap& bitmap, const string& filename)
102
122
 
103
123
  static void stbi_write_to_writer(void* context, void* data, int size)
104
124
  {
105
- Gosu::Writer* writer = reinterpret_cast<Gosu::Writer*>(context);
106
- writer->write(data, size);
125
+ reinterpret_cast<Gosu::Writer*>(context)->write(data, size);
107
126
  }
108
127
 
109
128
  void Gosu::save_image_file(const Gosu::Bitmap& bitmap, Gosu::Writer writer,
@@ -111,7 +111,7 @@ bool Gosu::BlockAllocator::alloc(unsigned a_width, unsigned a_height, Block& b)
111
111
 
112
112
  void Gosu::BlockAllocator::block(unsigned left, unsigned top, unsigned width, unsigned height)
113
113
  {
114
- pimpl->blocks.push_back(Block(left, top, width, height));
114
+ pimpl->blocks.emplace_back(left, top, width, height);
115
115
  }
116
116
 
117
117
  void Gosu::BlockAllocator::free(unsigned left, unsigned top, unsigned width, unsigned height)
@@ -3,7 +3,8 @@
3
3
  using namespace std;
4
4
 
5
5
  // Returns the current state of a source
6
- static ALint state(int& channel) {
6
+ static ALint state(int& channel)
7
+ {
7
8
  ALint state;
8
9
  alGetSourcei(Gosu::al_source_for_channel(channel), AL_SOURCE_STATE, &state);
9
10
  if (state != AL_PLAYING && state != AL_PAUSED) {
@@ -12,6 +13,11 @@ static ALint state(int& channel) {
12
13
  return state;
13
14
  }
14
15
 
16
+ Gosu::Channel::Channel()
17
+ : channel(NO_CHANNEL), token(0)
18
+ {
19
+ }
20
+
15
21
  Gosu::Channel::Channel(int channel, int token)
16
22
  : channel(channel), token(token)
17
23
  {
@@ -40,7 +40,10 @@ class Gosu::ClipRectStack
40
40
  // TODO: Doesn't this affect Retina Macs as well?
41
41
  // TODO: This should be handled by a global transform.
42
42
  int fac = clip_rect_base_factor();
43
- result.x *= fac, result.y *= fac, result.width *= fac, result.height *= fac;
43
+ result.x *= fac;
44
+ result.y *= fac;
45
+ result.width *= fac;
46
+ result.height *= fac;
44
47
 
45
48
  // Normal clipping.
46
49
  effective_rect = result;
@@ -1,5 +1,6 @@
1
1
  #include <Gosu/Color.hpp>
2
2
  #include <Gosu/Math.hpp>
3
+ #include <cmath>
3
4
  #include <algorithm>
4
5
 
5
6
  namespace
@@ -61,7 +62,7 @@ Gosu::Color Gosu::Color::from_ahsv(Channel alpha, double h, double s, double v)
61
62
  s = clamp(s, 0.0, 1.0);
62
63
  v = clamp(v, 0.0, 1.0);
63
64
 
64
- int sector = h / 60;
65
+ int sector = static_cast<int>(h / 60);
65
66
  double factorial = h / 60 - sector;
66
67
 
67
68
  double p = v * (1 - s);
@@ -46,7 +46,7 @@ string Gosu::resource_prefix()
46
46
  if (result.empty()) {
47
47
  result = exe_filename();
48
48
  auto last_delim = result.find_last_of("\\/");
49
- result.resize(last_delim == result.npos ? 0 : last_delim + 1);
49
+ result.resize(last_delim == string::npos ? 0 : last_delim + 1);
50
50
  }
51
51
  return result;
52
52
  }
@@ -147,10 +147,14 @@ namespace Gosu
147
147
  RenderState va_render_state = render_state;
148
148
  va_render_state.transform = 0;
149
149
 
150
- result[0].tex_coords[0] = left, result[0].tex_coords[1] = top;
151
- result[1].tex_coords[0] = right, result[1].tex_coords[1] = top;
152
- result[2].tex_coords[0] = right, result[2].tex_coords[1] = bottom;
153
- result[3].tex_coords[0] = left, result[3].tex_coords[1] = bottom;
150
+ result[0].tex_coords[0] = left;
151
+ result[0].tex_coords[1] = top;
152
+ result[1].tex_coords[0] = right;
153
+ result[1].tex_coords[1] = top;
154
+ result[2].tex_coords[0] = right;
155
+ result[2].tex_coords[1] = bottom;
156
+ result[3].tex_coords[0] = left;
157
+ result[3].tex_coords[1] = bottom;
154
158
 
155
159
  if (vas.empty() || !(vas.back().render_state == va_render_state)) {
156
160
  vas.push_back(VertexArray());
@@ -15,32 +15,25 @@ class Gosu::DrawOpQueue
15
15
  {
16
16
  TransformStack transform_stack;
17
17
  ClipRectStack clip_rect_stack;
18
- bool rec;
18
+ QueueMode queue_mode = QM_RENDER_TO_SCREEN;
19
19
 
20
20
  std::vector<DrawOp> ops;
21
21
  std::vector<std::function<void ()>> gl_blocks;
22
22
 
23
23
  public:
24
- DrawOpQueue()
25
- : rec(false)
24
+ DrawOpQueue(QueueMode mode)
25
+ : queue_mode(mode)
26
26
  {
27
27
  }
28
28
 
29
- bool recording() const
29
+ QueueMode mode() const
30
30
  {
31
- return rec;
32
- }
33
-
34
- void set_recording()
35
- {
36
- rec = true;
31
+ return queue_mode;
37
32
  }
38
33
 
39
34
  void schedule_draw_op(DrawOp op)
40
35
  {
41
- if (clip_rect_stack.clipped_world_away()) {
42
- return;
43
- }
36
+ if (clip_rect_stack.clipped_world_away()) return;
44
37
 
45
38
  #ifdef GOSU_IS_OPENGLES
46
39
  // No triangles, no lines supported
@@ -57,9 +50,7 @@ public:
57
50
  void gl(std::function<void ()> gl_block, ZPos z)
58
51
  {
59
52
  // TODO: Document this case: Clipped-away GL blocks are *not* being run.
60
- if (clip_rect_stack.clipped_world_away()) {
61
- return;
62
- }
53
+ if (clip_rect_stack.clipped_world_away()) return;
63
54
 
64
55
  int complement_of_block_index = ~(int)gl_blocks.size();
65
56
  gl_blocks.push_back(gl_block);
@@ -76,7 +67,7 @@ public:
76
67
 
77
68
  void begin_clipping(double x, double y, double width, double height, double screen_height)
78
69
  {
79
- if (recording()) {
70
+ if (mode() == QM_RECORD_MACRO) {
80
71
  throw std::logic_error("Clipping is not allowed while creating a macro");
81
72
  }
82
73
 
@@ -119,9 +110,9 @@ public:
119
110
  transform_stack.pop();
120
111
  }
121
112
 
122
- void perform_draw_ops_andCode()
113
+ void perform_draw_ops_and_code()
123
114
  {
124
- if (recording()) {
115
+ if (mode() == QM_RECORD_MACRO) {
125
116
  throw std::logic_error("Flushing to the screen is not allowed while recording a macro");
126
117
  }
127
118
 
@@ -131,9 +122,7 @@ public:
131
122
  RenderStateManager manager;
132
123
 
133
124
  #ifdef GOSU_IS_OPENGLES
134
- if (ops.empty()) {
135
- return;
136
- }
125
+ if (ops.empty()) return;
137
126
 
138
127
  auto current = ops.begin(), last = ops.end() - 1;
139
128
  for (; current != last; ++current) {
@@ -141,12 +130,12 @@ public:
141
130
  current->perform(&*(current + 1));
142
131
  }
143
132
  manager.set_render_state(last->render_state);
144
- last->perform(0);
133
+ last->perform(nullptr);
145
134
  #else
146
135
  for (const auto& op : ops) {
147
136
  manager.set_render_state(op.render_state);
148
137
  if (op.vertices_or_block_index >= 0) {
149
- op.perform(0);
138
+ op.perform(nullptr);
150
139
  }
151
140
  else {
152
141
  // GL code
@@ -44,6 +44,8 @@ Gosu::File::File(const string& filename, FileMode mode)
44
44
  case FM_ALTER:
45
45
  flags = O_RDWR | O_CREAT;
46
46
  break;
47
+ default:
48
+ throw invalid_argument("Unknown file mode: " + to_string(mode));
47
49
  }
48
50
 
49
51
  // TODO: Locking flags?
@@ -66,7 +68,7 @@ Gosu::File::~File()
66
68
  size_t Gosu::File::size() const
67
69
  {
68
70
  // TODO: Error checking?
69
- return lseek(pimpl->fd, 0, SEEK_END);
71
+ return static_cast<size_t>(lseek(pimpl->fd, 0, SEEK_END));
70
72
  }
71
73
 
72
74
  void Gosu::File::resize(size_t new_size)
@@ -1,4 +1,4 @@
1
- #include "FormattedString.hpp"
1
+ #include "MarkupParser.hpp"
2
2
  #include "GraphicsImpl.hpp"
3
3
  #include <Gosu/Font.hpp>
4
4
  #include <Gosu/Graphics.hpp>
@@ -6,129 +6,119 @@
6
6
  #include <Gosu/Math.hpp>
7
7
  #include <Gosu/Text.hpp>
8
8
  #include <Gosu/Utility.hpp>
9
+ #include "utf8proc.h"
9
10
  #include <array>
10
11
  #include <cassert>
11
12
  #include <map>
12
13
  using namespace std;
13
14
 
15
+ static const int FONT_RENDER_SCALE = 2;
16
+
14
17
  struct Gosu::Font::Impl
15
18
  {
16
19
  string name;
17
- unsigned height, flags;
20
+ int height;
21
+ unsigned base_flags;
18
22
 
19
- // Unicode planes of 2^16 characters each. On Windows, where wchar_t is only 16 bits wide, only
20
- // the first plane will ever be touched.
21
- struct CharInfo
22
- {
23
- unique_ptr<Image> image;
24
- double factor;
25
- };
26
- typedef array<CharInfo, 65536> Plane;
27
- unique_ptr<Plane> planes[16][FF_COMBINATIONS];
28
-
29
- map<string, shared_ptr<Image>> entity_cache;
23
+ // The most common characters are stored directly in an array for maximum performance.
24
+ // (This is the start of the Basic Multilingual Plane, up until the part where right-to-left
25
+ // languages begin, which don't really work with Gosu yet.)
26
+ array<array<Image, 0x58f>, FF_COMBINATIONS> fast_glyphs;
27
+ // Everything else is looked up through a map...
28
+ array<map<utf8proc_int32_t, Image>, FF_COMBINATIONS> other_glyphs;
30
29
 
31
- CharInfo& char_info(wchar_t wc, unsigned flags)
30
+ const Image& image(char32_t codepoint, unsigned font_flags)
32
31
  {
33
- size_t plane_index = wc / 65536;
34
- size_t char_index = wc % 65536;
35
-
36
- if (plane_index >= 16) throw invalid_argument("Unicode plane out of reach");
37
- if (flags >= FF_COMBINATIONS) throw invalid_argument("Font flags out of range");
38
-
39
- if (!planes[plane_index][flags].get()) {
40
- planes[plane_index][flags].reset(new Plane);
32
+ Image* image;
33
+ if (codepoint < fast_glyphs.size()) {
34
+ image = &fast_glyphs[font_flags][codepoint];
41
35
  }
42
- return (*planes[plane_index][flags])[char_index];
43
- }
44
-
45
- const Image& image_at(const FormattedString& fs, unsigned i)
46
- {
47
- if (const char* entity = fs.entity_at(i)) {
48
- shared_ptr<Image>& ptr = entity_cache[entity];
49
- if (!ptr) {
50
- ptr.reset(new Image(entity_bitmap(fs.entity_at(i)), IF_SMOOTH));
51
- }
52
- return *ptr;
36
+ else {
37
+ image = &other_glyphs[font_flags][codepoint];
53
38
  }
54
39
 
55
- wchar_t wc = fs.char_at(i);
56
- unsigned flags = fs.flags_at(i);
57
- CharInfo& info = char_info(wc, flags);
58
-
59
- if (info.image.get()) return *info.image;
60
-
61
- string char_string = wstring_to_utf8(wstring(1, wc));
62
- // TODO: Would be nice to have.
63
- // if (is_formatting_char(wc))
64
- // char_string.clear();
65
- unsigned char_width = Gosu::text_width(char_string, name, height, flags);
40
+ // If this codepoint has not been rendered before, do it now.
41
+ if (image->width() == 0 && image->height() == 0) {
42
+ auto scaled_height = height * FONT_RENDER_SCALE;
43
+
44
+ u32string string(1, codepoint);
45
+ Bitmap bitmap(scaled_height, scaled_height);
46
+ auto required_width = ceil(draw_text(bitmap, 0, 0, Color::WHITE, string,
47
+ name, scaled_height, font_flags));
48
+ if (required_width > bitmap.width()) {
49
+ // If the character was wider than high, we need to render it again.
50
+ Bitmap(required_width, scaled_height).swap(bitmap);
51
+ draw_text(bitmap, 0, 0, Color::WHITE, string,
52
+ name, scaled_height, font_flags);
53
+ }
54
+
55
+ *image = Image(bitmap, 0, 0, required_width, scaled_height);
56
+ }
66
57
 
67
- Bitmap bitmap(char_width, height, 0x00ffffff);
68
- draw_text(bitmap, char_string, 0, 0, Color::WHITE, name, height, flags);
69
- info.image.reset(new Image(bitmap));
70
- info.factor = 0.5;
71
- return *info.image;
72
- }
73
-
74
- double factor_at(const FormattedString& fs, unsigned index)
75
- {
76
- if (fs.entity_at(index)) return 1;
77
- return char_info(fs.char_at(index), fs.flags_at(index)).factor;
58
+ return *image;
78
59
  }
79
60
  };
80
61
 
81
- Gosu::Font::Font(unsigned font_height, const string& font_name, unsigned font_flags)
62
+ Gosu::Font::Font(int font_height, const string& font_name, unsigned font_flags)
82
63
  : pimpl(new Impl)
83
64
  {
84
65
  pimpl->name = font_name;
85
- pimpl->height = font_height * 2;
86
- pimpl->flags = font_flags;
66
+ pimpl->height = font_height;
67
+ pimpl->base_flags = font_flags;
87
68
  }
88
69
 
89
- string Gosu::Font::name() const
70
+ const string& Gosu::Font::name() const
90
71
  {
91
72
  return pimpl->name;
92
73
  }
93
74
 
94
- unsigned Gosu::Font::height() const
75
+ int Gosu::Font::height() const
95
76
  {
96
- return pimpl->height / 2;
77
+ return pimpl->height;
97
78
  }
98
79
 
99
80
  unsigned Gosu::Font::flags() const
100
81
  {
101
- return pimpl->flags;
82
+ return pimpl->base_flags;
102
83
  }
103
84
 
104
85
  double Gosu::Font::text_width(const string& text, double scale_x) const
105
86
  {
106
- wstring wtext = utf8_to_wstring(text);
107
- FormattedString fs(wtext.c_str(), flags());
108
- double result = 0;
109
- for (unsigned i = 0; i < fs.length(); ++i) {
110
- const Image& image = pimpl->image_at(fs, i);
111
- double factor = pimpl->factor_at(fs, i);
112
- result += image.width() * factor;
113
- }
114
- return result * scale_x;
87
+ int width = 0;
88
+
89
+ // Split the text into lines (split_words = false) because Font doesn't implement word-wrapping.
90
+ MarkupParser(text.c_str(), pimpl->base_flags, false, [&](vector<FormattedString>&& line) {
91
+ int line_width = 0;
92
+ for (auto& part : line) {
93
+ for (auto codepoint : part.text) {
94
+ line_width += pimpl->image(codepoint, part.flags).width();
95
+ }
96
+ }
97
+ width = max(width, line_width);
98
+ }).parse();
99
+
100
+ return scale_x * width / FONT_RENDER_SCALE;
115
101
  }
116
102
 
117
103
  void Gosu::Font::draw(const string& text, double x, double y, ZPos z,
118
104
  double scale_x, double scale_y, Color c, AlphaMode mode) const
119
105
  {
120
- wstring wtext = utf8_to_wstring(text);
121
- FormattedString fs(wtext.c_str(), flags());
106
+ double current_y = y;
122
107
 
123
- for (unsigned i = 0; i < fs.length(); ++i) {
124
- const Image& image = pimpl->image_at(fs, i);
125
- double factor = pimpl->factor_at(fs, i);
126
- Color color = fs.entity_at(i)
127
- ? Color(fs.color_at(i).alpha() * c.alpha() / 255, 255, 255, 255)
128
- : multiply(fs.color_at(i), c);
129
- image.draw(x, y, z, scale_x * factor, scale_y * factor, color, mode);
130
- x += image.width() * scale_x * factor;
131
- }
108
+ // Split the text into lines (split_words = false) because Font doesn't implement word-wrapping.
109
+ MarkupParser(text.c_str(), pimpl->base_flags, false, [&](vector<FormattedString>&& line) {
110
+ double current_x = x;
111
+ for (auto& part : line) {
112
+ for (auto codepoint : part.text) {
113
+ auto& image = pimpl->image(codepoint, part.flags);
114
+ image.draw(current_x, current_y, z,
115
+ scale_x / FONT_RENDER_SCALE, scale_y / FONT_RENDER_SCALE,
116
+ c, mode);
117
+ current_x += scale_x * image.width() / FONT_RENDER_SCALE;
118
+ }
119
+ }
120
+ current_y += scale_y * height();
121
+ }).parse();
132
122
  }
133
123
 
134
124
  void Gosu::Font::draw_rel(const string& text, double x, double y, ZPos z,
@@ -140,25 +130,31 @@ void Gosu::Font::draw_rel(const string& text, double x, double y, ZPos z,
140
130
  draw(text, x, y, z, scale_x, scale_y, c, mode);
141
131
  }
142
132
 
143
- void Gosu::Font::set_image(wchar_t wc, const Image& image)
133
+ void Gosu::Font::draw_rot(const string& text, double x, double y, ZPos z, double angle,
134
+ double scale_x, double scale_y, Color c, AlphaMode mode) const
144
135
  {
145
- for (unsigned flags = 0; flags < FF_COMBINATIONS; ++flags) {
146
- set_image(wc, flags, image);
147
- }
136
+ Graphics::transform(rotate(angle, x, y), [&] {
137
+ draw(text, x, y, z, scale_x, scale_y, c, mode);
138
+ });
148
139
  }
149
140
 
150
- void Gosu::Font::set_image(wchar_t wc, unsigned font_flags, const Image& image)
141
+ void Gosu::Font::set_image(std::string codepoint, unsigned font_flags, const Gosu::Image& image)
151
142
  {
152
- Impl::CharInfo& ci = pimpl->char_info(wc, font_flags);
153
- if (ci.image.get()) throw logic_error("Cannot set image for the same character twice");
154
- ci.image.reset(new Image(image));
155
- ci.factor = 1.0;
143
+ auto utc4 = utf8_to_composed_utc4(codepoint);
144
+ if (utc4.length() != 1) {
145
+ throw invalid_argument("Could not compose '" + codepoint + "' into a single codepoint");
146
+ }
147
+
148
+ if (utc4[0] < pimpl->fast_glyphs[font_flags].size()) {
149
+ pimpl->fast_glyphs[font_flags][utc4[0]] = image;
150
+ } else {
151
+ pimpl->other_glyphs[font_flags][utc4[0]] = image;
152
+ }
156
153
  }
157
154
 
158
- void Gosu::Font::draw_rot(const string& text, double x, double y, ZPos z, double angle,
159
- double scale_x, double scale_y, Color c, AlphaMode mode) const
155
+ void Gosu::Font::set_image(std::string codepoint, const Gosu::Image& image)
160
156
  {
161
- Graphics::transform(rotate(angle, x, y), [&] {
162
- draw(text, x, y, z, scale_x, scale_y, c, mode);
163
- });
157
+ for (unsigned font_flags = 0; font_flags < FF_COMBINATIONS; ++font_flags) {
158
+ set_image(codepoint, font_flags, image);
159
+ }
164
160
  }