gosu 1.4.6 → 2.0.0.pre6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/COPYING +2 -1
  3. data/dependencies/SDL/include/SDL_atomic.h +2 -3
  4. data/dependencies/SDL/include/SDL_audio.h +7 -7
  5. data/dependencies/SDL/include/SDL_blendmode.h +1 -1
  6. data/dependencies/SDL/include/SDL_endian.h +3 -3
  7. data/dependencies/SDL/include/SDL_gamecontroller.h +4 -4
  8. data/dependencies/SDL/include/SDL_hints.h +72 -28
  9. data/dependencies/SDL/include/SDL_joystick.h +8 -5
  10. data/dependencies/SDL/include/SDL_keycode.h +1 -1
  11. data/dependencies/SDL/include/SDL_main.h +7 -0
  12. data/dependencies/SDL/include/SDL_mouse.h +6 -7
  13. data/dependencies/SDL/include/SDL_mutex.h +79 -5
  14. data/dependencies/SDL/include/SDL_opengl_glext.h +5 -1
  15. data/dependencies/SDL/include/SDL_power.h +7 -8
  16. data/dependencies/SDL/include/SDL_render.h +5 -0
  17. data/dependencies/SDL/include/SDL_revision.h +2 -2
  18. data/dependencies/SDL/include/SDL_sensor.h +1 -1
  19. data/dependencies/SDL/include/SDL_stdinc.h +19 -11
  20. data/dependencies/SDL/include/SDL_thread.h +2 -2
  21. data/dependencies/SDL/include/SDL_version.h +2 -2
  22. data/dependencies/SDL/include/SDL_video.h +30 -2
  23. data/dependencies/SDL/include/begin_code.h +7 -7
  24. data/dependencies/SDL/include/close_code.h +2 -2
  25. data/dependencies/SDL/lib/x64/libSDL2.dll.a +0 -0
  26. data/dependencies/SDL/lib/x86/libSDL2.dll.a +0 -0
  27. data/dependencies/SDL_sound/SDL_sound.h +1 -1
  28. data/dependencies/SDL_sound/dr_flac.h +48 -23
  29. data/dependencies/SDL_sound/dr_mp3.h +34 -14
  30. data/dependencies/SDL_sound/stb_vorbis.h +3 -2
  31. data/dependencies/mojoAL/mojoal.c +1 -1
  32. data/ext/{gosu → gosu-ffi}/extconf.rb +34 -33
  33. data/ext/gosu-ffi/gosu-ffi.def +464 -0
  34. data/ffi/Gosu.cpp +307 -0
  35. data/ffi/Gosu.h +84 -0
  36. data/ffi/Gosu_Channel.cpp +62 -0
  37. data/ffi/Gosu_Channel.h +17 -0
  38. data/ffi/Gosu_Color.cpp +132 -0
  39. data/ffi/Gosu_Color.h +31 -0
  40. data/ffi/Gosu_Constants.cpp +334 -0
  41. data/ffi/Gosu_FFI.h +34 -0
  42. data/ffi/Gosu_FFI_internal.h +161 -0
  43. data/ffi/Gosu_Font.cpp +92 -0
  44. data/ffi/Gosu_Font.h +32 -0
  45. data/ffi/Gosu_Image.cpp +206 -0
  46. data/ffi/Gosu_Image.h +60 -0
  47. data/ffi/Gosu_Sample.cpp +29 -0
  48. data/ffi/Gosu_Sample.h +14 -0
  49. data/ffi/Gosu_Song.cpp +69 -0
  50. data/ffi/Gosu_Song.h +18 -0
  51. data/ffi/Gosu_TextInput.cpp +94 -0
  52. data/ffi/Gosu_TextInput.h +25 -0
  53. data/ffi/Gosu_Window.cpp +314 -0
  54. data/ffi/Gosu_Window.h +78 -0
  55. data/include/Gosu/Audio.hpp +6 -11
  56. data/include/Gosu/Bitmap.hpp +38 -53
  57. data/include/Gosu/Buffer.hpp +54 -0
  58. data/include/Gosu/Color.hpp +27 -35
  59. data/include/Gosu/Directories.hpp +25 -28
  60. data/include/Gosu/Drawable.hpp +58 -0
  61. data/include/Gosu/Font.hpp +6 -5
  62. data/include/Gosu/Fwd.hpp +4 -6
  63. data/include/Gosu/Gosu.hpp +5 -5
  64. data/include/Gosu/Graphics.hpp +51 -61
  65. data/include/Gosu/GraphicsBase.hpp +1 -11
  66. data/include/Gosu/Image.hpp +11 -14
  67. data/include/Gosu/Math.hpp +50 -72
  68. data/include/Gosu/Transform.hpp +32 -0
  69. data/include/Gosu/Utility.hpp +51 -1
  70. data/include/Gosu/Version.hpp +3 -3
  71. data/include/Gosu/Window.hpp +15 -9
  72. data/lib/SDL2.dll +0 -0
  73. data/lib/gosu/channel.rb +49 -0
  74. data/lib/gosu/color.rb +150 -0
  75. data/lib/gosu/compat.rb +29 -8
  76. data/lib/gosu/constants.rb +386 -0
  77. data/lib/gosu/ffi.rb +258 -0
  78. data/lib/gosu/font.rb +56 -0
  79. data/lib/gosu/gl_tex_info.rb +33 -0
  80. data/lib/gosu/gosu.rb +210 -0
  81. data/lib/gosu/image.rb +141 -0
  82. data/lib/gosu/numeric.rb +17 -0
  83. data/lib/gosu/preview.rb +6 -6
  84. data/lib/gosu/sample.rb +24 -0
  85. data/lib/gosu/song.rb +56 -0
  86. data/lib/gosu/text_input.rb +69 -0
  87. data/lib/gosu/window.rb +228 -0
  88. data/lib/gosu.rb +29 -8
  89. data/lib64/SDL2.dll +0 -0
  90. data/rdoc/gosu.rb +0 -2
  91. data/src/Audio.cpp +12 -12
  92. data/src/AudioFile.hpp +5 -4
  93. data/src/AudioFileAudioToolbox.cpp +8 -8
  94. data/src/AudioFileSDLSound.cpp +7 -10
  95. data/src/BinPacker.cpp +187 -0
  96. data/src/BinPacker.hpp +55 -0
  97. data/src/Bitmap.cpp +166 -144
  98. data/src/BitmapIO.cpp +60 -86
  99. data/src/Buffer.cpp +159 -0
  100. data/src/Color.cpp +75 -80
  101. data/src/Directories.cpp +47 -0
  102. data/src/DirectoriesUIKit.cpp +50 -0
  103. data/src/DrawOp.hpp +9 -4
  104. data/src/DrawOpQueue.hpp +2 -2
  105. data/src/Drawable.cpp +95 -0
  106. data/src/EmptyDrawable.hpp +38 -0
  107. data/src/FPS.cpp +31 -0
  108. data/src/Font.cpp +104 -74
  109. data/src/GosuGLView.cpp +14 -6
  110. data/src/GosuViewController.cpp +2 -10
  111. data/src/Graphics.cpp +60 -126
  112. data/src/GraphicsImpl.hpp +17 -47
  113. data/src/Image.cpp +41 -35
  114. data/src/Input.cpp +7 -8
  115. data/src/Macro.cpp +6 -6
  116. data/src/Macro.hpp +4 -4
  117. data/src/MarkupParser.cpp +5 -5
  118. data/src/Math.cpp +35 -22
  119. data/src/OffScreenTarget.cpp +53 -49
  120. data/src/OffScreenTarget.hpp +13 -11
  121. data/src/OpenGLContext.cpp +117 -0
  122. data/src/OpenGLContext.hpp +41 -0
  123. data/src/RenderState.hpp +21 -19
  124. data/src/Resolution.cpp +23 -21
  125. data/src/TexChunk.cpp +35 -80
  126. data/src/TexChunk.hpp +44 -35
  127. data/src/Text.cpp +1 -1
  128. data/src/TextBuilder.cpp +35 -21
  129. data/src/TextBuilder.hpp +6 -9
  130. data/src/Texture.cpp +62 -80
  131. data/src/Texture.hpp +25 -23
  132. data/src/TiledDrawable.cpp +150 -0
  133. data/src/TiledDrawable.hpp +47 -0
  134. data/src/TimingApple.cpp +1 -1
  135. data/src/Transform.cpp +45 -50
  136. data/src/TransformStack.hpp +16 -16
  137. data/src/TrueTypeFont.cpp +59 -51
  138. data/src/TrueTypeFont.hpp +6 -7
  139. data/src/TrueTypeFontApple.cpp +28 -19
  140. data/src/TrueTypeFontUnix.cpp +27 -23
  141. data/src/TrueTypeFontWin.cpp +30 -30
  142. data/src/Utility.cpp +84 -21
  143. data/src/UtilityWin.cpp +45 -0
  144. data/src/Window.cpp +92 -142
  145. data/src/WindowUIKit.cpp +14 -14
  146. metadata +72 -31
  147. data/include/Gosu/IO.hpp +0 -254
  148. data/include/Gosu/ImageData.hpp +0 -53
  149. data/include/Gosu/Inspection.hpp +0 -7
  150. data/lib/gosu/patches.rb +0 -66
  151. data/lib/gosu/run.rb +0 -20
  152. data/lib/gosu/swig_patches.rb +0 -110
  153. data/src/BlockAllocator.cpp +0 -131
  154. data/src/BlockAllocator.hpp +0 -32
  155. data/src/DirectoriesApple.cpp +0 -69
  156. data/src/DirectoriesUnix.cpp +0 -46
  157. data/src/DirectoriesWin.cpp +0 -65
  158. data/src/EmptyImageData.hpp +0 -52
  159. data/src/FileUnix.cpp +0 -99
  160. data/src/FileWin.cpp +0 -88
  161. data/src/IO.cpp +0 -60
  162. data/src/Iconv.hpp +0 -51
  163. data/src/Inspection.cpp +0 -27
  164. data/src/LargeImageData.cpp +0 -215
  165. data/src/LargeImageData.hpp +0 -39
  166. data/src/Log.hpp +0 -19
  167. data/src/RubyGosu.cxx +0 -13100
  168. data/src/RubyGosu.h +0 -49
  169. data/src/WinUtility.cpp +0 -61
  170. data/src/WinUtility.hpp +0 -27
