gosu 1.4.6 → 2.0.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/COPYING +2 -1
  3. data/dependencies/SDL/include/SDL_assert.h +2 -0
  4. data/dependencies/SDL/include/SDL_atomic.h +2 -3
  5. data/dependencies/SDL/include/SDL_audio.h +7 -7
  6. data/dependencies/SDL/include/SDL_blendmode.h +1 -1
  7. data/dependencies/SDL/include/SDL_endian.h +3 -3
  8. data/dependencies/SDL/include/SDL_gamecontroller.h +4 -4
  9. data/dependencies/SDL/include/SDL_hints.h +83 -28
  10. data/dependencies/SDL/include/SDL_joystick.h +8 -5
  11. data/dependencies/SDL/include/SDL_keycode.h +1 -1
  12. data/dependencies/SDL/include/SDL_main.h +7 -0
  13. data/dependencies/SDL/include/SDL_mouse.h +6 -7
  14. data/dependencies/SDL/include/SDL_mutex.h +79 -5
  15. data/dependencies/SDL/include/SDL_opengl_glext.h +5 -1
  16. data/dependencies/SDL/include/SDL_power.h +7 -8
  17. data/dependencies/SDL/include/SDL_render.h +6 -1
  18. data/dependencies/SDL/include/SDL_revision.h +2 -2
  19. data/dependencies/SDL/include/SDL_sensor.h +1 -1
  20. data/dependencies/SDL/include/SDL_stdinc.h +19 -11
  21. data/dependencies/SDL/include/SDL_thread.h +2 -2
  22. data/dependencies/SDL/include/SDL_version.h +2 -2
  23. data/dependencies/SDL/include/SDL_video.h +30 -2
  24. data/dependencies/SDL/include/begin_code.h +7 -7
  25. data/dependencies/SDL/include/close_code.h +2 -2
  26. data/dependencies/SDL/lib/x64/libSDL2.dll.a +0 -0
  27. data/dependencies/SDL/lib/x86/libSDL2.dll.a +0 -0
  28. data/dependencies/SDL_sound/SDL_sound.h +1 -1
  29. data/dependencies/SDL_sound/dr_flac.h +48 -23
  30. data/dependencies/SDL_sound/dr_mp3.h +34 -14
  31. data/dependencies/SDL_sound/stb_vorbis.h +3 -2
  32. data/dependencies/mojoAL/mojoal.c +1 -1
  33. data/ext/{gosu → gosu-ffi}/extconf.rb +34 -33
  34. data/ext/gosu-ffi/gosu-ffi.def +465 -0
  35. data/ffi/Gosu.cpp +307 -0
  36. data/ffi/Gosu.h +84 -0
  37. data/ffi/Gosu_Channel.cpp +62 -0
  38. data/ffi/Gosu_Channel.h +17 -0
  39. data/ffi/Gosu_Color.cpp +132 -0
  40. data/ffi/Gosu_Color.h +31 -0
  41. data/ffi/Gosu_Constants.cpp +334 -0
  42. data/ffi/Gosu_FFI.h +34 -0
  43. data/ffi/Gosu_FFI_internal.h +161 -0
  44. data/ffi/Gosu_Font.cpp +92 -0
  45. data/ffi/Gosu_Font.h +32 -0
  46. data/ffi/Gosu_Image.cpp +218 -0
  47. data/ffi/Gosu_Image.h +66 -0
  48. data/ffi/Gosu_Sample.cpp +29 -0
  49. data/ffi/Gosu_Sample.h +14 -0
  50. data/ffi/Gosu_Song.cpp +69 -0
  51. data/ffi/Gosu_Song.h +18 -0
  52. data/ffi/Gosu_TextInput.cpp +94 -0
  53. data/ffi/Gosu_TextInput.h +25 -0
  54. data/ffi/Gosu_Window.cpp +314 -0
  55. data/ffi/Gosu_Window.h +78 -0
  56. data/include/Gosu/Audio.hpp +6 -11
  57. data/include/Gosu/Bitmap.hpp +38 -53
  58. data/include/Gosu/Buffer.hpp +54 -0
  59. data/include/Gosu/Color.hpp +27 -35
  60. data/include/Gosu/Directories.hpp +25 -28
  61. data/include/Gosu/Drawable.hpp +58 -0
  62. data/include/Gosu/Font.hpp +6 -5
  63. data/include/Gosu/Fwd.hpp +4 -6
  64. data/include/Gosu/Gosu.hpp +5 -5
  65. data/include/Gosu/Graphics.hpp +51 -61
  66. data/include/Gosu/GraphicsBase.hpp +1 -11
  67. data/include/Gosu/Image.hpp +12 -15
  68. data/include/Gosu/Math.hpp +50 -72
  69. data/include/Gosu/Transform.hpp +32 -0
  70. data/include/Gosu/Utility.hpp +51 -1
  71. data/include/Gosu/Version.hpp +3 -3
  72. data/include/Gosu/Window.hpp +15 -9
  73. data/lib/SDL2.dll +0 -0
  74. data/lib/gosu/channel.rb +49 -0
  75. data/lib/gosu/color.rb +150 -0
  76. data/lib/gosu/compat.rb +29 -8
  77. data/lib/gosu/constants.rb +386 -0
  78. data/lib/gosu/ffi.rb +259 -0
  79. data/lib/gosu/font.rb +56 -0
  80. data/lib/gosu/gl_tex_info.rb +33 -0
  81. data/lib/gosu/gosu.rb +210 -0
  82. data/lib/gosu/image.rb +153 -0
  83. data/lib/gosu/numeric.rb +17 -0
  84. data/lib/gosu/preview.rb +6 -6
  85. data/lib/gosu/sample.rb +24 -0
  86. data/lib/gosu/song.rb +56 -0
  87. data/lib/gosu/text_input.rb +69 -0
  88. data/lib/gosu/window.rb +228 -0
  89. data/lib/gosu.rb +29 -8
  90. data/lib64/SDL2.dll +0 -0
  91. data/rdoc/gosu.rb +0 -2
  92. data/src/Audio.cpp +12 -12
  93. data/src/AudioFile.hpp +5 -4
  94. data/src/AudioFileAudioToolbox.cpp +8 -8
  95. data/src/AudioFileSDLSound.cpp +7 -10
  96. data/src/BinPacker.cpp +187 -0
  97. data/src/BinPacker.hpp +55 -0
  98. data/src/Bitmap.cpp +166 -144
  99. data/src/BitmapIO.cpp +60 -86
  100. data/src/Buffer.cpp +159 -0
  101. data/src/ClipRectStack.cpp +38 -0
  102. data/src/ClipRectStack.hpp +17 -83
  103. data/src/Color.cpp +75 -80
  104. data/src/Directories.cpp +47 -0
  105. data/src/DirectoriesUIKit.cpp +50 -0
  106. data/src/DrawOp.hpp +9 -9
  107. data/src/DrawOpQueue.hpp +26 -32
  108. data/src/Drawable.cpp +95 -0
  109. data/src/EmptyDrawable.hpp +38 -0
  110. data/src/FPS.cpp +31 -0
  111. data/src/Font.cpp +104 -74
  112. data/src/GosuGLView.cpp +13 -11
  113. data/src/GosuViewController.cpp +2 -10
  114. data/src/Graphics.cpp +66 -129
  115. data/src/GraphicsImpl.hpp +14 -67
  116. data/src/Image.cpp +41 -35
  117. data/src/Input.cpp +7 -8
  118. data/src/Macro.cpp +6 -6
  119. data/src/Macro.hpp +4 -4
  120. data/src/MarkupParser.cpp +5 -5
  121. data/src/Math.cpp +35 -22
  122. data/src/OffScreenTarget.cpp +53 -49
  123. data/src/OffScreenTarget.hpp +13 -11
  124. data/src/OpenGLContext.cpp +117 -0
  125. data/src/OpenGLContext.hpp +41 -0
  126. data/src/RenderState.hpp +45 -56
  127. data/src/Resolution.cpp +23 -21
  128. data/src/TexChunk.cpp +35 -80
  129. data/src/TexChunk.hpp +44 -35
  130. data/src/Text.cpp +1 -1
  131. data/src/TextBuilder.cpp +35 -21
  132. data/src/TextBuilder.hpp +6 -9
  133. data/src/Texture.cpp +62 -80
  134. data/src/Texture.hpp +25 -23
  135. data/src/TiledDrawable.cpp +150 -0
  136. data/src/TiledDrawable.hpp +47 -0
  137. data/src/TimingApple.cpp +1 -1
  138. data/src/Transform.cpp +45 -50
  139. data/src/TransformStack.hpp +16 -16
  140. data/src/TrueTypeFont.cpp +59 -51
  141. data/src/TrueTypeFont.hpp +6 -7
  142. data/src/TrueTypeFontApple.cpp +28 -19
  143. data/src/TrueTypeFontUnix.cpp +27 -23
  144. data/src/TrueTypeFontWin.cpp +30 -30
  145. data/src/Utility.cpp +84 -21
  146. data/src/UtilityWin.cpp +45 -0
  147. data/src/Window.cpp +92 -142
  148. data/src/WindowUIKit.cpp +14 -14
  149. metadata +74 -32
  150. data/include/Gosu/IO.hpp +0 -254
  151. data/include/Gosu/ImageData.hpp +0 -53
  152. data/include/Gosu/Inspection.hpp +0 -7
  153. data/lib/gosu/patches.rb +0 -66
  154. data/lib/gosu/run.rb +0 -20
  155. data/lib/gosu/swig_patches.rb +0 -110
  156. data/src/BlockAllocator.cpp +0 -131
  157. data/src/BlockAllocator.hpp +0 -32
  158. data/src/DirectoriesApple.cpp +0 -69
  159. data/src/DirectoriesUnix.cpp +0 -46
  160. data/src/DirectoriesWin.cpp +0 -65
  161. data/src/EmptyImageData.hpp +0 -52
  162. data/src/FileUnix.cpp +0 -99
  163. data/src/FileWin.cpp +0 -88
  164. data/src/IO.cpp +0 -60
  165. data/src/Iconv.hpp +0 -51
  166. data/src/Inspection.cpp +0 -27
  167. data/src/LargeImageData.cpp +0 -215
  168. data/src/LargeImageData.hpp +0 -39
  169. data/src/Log.hpp +0 -19
  170. data/src/RubyGosu.cxx +0 -13100
  171. data/src/RubyGosu.h +0 -49
  172. data/src/WinUtility.cpp +0 -61
  173. data/src/WinUtility.hpp +0 -27