data/src/Buffer.cpp ADDED
@@ -0,0 +1,159 @@
1
+ #include <Gosu/Buffer.hpp>
2
+ #include <Gosu/Platform.hpp>
3
+ #include <limits>
4
+ #include <memory>
5
+ #include <stdexcept>
6
+
7
+ #ifdef GOSU_IS_IPHONE
8
+ #include <filesystem>
9
+ #include <fstream>
10
+ #else
11
+ #include <SDL.h>
12
+ #endif
13
+
14
+ Gosu::Buffer::Buffer(void* buffer, std::size_t size, std::function<void(void*)> deleter)
15
+ : m_buffer(buffer),
16
+ m_size(size),
17
+ m_deleter(std::move(deleter))
18
+ {
19
+ if (buffer == nullptr && size > 0) {
20
+ throw std::invalid_argument("Tried to create non-empty Gosu::Buffer from nullptr");
21
+ }
22
+ if (size >= std::numeric_limits<std::ptrdiff_t>::max()) {
23
+ throw std::length_error("Tried to create Gosu::Buffer with a negative size");
24
+ }
25
+ }
26
+
27
+ Gosu::Buffer::Buffer(Gosu::Buffer&& other) noexcept
28
+ : m_buffer(nullptr),
29
+ m_size(0),
30
+ m_deleter(nullptr)
31
+ {
32
+ *this = std::move(other);
33
+ }
34
+
35
+ Gosu::Buffer& Gosu::Buffer::operator=(Gosu::Buffer&& other) noexcept
36
+ {
37
+ if (this == &other) {
38
+ return *this;
39
+ }
40
+
41
+ if (m_buffer && m_deleter) {
42
+ m_deleter(m_buffer);
43
+ }
44
+
45
+ m_buffer = other.m_buffer;
46
+ m_size = other.m_size;
47
+ // In theory, this could throw. In that case, we are fine with a full crash of the current
48
+ // process (as this function is marked noexcept) because throwing deleters are a user error.
49
+ m_deleter = std::move(other.m_deleter);
50
+
51
+ other.m_buffer = nullptr;
52
+ other.m_size = 0;
53
+ other.m_deleter = nullptr;
54
+
55
+ return *this;
56
+ }
57
+
58
+ Gosu::Buffer::~Buffer()
59
+ {
60
+ if (m_buffer && m_deleter) {
61
+ m_deleter(m_buffer);
62
+ }
63
+ }
64
+
65
+ Gosu::Buffer::Buffer(std::size_t size)
66
+ : m_buffer(nullptr),
67
+ m_size(size),
68
+ m_deleter([](void* p) { delete[] static_cast<std::uint8_t*>(p); })
69
+ {
70
+ if (size >= std::numeric_limits<std::ptrdiff_t>::max()) {
71
+ throw std::length_error("Tried to create Gosu::Buffer with a negative size");
72
+ }
73
+
74
+ if (m_size) {
75
+ m_buffer = new std::uint8_t[m_size];
76
+ }
77
+ }
78
+
79
+ Gosu::Buffer::Buffer(std::vector<std::uint8_t>&& vector) noexcept
80
+ : m_buffer(vector.data()),
81
+ m_size(vector.size()),
82
+ // Absorb the vector into this Gosu::Buffer by moving it into the deleter lambda.
83
+ // This is safe because the vector move constructor guarantees that pointers remain stable.
84
+ m_deleter([vector = std::move(vector)](void*) mutable { vector.clear(); })
85
+ {
86
+ }
87
+
88
+ Gosu::Buffer::Buffer(const Gosu::Buffer& other)
89
+ : Buffer(std::vector<std::uint8_t>(other.data(), other.data() + other.size()))
90
+ {
91
+ }
92
+
93
+ Gosu::Buffer& Gosu::Buffer::operator=(const Gosu::Buffer& other)
94
+ {
95
+ if (this == &other) {
96
+ return *this;
97
+ }
98
+
99
+ // Delegate to copy constructor + move assignment operator.
100
+ return *this = Buffer(other);
101
+ }
102
+
103
+ #ifdef GOSU_IS_IPHONE
104
+ Gosu::Buffer Gosu::load_file(const std::string& filename)
105
+ {
106
+ Buffer buffer(std::filesystem::file_size(filename));
107
+ char* ptr = reinterpret_cast<char*>(buffer.data());
108
+ std::ifstream file(filename, std::ios::binary);
109
+ // GCOV_EXCL_START: It is hard to set up a file where file_size works but reading doesn't.
110
+ if (!file || !file.read(ptr, static_cast<std::streamoff>(buffer.size()))) {
111
+ throw std::runtime_error("Could not read file '" + filename + "'");
112
+ }
113
+ // GCOV_EXCL_END
114
+ return buffer;
115
+ }
116
+
117
+ void Gosu::save_file(const Buffer& buffer, const std::string& filename)
118
+ {
119
+ std::ofstream file(filename, std::ios::binary | std::ios::trunc);
120
+ const char* ptr = reinterpret_cast<const char*>(buffer.data());
121
+ if (!file || !file.write(ptr, static_cast<std::streamoff>(buffer.size()))) {
122
+ throw std::runtime_error("Could not write file '" + filename + "'");
123
+ }
124
+ }
125
+ #else
126
+ Gosu::Buffer Gosu::load_file(const std::string& filename)
127
+ {
128
+ std::size_t size = 0;
129
+ void* contents = SDL_LoadFile(filename.c_str(), &size);
130
+ if (contents == nullptr) {
131
+ throw std::runtime_error("Could not read '" + filename + "', error: " + SDL_GetError());
132
+ }
133
+ return Buffer(contents, size, &SDL_free);
134
+ }
135
+
136
+ void Gosu::save_file(const Buffer& buffer, const std::string& filename)
137
+ {
138
+ struct RWopsDeleter
139
+ {
140
+ void operator()(SDL_RWops* p) const
141
+ {
142
+ // SDL_RWclose will crash if given nullptr.
143
+ if (p) {
144
+ SDL_RWclose(p);
145
+ }
146
+ }
147
+ };
148
+ std::unique_ptr<SDL_RWops, RWopsDeleter> rwops(SDL_RWFromFile(filename.c_str(), "w"));
149
+ if (rwops == nullptr) {
150
+ throw std::runtime_error("Could not open '" + filename + "', error: " + SDL_GetError());
151
+ }
152
+ std::size_t written = SDL_RWwrite(rwops.get(), buffer.data(), buffer.size(), 1);
153
+ // GCOV_EXCL_START: It is hard to set up a file where opening works but writing doesn't.
154
+ if (written == 0) {
155
+ throw std::runtime_error("Could not write to '" + filename + "', error: " + SDL_GetError());
156
+ }
157
+ // GCOV_EXCL_END
158
+ }
159
+ #endif
data/src/Color.cpp CHANGED
@@ -1,56 +1,8 @@
1
1
  #include <Gosu/Color.hpp>
2
2
  #include <Gosu/Math.hpp>
3
- #include <algorithm>
3
+ #include <algorithm> // for std::min, std::max
4
4
  #include <cmath>
5
-
6
- namespace
7
- {
8
- struct HSV
9
- {
10
- double h, s, v;
11
- };
12
-
13
- HSV color_to_hsv(const Gosu::Color& c)
14
- {
15
- double r = c.red / 255.0;
16
- double g = c.green / 255.0;
17
- double b = c.blue / 255.0;
18
-
19
- double min = std::min(std::min(r, g), b);
20
- double max = std::max(std::max(r, g), b);
21
- double delta = max - min;
22
-
23
- if (max == 0) {
24
- HSV hsv = {0, 0, 0};
25
- return hsv;
26
- }
27
-
28
- HSV hsv{};
29
-
30
- // Value.
31
- hsv.v = max;
32
-
33
- // Saturation.
34
- hsv.s = delta / max;
35
-
36
- // Hue.
37
- if (delta == 0) {
38
- hsv.h = 0;
39
- }
40
- else if (r == max) {
41
- hsv.h = (g - b) / delta + (g < b ? 6 : 0);
42
- }
43
- else if (g == max) {
44
- hsv.h = (b - r) / delta + 2;
45
- }
46
- else {
47
- hsv.h = (r - g) / delta + 4;
48
- }
49
- hsv.h *= 60;
50
-
51
- return hsv;
52
- }
53
- }
5
+ #include <iomanip> // for std::setw
54
6
 