data/src/DrawOpQueue.hpp CHANGED
@@ -9,6 +9,7 @@
9
9
  #include <cmath>
10
10
  #include <functional>
11
11
  #include <map>
12
+ #include <utility>
12
13
  #include <vector>
13
14
 
14
15
  class Gosu::DrawOpQueue
@@ -22,7 +23,7 @@ class Gosu::DrawOpQueue
22
23
  std::vector<std::function<void ()>> gl_blocks;
23
24
 
24
25
  public:
25
- DrawOpQueue(QueueMode mode)
26
+ explicit DrawOpQueue(QueueMode mode)
26
27
  : queue_mode(mode)
27
28
  {
28
29
  }
@@ -34,39 +35,31 @@ public:
34
35
 
35
36
  void schedule_draw_op(DrawOp op)
36
37
  {
37
- if (clip_rect_stack.clipped_world_away()) return;
38
-
39
- #ifdef GOSU_IS_OPENGLES
38
+ #ifdef GOSU_IS_OPENGLES
40
39
  // No triangles, no lines supported
41
- assert (op.vertices_or_block_index == 4);
42
- #endif
40
+ assert(op.vertices_or_block_index == 4);
41
+ #endif
43
42
 
44
43
  op.render_state.transform = &transform_stack.current();
45
- if (const ClipRect* cr = clip_rect_stack.maybe_effective_rect()) {
46
- op.render_state.clip_rect = *cr;
47
- }
44
+ op.render_state.clip_rect = clip_rect_stack.effective_rect();
48
45
  ops.push_back(op);
49
46
  }