55
7
  Gosu::Color Gosu::Color::from_hsv(double h, double s, double v)
56
8
  {
@@ -60,32 +12,51 @@ Gosu::Color Gosu::Color::from_hsv(double h, double s, double v)
60
12
  s = std::clamp(s, 0.0, 1.0);
61
13
  v = std::clamp(v, 0.0, 1.0);
62
14
 
15
+ const auto to_channel = [](double d) { return static_cast<Channel>(std::round(255.0 * d)); };
16
+
63
17
  int sector = static_cast<int>(h / 60);
64
- double factorial = h / 60 - sector;
18
+ double remainder = h / 60 - sector;
65
19
 
66
- Channel p = static_cast<Channel>(255 * v * (1 - s));
67
- Channel q = static_cast<Channel>(255 * v * (1 - s * factorial));
68
- Channel t = static_cast<Channel>(255 * v * (1 - s * (1 - factorial)));
20
+ Channel p = to_channel(v * (1 - s));
21
+ Channel q = to_channel(v * (1 - s * remainder));
22
+ Channel t = to_channel(v * (1 - s * (1 - remainder)));
69
23
 
70
24
  switch (sector) {
71
25
  case 0:
72
- return Color{static_cast<Channel>(255 * v), t, p};
26
+ return Color { to_channel(v), t, p };
73
27
  case 1:
74
- return Color{q, static_cast<Channel>(255 * v), p};
28
+ return Color { q, to_channel(v), p };
75
29
  case 2:
76
- return Color{p, static_cast<Channel>(255 * v), t};
30
+ return Color { p, to_channel(v), t };
77
31
  case 3:
78
- return Color{p, q, static_cast<Channel>(255 * v)};
32
+ return Color { p, q, to_channel(v) };
79
33
  case 4:
80
- return Color{t, p, static_cast<Channel>(255 * v)};
34
+ return Color { t, p, to_channel(v) };
81
35
  default: // sector 5
82
- return Color{static_cast<Channel>(255 * v), p, q};
36
+ return Color { to_channel(v), p, q };
83
37
  }
84
38
  }
85
39
 
86
40
  double Gosu::Color::hue() const
87
41
  {
88
- return color_to_hsv(*this).h;
42
+ double max = std::max({ red, green, blue });
43
+ double min = std::min({ red, green, blue });
44
+
45
+ if (min == max) {
46
+ return 0;
47
+ }
48
+
49
+ double factor = 60 / (max - min);
50
+
51
+ if (green == max) {
52
+ return (blue - red) * factor + 120; // 60...180
53
+ } else if (blue == max) {
54
+ return (red - green) * factor + 240; // 180...300
55
+ } else if (blue > green) {
56
+ return (green - blue) * factor + 360; // 300...360
57
+ } else {
58
+ return (green - blue) * factor + 0; // 0...60
59
+ }
89
60
  }
90
61
 
91
62
  void Gosu::Color::set_hue(double h)
@@ -95,7 +66,15 @@ void Gosu::Color::set_hue(double h)
95
66
 
96
67
  double Gosu::Color::saturation() const
97
68
  {
98
- return color_to_hsv(*this).s;
69
+ double max = std::max({ red, green, blue });
70
+
71
+ if (max == 0) {
72
+ return 0;
73
+ }
74
+
75
+ double min = std::min({ red, green, blue });
76
+
77
+ return 1 - (min / max);
99
78
  }