50
47
 
51
48
  void gl(std::function<void ()> gl_block, ZPos z)
52
49
  {
53
- // TODO: Document this case: Clipped-away GL blocks are *not* being run.
54
- if (clip_rect_stack.clipped_world_away()) return;
55
-
56
50
  int complement_of_block_index = ~(int)gl_blocks.size();
57
- gl_blocks.push_back(gl_block);
51
+ gl_blocks.push_back(std::move(gl_block));
58
52
 
59
53
  DrawOp op;
60
54
  op.vertices_or_block_index = complement_of_block_index;
61
55
  op.render_state.transform = &transform_stack.current();
62
- if (const ClipRect* cr = clip_rect_stack.maybe_effective_rect()) {
63
- op.render_state.clip_rect = *cr;
64
- }
56
+ op.render_state.clip_rect = clip_rect_stack.effective_rect();
65
57
  op.z = z;
66
58
  ops.push_back(op);
67
59
  }
68
60
 
69
- void begin_clipping(double x, double y, double width, double height, double screen_height)
61
+ void begin_clipping(double x, double y, double width, double height,
62
+ std::optional<int> viewport_height)
70
63
  {
71
64
  if (mode() == QM_RECORD_MACRO) {
72
65
  throw std::logic_error("Clipping is not allowed while creating a macro");
@@ -77,24 +70,24 @@ public:
77
70
  double left = x, right = x + width;
78
71
  double top = y, bottom = y + height;
79
72
 
80
- apply_transform(transform_stack.current(), left, top);
81
- apply_transform(transform_stack.current(), right, bottom);
82
-
83
- double phys_x = std::min(left, right);
84
- double phys_y = std::min(top, bottom);
85
- double phys_width = std::abs(left - right);
86
- double phys_height = std::abs(top - bottom);
73
+ transform_stack.current().apply(left, top);
74
+ transform_stack.current().apply(right, bottom);
87
75
 
76
+ Rect clip_rect {
77
+ .x = static_cast<int>(std::min(left, right)),
78
+ .y = static_cast<int>(std::min(top, bottom)),
79
+ .width = static_cast<int>(std::abs(left - right)),
80
+ .height = static_cast<int>(std::abs(top - bottom)),
81
+ };
88
82
  // Adjust for OpenGL having the wrong idea of where y=0 is.
89
- phys_y = screen_height - phys_y - phys_height;
83
+ if (viewport_height && mode() == QM_RENDER_TO_SCREEN) {
84
+ clip_rect.y = *viewport_height - clip_rect.y - clip_rect.height;
85
+ }
90
86
 
91
- clip_rect_stack.begin_clipping(phys_x, phys_y, phys_width, phys_height);
87
+ clip_rect_stack.push(clip_rect);
92
88
  }
93
89
 
94
- void end_clipping()
95
- {
96
- clip_rect_stack.end_clipping();
97
- }
90
+ void end_clipping() { clip_rect_stack.pop(); }
98
91
 
99
92
  void set_base_transform(const Transform& base_transform)
100
93
  {
@@ -118,7 +111,8 @@ public:
118
111
  }
119
112
 
120
113
  // Apply Z-Ordering.
121
- std::stable_sort(ops.begin(), ops.end());
114
+ std::stable_sort(ops.begin(), ops.end(),
115
+ [](const DrawOp& lhs, const DrawOp& rhs) { return lhs.z < rhs.z; });
122
116
 
123
117
  RenderStateManager manager;
124
118
 
@@ -156,7 +150,7 @@ public:
156
150
  throw std::logic_error("Custom OpenGL code cannot be recorded as a macro");
157
151
  }