100
79
 
101
80
  void Gosu::Color::set_saturation(double s)
@@ -105,7 +84,7 @@ void Gosu::Color::set_saturation(double s)
105
84
 
106
85
  double Gosu::Color::value() const
107
86
  {
108
- return color_to_hsv(*this).v;
87
+ return std::max({ red, green, blue }) / 255.0;
109
88
  }
110
89
 
111
90
  void Gosu::Color::set_value(double v)
@@ -113,10 +92,23 @@ void Gosu::Color::set_value(double v)
113
92
  *this = from_hsv(hue(), saturation(), v).with_alpha(alpha);
114
93
  }
115
94
 
95
+ const Gosu::Color Gosu::Color::NONE { 0x00'000000 };
96
+ const Gosu::Color Gosu::Color::BLACK { 0, 0, 0 };
97
+ const Gosu::Color Gosu::Color::GRAY { 128, 128, 128 };
98
+ const Gosu::Color Gosu::Color::WHITE { 255, 255, 255 };
99
+
100
+ const Gosu::Color Gosu::Color::AQUA { 0, 255, 255 };
101
+ const Gosu::Color Gosu::Color::RED { 255, 0, 0 };
102
+ const Gosu::Color Gosu::Color::GREEN { 0, 255, 0 };
103
+ const Gosu::Color Gosu::Color::BLUE { 0, 0, 255 };
104
+ const Gosu::Color Gosu::Color::YELLOW { 255, 255, 0 };
105
+ const Gosu::Color Gosu::Color::FUCHSIA { 255, 0, 255 };
106
+ const Gosu::Color Gosu::Color::CYAN { 0, 255, 255 };
107
+
116
108
  Gosu::Color Gosu::lerp(Color a, Color b, double t)
117
109
  {
118
110
  const auto lerp_channel = [](Color::Channel a, Color::Channel b, double t) {
119
- return static_cast<Color::Channel>(std::clamp(std::round(lerp(a, b, t)), 0.0, 255.0));
111
+ return static_cast<Color::Channel>(std::clamp(std::round(std::lerp(a, b, t)), 0.0, 255.0));
120
112
  };
121
113
 
122
114
  Color result;
@@ -129,23 +121,26 @@ Gosu::Color Gosu::lerp(Color a, Color b, double t)
129
121
 
130
122
  Gosu::Color Gosu::multiply(Color a, Color b)
131
123
  {
124
+ const auto multiply_channel = [](Color::Channel a, Color::Channel b) {
125
+ return static_cast<Color::Channel>(std::round(a * b / 255.0));
126
+ };
127
+
132
128
  Color result;
133
- result.red = static_cast<Color::Channel>(std::round(a.red * b.red / 255.0));
134
- result.green = static_cast<Color::Channel>(std::round(a.green * b.green / 255.0));
135
- result.blue = static_cast<Color::Channel>(std::round(a.blue * b.blue / 255.0));
136
- result.alpha = static_cast<Color::Channel>(std::round(a.alpha * b.alpha / 255.0));
129
+ result.red = multiply_channel(a.red, b.red);
130
+ result.green = multiply_channel(a.green, b.green);
131
+ result.blue = multiply_channel(a.blue, b.blue);
132
+ result.alpha = multiply_channel(a.alpha, b.alpha);
137
133
  return result;
138
134
  }
139
135
 
140
- const Gosu::Color Gosu::Color::NONE{0x00'000000};
141
- const Gosu::Color Gosu::Color::BLACK{0, 0, 0};
142
- const Gosu::Color Gosu::Color::GRAY{128, 128, 128};
143
- const Gosu::Color Gosu::Color::WHITE{255, 255, 255};
144
-
145
- const Gosu::Color Gosu::Color::AQUA{0, 255, 255};
146
- const Gosu::Color Gosu::Color::RED{255, 0, 0};
147
- const Gosu::Color Gosu::Color::GREEN{0, 255, 0};
148
- const Gosu::Color Gosu::Color::BLUE{0, 0, 255};
149
- const Gosu::Color Gosu::Color::YELLOW{255, 255, 0};
150
- const Gosu::Color Gosu::Color::FUCHSIA{255, 0, 255};
151
- const Gosu::Color Gosu::Color::CYAN{0, 255, 255};
136
+ std::ostream& Gosu::operator<<(std::ostream& stream, Gosu::Color color)
137
+ {
138
+ const auto previous_flags = stream.flags();
139
+ const char previous_fill = stream.fill();
140
+ stream << std::setfill('0');
141
+ stream << "0x" << std::hex << std::setw(2) << static_cast<int>(color.alpha);
142
+ stream << '\'' << std::setw(6) << static_cast<int>(color.argb() & 0x00'ffffff);
143
+ stream.flags(previous_flags);
144
+ stream.fill(previous_fill);
145
+ return stream;
146
+ }
@@ -0,0 +1,47 @@
1
+ #include <Gosu/Directories.hpp>
2
+ #include <Gosu/Platform.hpp>
3
+
4
+ #ifdef GOSU_IS_WIN
5
+ #include <Gosu/Utility.hpp>
6
+ #include <windows.h>
7
+ #else
8
+ #include <unistd.h>
9
+ #endif
10
+
11
+ void Gosu::use_resource_directory()
12
+ {
13
+ #ifdef GOSU_IS_WIN
14
+ SetCurrentDirectoryW(utf8_to_utf16(resource_path()).c_str());
15
+ #else
16
+ chdir(resource_path().c_str());
17
+ #endif
18
+ }
19
+
20
+ #ifndef GOSU_IS_IPHONE
21
+ #include <SDL.h>
22
+ #include <memory>
23
+ #include <stdexcept>
24
+ #endif
25
+
26
+ #ifndef GOSU_IS_IPHONE
27
+ std::string Gosu::resource_path(const std::string& relative_filename)
28
+ {
29
+ static const char* resource_prefix = SDL_GetBasePath(); // never freed, doesn't matter
30
+ return relative_filename.empty() ? resource_prefix : resource_prefix + relative_filename;
31
+ }
32
+
33
+ std::string Gosu::user_settings_path(const std::string& organization,
34
+ const std::string& application,
35
+ const std::string& relative_filename)
36
+ {
37
+ char* settings_prefix = SDL_GetPrefPath(organization.c_str(), application.c_str());
38
+ // GCOV_EXCL_START: Hard to simulate errors here because even creating "/:\\/:\\" works (macOS).
39
+ if (!settings_prefix) {
40
+ throw std::runtime_error("Could not create settings directory: "
41
+ + std::string(SDL_GetError()));
42
+ }
43
+ // GCOV_EXCL_END
44
+ const std::unique_ptr<char, decltype(&SDL_free)> guard(settings_prefix, &SDL_free);
45
+ return relative_filename.empty() ? settings_prefix : settings_prefix + relative_filename;
46
+ }
47
+ #endif
@@ -0,0 +1,50 @@
1
+ #include <Gosu/Directories.hpp>
2
+ #include <Gosu/Platform.hpp>
3
+ #if defined(GOSU_IS_IPHONE)
4
+
5
+ #import <Foundation/Foundation.h>
6
+ #include <filesystem>
7
+
8
+ std::string Gosu::resource_path(const std::string& relative_filename)
9
+ {
10
+ static const std::string resource_prefix = [] {
11
+ @autoreleasepool {
12
+ NSString* resources = [NSBundle mainBundle].resourcePath;
13
+ return std::string(resources.UTF8String ?: ".");
14
+ }
15
+ }();
16
+
17
+ return relative_filename.empty() ? resource_prefix : resource_prefix + "/" + relative_filename;
18
+ }
19
+
20
+ std::string Gosu::user_settings_path(const std::string& organization,
21
+ const std::string& application,
22
+ const std::string& relative_filename)
23
+ {
24
+ static const std::string user_settings_prefix = [] {
25
+ @autoreleasepool {
26
+ NSString* library
27
+ = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)
28
+ .firstObject;
29
+ NSString* preferences = [library stringByAppendingPathComponent:@"Application Support"];
30
+ return std::string(preferences.UTF8String ?: ".");
31
+ }
32
+ }();
33
+
34
+ std::string directory = user_settings_prefix;
35
+ if (!organization.empty()) {
36
+ directory += '/';
37
+ directory += organization;
38
+ }
39
+ if (!application.empty()) {
40
+ directory += '/';
41
+ directory += application;
42
+ }
43
+ if (!std::filesystem::is_directory(directory)) {
44
+ std::filesystem::create_directories(directory);
45
+ }
46
+
47
+ return relative_filename.empty() ? directory : directory + '/' + relative_filename;
48
+ }
49
+
50
+ #endif
data/src/DrawOp.hpp CHANGED
@@ -17,7 +17,9 @@ namespace Gosu
17
17
  RenderState render_state;
18
18
  // Only valid if render_state.tex_name != NO_TEXTURE
19
19
  GLfloat top, left, bottom, right;
20
-
20
+ // Used to keep TexChunk rectangles on shared textures alive until the end of the frame.
21
+ std::shared_ptr<const Rect> rect_handle;
22
+
21
23
  // TODO: Merge with Gosu::ArrayVertex.
22
24
  struct Vertex
23
25
  {
@@ -30,7 +32,7 @@ namespace Gosu
30
32
 
31
33
  // Number of vertices used, or: complement index of code block
32
34
  int vertices_or_block_index;
33
-
35
+
34
36
  void perform(const DrawOp* next) const
35
37
  {
36
38
  // This should not be called on GL code ops.
@@ -141,8 +143,11 @@ namespace Gosu
141
143
  result[i].vertices[1] = vertices[i].y;
142
144
  result[i].vertices[2] = 0;
143
145
  result[i].color = vertices[i].c.abgr();
144
- apply_transform(*render_state.transform,
145
- result[i].vertices[0], result[i].vertices[1]);
146
+ double x = result[i].vertices[0];
147
+ double y = result[i].vertices[1];
148
+ render_state.transform->apply(x, y);
149
+ result[i].vertices[0] = static_cast<float>(x);
150
+ result[i].vertices[1] = static_cast<float>(y);
146
151
  }
147
152
  RenderState va_render_state = render_state;
148
153
  va_render_state.transform = 0;
data/src/DrawOpQueue.hpp CHANGED
@@ -77,8 +77,8 @@ public:
77
77
  double left = x, right = x + width;
78
78
  double top = y, bottom = y + height;
79
79
 
80
- apply_transform(transform_stack.current(), left, top);
81
- apply_transform(transform_stack.current(), right, bottom);
80
+ transform_stack.current().apply(left, top);
81
+ transform_stack.current().apply(right, bottom);
82
82
 
83
83
  double phys_x = std::min(left, right);
84
84
  double phys_y = std::min(top, bottom);
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
+ }