158
152
 
159
- std::stable_sort(ops.begin(), ops.end());
153
+ std::stable_sort(ops.begin(), ops.end(), [](const DrawOp& lhs, const DrawOp& rhs) { return lhs.z < rhs.z; });
160
154
  for (const auto& op : ops) {
161
155
  op.compile_to(vas);
162
156
  }
data/src/Drawable.cpp ADDED
@@ -0,0 +1,95 @@
1
+ #include <Gosu/Drawable.hpp>
2
+ #include <Gosu/Utility.hpp>
3
+ #include "EmptyDrawable.hpp"
4
+ #include "Texture.hpp"
5
+ #include "TiledDrawable.hpp"
6
+ #include <list>
7
+ #include <mutex>
8
+
9
+ namespace Gosu
10
+ {
11
+ // This variable has been declared in multiple places. Define it here, where it will be used.
12
+ // This is a compatibility hack for old versions of Ruby/Gosu that didn't yet support :retro.
13
+ bool undocumented_retrofication = false; // NOLINT(*-avoid-non-const-global-variables)
14
+ }
15
+
16
+ std::unique_ptr<Gosu::Drawable> Gosu::create_drawable(const Bitmap& source, const Rect& source_rect,
17
+ unsigned image_flags)
18
+ {
19
+ if (!Rect::covering(source).contains(source_rect)) {
20
+ throw std::invalid_argument("Source rectangle exceeds bitmap");
21
+ }
22
+
23
+ if (source_rect.empty()) {
24
+ // It can happen that Gosu::Font wants us to create an empty drawable when rendering
25
+ // invisible characters (e.g. '\r'). In this case, return an empty drawable that is not
26
+ // backed by any kind of OpenGL texture.
27
+ return std::make_unique<EmptyDrawable>(source_rect.width, source_rect.height);
28
+ }
29
+
30
+ // Backward compatibility: This used to be 'bool tileable', help users that still pass 'true'.
31
+ if (image_flags == 1) {
32
+ image_flags = IF_TILEABLE;
33
+ }
34
+
35
+ bool wants_retro = (image_flags & IF_RETRO) || undocumented_retrofication;
36
+
37
+ // Special case: If the texture is supposed to be tileable, is quadratic, has a size that is at
38
+ // least 64 pixels but no more than MAX_TEXTURE_SIZE pixels and a power of two, create a single
39
+ // texture just for this image.
40
+ // This is not just an optimization, but a feature of Gosu so that one can use Gosu for loading
41
+ // textures for use in 3D scenes, where it is important that the full u/v range is dedicated to
42
+ // a single image so that texture repetition works as expected.
43
+ if ((image_flags & IF_TILEABLE) == IF_TILEABLE && source_rect.width == source_rect.height
44
+ && (source_rect.width & (source_rect.width - 1)) == 0 && source_rect.width >= 64
45
+ && source_rect.width <= MAX_TEXTURE_SIZE) {
46
+
47
+ const std::shared_ptr<Texture> texture
48
+ = std::make_shared<Texture>(source_rect.width, source_rect.height, wants_retro);
49
+
50
+ // Use the source bitmap directly if the source area completely covers it.
51
+ if (source_rect == Rect::covering(source)) {
52
+ return texture->try_alloc(source, 0);
53
+ }
54
+ else {
55
+ Bitmap trimmed_source(source_rect.width, source_rect.height);
56
+ trimmed_source.insert(source, 0, 0, source_rect);
57
+ return texture->try_alloc(trimmed_source, 0);
58
+ }
59
+ }
60
+
61
+ const int max_size = MAX_TEXTURE_SIZE;
62
+
63
+ // Too large to fit on a single texture? -> Create a tiled representation.
64
+ if (source_rect.width > max_size - 2 || source_rect.height > max_size - 2) {
65
+ return std::make_unique<TiledDrawable>(source, source_rect, max_size - 2, image_flags);
66
+ }
67
+
68
+ Bitmap source_with_borders = apply_border_flags(image_flags, source, source_rect);
69
+
70
+ // Try to put the bitmap into one of the already allocated textures.
71
+ static std::list<std::weak_ptr<Texture>> texture_pool;
72
+ static std::mutex mutex;
73
+ std::scoped_lock lock(mutex);
74
+
75
+ texture_pool.remove_if([](const auto& weak_ptr) { return weak_ptr.expired(); });
76
+
77
+ for (const std::weak_ptr<Texture>& weak_texture : texture_pool) {
78
+ const auto texture = weak_texture.lock();
79
+ if (!texture || texture->retro() != wants_retro) {
80
+ continue;
81
+ }
82
+
83
+ std::unique_ptr<Drawable> data = texture->try_alloc(source_with_borders, 1);
84
+ if (data) {
85
+ return data;
86
+ }
87
+ }
88
+
89
+ // All textures are full: Create a new one.
90
+
91
+ std::shared_ptr<Texture> texture
92
+ = std::make_shared<Texture>(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, wants_retro);
93
+ texture_pool.push_back(texture);
94
+ return texture->try_alloc(source_with_borders, 1);
95
+ }
@@ -0,0 +1,38 @@
1
+ #pragma once
2
+
3
+ #include <Gosu/Bitmap.hpp>
4
+ #include <Gosu/Drawable.hpp>
5
+ #include <memory>
6
+
7
+ namespace Gosu
8
+ {
9
+ class EmptyDrawable : public Drawable
10
+ {
11
+ int m_width, m_height;
12
+
13
+ public:
14
+ EmptyDrawable(int width, int height)
15
+ : m_width(width),
16
+ m_height(height)
17
+ {
18
+ }
19
+
20
+ int width() const override { return m_width; }
21
+
22
+ int height() const override { return m_height; }
23
+
24
+ void draw(double, double, Color, double, double, Color, //
25
+ double, double, Color, double, double, Color, //
26
+ ZPos, BlendMode) const override
27
+ {
28
+ }
29
+
30
+ GLTexInfo* gl_tex_info() const override { return nullptr; }
31
+
32
+ Bitmap to_bitmap() const override { return Bitmap(m_width, m_height); }
33
+
34
+ std::unique_ptr<Drawable> subimage(const Rect&) const override { return nullptr; }
35
+
36
+ void insert(const Bitmap&, int, int) override { }
37
+ };
38
+ }
data/src/FPS.cpp ADDED
@@ -0,0 +1,31 @@
1
+ #include <Gosu/Timing.hpp>
2
+ #include "GraphicsImpl.hpp"
3
+
4
+ namespace Gosu
5
+ {
6
+ namespace
7
+ {
8
+ int current_fps = 0;
9
+ }
10
+
11
+ void register_frame()
12
+ {
13
+ static unsigned long current_second = Gosu::milliseconds() / 1000;
14
+ static int frames_in_current_second = 0;
15
+
16
+ unsigned long new_sec = Gosu::milliseconds() / 1000;
17
+ if (current_second != new_sec) {
18
+ current_second = new_sec;
19
+ current_fps = frames_in_current_second;
20
+ frames_in_current_second = 0;
21
+ }
22
+ else {
23
+ ++frames_in_current_second;
24
+ }
25
+ }
26
+
27
+ int fps()
28
+ {
29
+ return current_fps;
30
+ }
31
+ }
data/src/Font.cpp CHANGED
@@ -1,74 +1,94 @@
1
1
  #include <Gosu/Font.hpp>
2
- #include <Gosu/Graphics.hpp>
3
2
  #include <Gosu/Image.hpp>
4
- #include <Gosu/Math.hpp>
5
3
  #include <Gosu/Text.hpp>
6
4
  #include <Gosu/Utility.hpp>
7
5
  #include "GraphicsImpl.hpp"
8
6
  #include "MarkupParser.hpp"
9
- #include <array>
10
- #include <map>
7
+ #include <cmath> // for std::ceil
8
+ #include <mutex>
11
9
  #include <stdexcept>
12
- #include <utf8proc.h>
13
-
14
- static const int FONT_RENDER_SCALE = 2;
10
+ #include <unordered_map>
15
11
 
16
12
  struct Gosu::Font::Impl : private Gosu::Noncopyable
17
13
  {
18
- std::string name;
19
- int height;
20
- unsigned base_flags;
21
- unsigned image_flags;
22
-
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
- std::array<std::array<Image, 0x58f>, FF_COMBINATIONS> fast_glyphs;
27
- // Everything else is looked up through a map...
28
- std::array<std::map<utf8proc_int32_t, Image>, FF_COMBINATIONS> other_glyphs;
29
-
30
- Image& image(char32_t codepoint, unsigned font_flags)
14
+ const int height;
15
+ const std::string name;
16
+ const unsigned base_flags;
17
+ const unsigned image_flags;
18
+
19
+ Impl(int height, std::string_view name, unsigned base_flags, unsigned image_flags)
20
+ : height(height),
21
+ name(name),
22
+ base_flags(base_flags),
23
+ image_flags(image_flags)
31
24
  {
32
- Image* image;
33
- if (codepoint < fast_glyphs[font_flags].size()) {
34
- image = &fast_glyphs[font_flags][codepoint];
25
+ }
26
+
27
+ struct GlyphKey
28
+ {
29
+ char32_t codepoint;
30
+ unsigned font_flags;
31
+ bool operator==(const GlyphKey&) const = default;
32
+ };
33
+
34
+ struct GlyphKeyHasher
35
+ {
36
+ std::size_t operator()(const GlyphKey& key) const noexcept
37
+ {
38
+ // The highest legal Unicode code point is 0x10FFFF, which is a 21-bit value, so we do
39
+ // not have to worry about overwriting any of the higher bits.
40
+ // This only works if unordered_map uses prime table sizes (which it should):
41
+ // https://www.reddit.com/r/cpp_questions/comments/us3nyb/comment/i937ulb/
42
+ return key.codepoint | (key.font_flags << 24);
35
43
  }
36
- else {
37
- image = &other_glyphs[font_flags][codepoint];
44
+ };
45
+
46
+ // Font implements copying through its shared_ptr, not by making a true copy. However, Fonts are
47
+ // not immutable: Glyphs are created and cashed on demand, and set_image() enables modifications
48
+ // to the shared data of a font. Having multiple references to the same object, but not being
49
+ // able to use it from two threads, seems counterintuitive, so introduce a mutex here, even
50
+ // though most Gosu games/programs will never really require it.
51
+ // (Could be a shared_mutex, but doesn't seem to be worth the trouble.)
52
+ std::mutex glyphs_mutex;
53
+ std::unordered_map<GlyphKey, Image, GlyphKeyHasher> glyphs;
54
+
55
+ const Image& image(char32_t codepoint, unsigned font_flags)
56
+ {
57
+ const GlyphKey key { codepoint, font_flags };
58
+ if (const auto iterator = glyphs.find(key); iterator != glyphs.end()) {
59
+ return iterator->second;
38
60
  }
39
61
 
40
62
  // 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
- // Optimization: Don't render higher-resolution versions if we use
44
- // next neighbor interpolation anyway.
45
- if (image_flags & IF_RETRO) scaled_height = height;
46
-
47
- std::u32string string(1, codepoint);
48
- Bitmap bitmap(scaled_height, scaled_height);
49
- auto required_width = ceil(Gosu::draw_text(bitmap, 0, 0, Color::WHITE, string, name,
50
- scaled_height, font_flags));
51
- if (required_width > bitmap.width()) {
52
- // If the character was wider than high, we need to render it again.
53
- Bitmap(required_width, scaled_height).swap(bitmap);
54
- Gosu::draw_text(bitmap, 0, 0, Color::WHITE, string, name, scaled_height,
55
- font_flags);
56
- }
63
+ // By default, render each glyph at 200% its size so that we have some wiggle room for
64
+ // changing the font size dynamically without it appearing too blurry.
65
+ auto scaled_height = height * 2;
66
+ // Optimization: Don't render higher-resolution versions if we use
67
+ // next neighbor interpolation anyway.
68
+ if (image_flags & IF_RETRO) {
69
+ scaled_height = height;
70
+ }
57
71
 
58
- *image = Image(bitmap, 0, 0, required_width, scaled_height, image_flags);
72
+ std::u32string string(1, codepoint);
73
+ Bitmap bitmap(scaled_height, scaled_height);
74
+ const int required_width = static_cast<int>(std::ceil(
75
+ Gosu::draw_text(bitmap, 0, 0, Color::WHITE, string, name, scaled_height, font_flags)));
76
+ if (required_width > bitmap.width()) {
77
+ // If the character was wider than high, we need to render it again.
78
+ Bitmap resized_bitmap(required_width, scaled_height);
79
+ std::swap(resized_bitmap, bitmap);
80
+ Gosu::draw_text(bitmap, 0, 0, Color::WHITE, string, name, scaled_height, font_flags);
59
81
  }
82
+ const Rect source_rect { 0, 0, required_width, bitmap.height() };
60
83
 
61
- return *image;
84
+ return glyphs[key] = Image(bitmap, source_rect, image_flags);
62
85
  }
63
86
  };
64
87
 
65
- Gosu::Font::Font(int font_height, const std::string& font_name, unsigned font_flags, unsigned image_flags)
66
- : m_impl{new Impl}
88
+ Gosu::Font::Font(int font_height, std::string_view font_name, unsigned font_flags,
89
+ unsigned image_flags)
90
+ : m_impl(new Impl(font_height, font_name, font_flags, image_flags))
67
91
  {
68
- m_impl->name = font_name;
69
- m_impl->height = font_height;
70
- m_impl->base_flags = font_flags;
71
- m_impl->image_flags = image_flags;
72
92
  }
73
93
 
74
94
  const std::string& Gosu::Font::name() const
@@ -98,15 +118,17 @@ double Gosu::Font::text_width(const std::string& text) const
98
118
 
99
119
  double Gosu::Font::markup_width(const std::string& markup) const
100
120
  {
121
+ const std::unique_lock lock(m_impl->glyphs_mutex);
122
+
101
123
  double width = 0;
102
124
 
103
125
  // Split the text into lines (split_words = false) because Font doesn't implement word-wrapping.
104
- MarkupParser parser(m_impl->base_flags, false, [&](std::vector<FormattedString>&& line) {
126
+ MarkupParser parser(m_impl->base_flags, false, [&](const std::vector<FormattedString>& line) {
105
127
  double line_width = 0;
106
- for (auto& part : line) {
107
- for (auto codepoint : part.text) {
108
- auto& image = m_impl->image(codepoint, part.flags);
109
- double image_scale = 1.0 * height() / image.height();
128
+ for (const auto& part : line) {
129
+ for (const auto codepoint : part.text) {
130
+ const auto& image = m_impl->image(codepoint, part.flags);
131
+ double image_scale = image.height() ? 1.0 * height() / image.height() : 1.0;
110
132
  line_width += image_scale * image.width();
111
133
  }
112
134
  }
@@ -126,15 +148,17 @@ void Gosu::Font::draw_text(const std::string& text, double x, double y, ZPos z,
126
148
  void Gosu::Font::draw_markup(const std::string& markup, double x, double y, ZPos z, //
127
149
  double scale_x, double scale_y, Color c, BlendMode mode) const
128
150
  {
151
+ const std::unique_lock lock(m_impl->glyphs_mutex);
152
+
129
153
  double current_y = y;
130
154
 
131
155
  // Split the text into lines (split_words = false) because Font doesn't implement word-wrapping.
132
- MarkupParser parser(m_impl->base_flags, false, [&](std::vector<FormattedString>&& line) {
156
+ MarkupParser parser(m_impl->base_flags, false, [&](const std::vector<FormattedString>& line) {
133
157
  double current_x = x;
134
- for (auto& part : line) {
135
- for (auto codepoint : part.text) {
136
- auto& image = m_impl->image(codepoint, part.flags);
137
- double image_scale = 1.0 * height() / image.height();
158
+ for (const auto& part : line) {
159
+ for (const auto codepoint : part.text) {
160
+ const auto& image = m_impl->image(codepoint, part.flags);
161
+ double image_scale = image.height() ? 1.0 * height() / image.height() : 1.0;
138
162
  image.draw(current_x, current_y, z, image_scale * scale_x, image_scale * scale_y,
139
163
  multiply(c, part.color), mode);
140
164
  current_x += image_scale * scale_x * image.width();
@@ -145,12 +169,16 @@ void Gosu::Font::draw_markup(const std::string& markup, double x, double y, ZPos
145
169
  parser.parse(markup);
146
170
  }
147
171
 
148
- void Gosu::Font::draw_text_rel(const std::string& text, double x, double y, ZPos z, //
172
+ void Gosu::Font::draw_text_rel(const std::string& text, double x, double y, ZPos z, //
149
173
  double rel_x, double rel_y, double scale_x, double scale_y, //
150
174
  Color c, BlendMode mode) const
151
175
  {
152
- if (rel_x) x -= text_width(text) * scale_x * rel_x;
153
- if (rel_y) y -= height() * scale_y * rel_y;
176
+ if (rel_x != 0) {
177
+ x -= text_width(text) * scale_x * rel_x;
178
+ }
179
+ if (rel_y != 0) {
180
+ y -= height() * scale_y * rel_y;
181
+ }
154
182
 
155
183
  draw_text(text, x, y, z, scale_x, scale_y, c, mode);
156
184
  }
@@ -159,28 +187,30 @@ void Gosu::Font::draw_markup_rel(const std::string& markup, double x, double y,
159
187
  double rel_x, double rel_y, double scale_x, double scale_y,
160
188
  Color c, BlendMode mode) const
161
189
  {
162
- if (rel_x) x -= markup_width(markup) * scale_x * rel_x;
163
- if (rel_y) y -= height() * scale_y * rel_y;
190
+ if (rel_x != 0) {
191
+ x -= markup_width(markup) * scale_x * rel_x;
192
+ }
193
+ if (rel_y != 0) {
194
+ y -= height() * scale_y * rel_y;
195
+ }
164
196
 
165
197
  draw_markup(markup, x, y, z, scale_x, scale_y, c, mode);
166
198
  }
167
199
 
168
- void Gosu::Font::set_image(std::string codepoint, unsigned font_flags, const Gosu::Image& image)
200
+ void Gosu::Font::set_image(std::string_view codepoint, unsigned font_flags,
201
+ const Gosu::Image& image)
169
202
  {
170
- auto utc4 = utf8_to_composed_utc4(codepoint);
203
+ const std::u32string utc4 = utf8_to_composed_utc4(codepoint);
171
204
  if (utc4.length() != 1) {
172
- throw std::invalid_argument{"Could not compose '" + codepoint + "' into single codepoint"};
205
+ throw std::invalid_argument("Could not compose '" + std::string(codepoint)
206
+ + "' into single codepoint");
173
207
  }
174
208
 
175
- if (utc4[0] < m_impl->fast_glyphs[font_flags].size()) {
176
- m_impl->fast_glyphs[font_flags][utc4[0]] = image;
177
- }
178
- else {
179
- m_impl->other_glyphs[font_flags][utc4[0]] = image;
180
- }
209
+ const std::unique_lock lock(m_impl->glyphs_mutex);
210
+ m_impl->glyphs.insert_or_assign({ .codepoint = utc4[0], .font_flags = font_flags }, image);
181
211
  }
182
212
 
183
- void Gosu::Font::set_image(std::string codepoint, const Gosu::Image& image)
213
+ void Gosu::Font::set_image(std::string_view codepoint, const Gosu::Image& image)
184
214
  {
185
215
  for (unsigned font_flags = 0; font_flags < FF_COMBINATIONS; ++font_flags) {
186
216
  set_image(codepoint, font_flags, image);
data/src/GosuGLView.cpp CHANGED
@@ -1,30 +1,32 @@
1
1
  #include <Gosu/Platform.hpp>
2
2
  #if defined(GOSU_IS_IPHONE)
3
3
 
4
- #import "GosuGLView.hpp"
5
4
  #import <Gosu/Input.hpp>
6
5
  #import <Gosu/TextInput.hpp>
7
-
6
+ #import "GosuGLView.hpp"
7
+ #import "GraphicsImpl.hpp"
8
+ #import "OpenGLContext.hpp"
8
9
  #import <OpenGLES/EAGL.h>
9
10
  #import <OpenGLES/EAGLDrawable.h>
10
- #import <OpenGLES/ES1/gl.h>
11
- #import <OpenGLES/ES1/glext.h>
12
11
  #import <QuartzCore/QuartzCore.h>
13
12
 
14
-
15
13
  static EAGLContext __weak* globalContext;
16
14
 
17
15
  namespace Gosu
18
16
  {
19
- void ensure_current_context()
17
+ OpenGLContext::OpenGLContext(bool)
20
18
  {
19
+ // Gosu does not support multithreading on iOS.
20
+ if (![NSThread isMainThread]) {
21
+ throw std::logic_error("Multi-threaded OpenGL access is not supported on iOS");
22
+ }
23
+
21
24
  [EAGLContext setCurrentContext:globalContext];
22
25
  }
23
-
24
- int clip_rect_base_factor()
26
+
27
+ OpenGLContext::~OpenGLContext()
25
28
  {
26
- static int result = [UIScreen mainScreen].scale;
27
- return result;
29
+ // Don't bother unsetting anything.
28
30
  }
29
31
  }
30
32
 
@@ -89,7 +91,7 @@ namespace Gosu
89
91
  [EAGLContext setCurrentContext:_context];
90
92
  [self destroyFramebuffer];
91
93
  [self createFramebuffer];
92
- self.contentScaleFactor = Gosu::clip_rect_base_factor();
94
+ self.contentScaleFactor = [UIScreen mainScreen].scale;
93
95
  }
94
96
 
95
97
  - (BOOL)createFramebuffer
@@ -10,14 +10,6 @@
10
10
  #import <AudioToolbox/AudioSession.h>
11
11
  #import <OpenAL/alc.h>
12
12
 
13
- namespace Gosu
14
- {
15
- namespace FPS
16
- {
17
- void register_frame();
18
- }
19
- }
20
-
21
13
  static void handle_audio_interruption(void* unused, UInt32 inInterruptionState)
22
14
  {
23
15
  if (inInterruptionState == kAudioSessionBeginInterruption) {
@@ -180,9 +172,9 @@ static void handle_audio_interruption(void* unused, UInt32 inInterruptionState)
180
172
 
181
173
  if (window.needs_redraw()) {
182
174
  [(GosuGLView*)self.view redrawGL:^{
183
- window.graphics().frame([&window] {
175
+ window.viewport().frame([&window] {
184
176
  window.draw();
185
- Gosu::FPS::register_frame();
177
+ Gosu::register_frame();
186
178
  });
187
179
  }];
188
180
  }