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
@@ -1,31 +1,30 @@
1
1
  #include <Gosu/Platform.hpp>
2
2
  #if defined(GOSU_IS_WIN)
3
3
 
4
- #include "Log.hpp"
5
4
  #include "TrueTypeFont.hpp"
6
5
 
7
- #define _WIN32_WINNT 0x0500
8
- #include <Gosu/IO.hpp>
6
+ #include <Gosu/Buffer.hpp>
9
7
  #include <Gosu/Text.hpp>
10
8
  #include <Gosu/Utility.hpp>
11
- #include "WinUtility.hpp"
12
9
  #include <cstdlib>
13
10
  #include <cwchar>
14
11
  #include <map>
15
12
  #include <memory>
13
+ #include <mutex>
16
14
  #include <windows.h>
17
15
 
18
- const unsigned char* Gosu::ttf_data_by_name(const std::string& font_name, unsigned font_flags)
16
+ const std::uint8_t* Gosu::ttf_data_by_name(const std::string& font_name, unsigned font_flags)
19
17
  {
20
- // TODO: Make this cache thread-safe.
21
18
  static std::map<std::pair<std::string, unsigned>, std::shared_ptr<Buffer>> ttf_file_cache;
19
+ static std::recursive_mutex mutex;
20
+ std::scoped_lock lock(mutex);
22
21
 
23
22
  auto& buffer_ptr = ttf_file_cache[make_pair(font_name, font_flags)];
24
- if (buffer_ptr) return static_cast<const unsigned char*>(buffer_ptr->data());
25
-
26
- log("Trying to find a font named '%s', flags=%x", font_name.c_str(), font_flags);
23
+ if (buffer_ptr) {
24
+ return static_cast<const std::uint8_t*>(buffer_ptr->data());
25
+ }
27
26
 
28
- LOGFONT logfont = {0,
27
+ LOGFONTW logfont { 0,
29
28
  0,
30
29
  0,
31
30
  0,
@@ -37,51 +36,52 @@ const unsigned char* Gosu::ttf_data_by_name(const std::string& font_name, unsign
37
36
  OUT_OUTLINE_PRECIS,
38
37
  CLIP_DEFAULT_PRECIS,
39
38
  ANTIALIASED_QUALITY,
40
- DEFAULT_PITCH};
39
+ DEFAULT_PITCH };
41
40
 
42
41
  std::wstring wfont_name = utf8_to_utf16(font_name);
43
42
  std::wcsncpy(logfont.lfFaceName, wfont_name.c_str(), LF_FACESIZE);
44
43
  logfont.lfFaceName[LF_FACESIZE - 1] = 0;
45
44
 
46
- if (HFONT font = CreateFontIndirect(&logfont)) {
45
+ if (HFONT font = CreateFontIndirectW(&logfont)) {
47
46
  if (HDC hdc = GetDC(0)) {
48
- HFONT last_font = (HFONT) SelectObject(hdc, (HGDIOBJ) font);
47
+ HFONT last_font = (HFONT)SelectObject(hdc, (HGDIOBJ)font);
49
48
  auto ttf_buffer_size = GetFontData(hdc, 0, 0, nullptr, 0);
50
49
  if (ttf_buffer_size != GDI_ERROR) {
51
- auto buffer = std::make_shared<Gosu::Buffer>();
52
- buffer->resize(ttf_buffer_size);
50
+ auto buffer = std::make_shared<Gosu::Buffer>(ttf_buffer_size);
53
51
  if (GetFontData(hdc, 0, 0, buffer->data(), buffer->size()) != GDI_ERROR) {
54
- auto data = static_cast<const unsigned char*>(buffer->data());
55
- if (font_name.empty() || TrueTypeFont::matches(data, font_name, font_flags)) {
52
+ if (font_name.empty()
53
+ || TrueTypeFont::matches(buffer->data(), font_name, font_flags)) {
56
54
  buffer_ptr = buffer;
57
- log("Found a matching file (%d bytes)", (int) buffer->size());
58
- }
59
- else {
60
- log("Internal font name did not match; discarding result");
61
55
  }
62
56
  }
63
57
  }
64
- SelectObject(hdc, (HGDIOBJ) last_font);
58
+ SelectObject(hdc, (HGDIOBJ)last_font);
65
59
  ReleaseDC(0, hdc);
66
60
  }
67
- DeleteObject((HGDIOBJ) font);
61
+ DeleteObject((HGDIOBJ)font);
68
62
  }
69
63
 
70
- return buffer_ptr ? static_cast<const unsigned char*>(buffer_ptr->data()) : nullptr;
64
+ return buffer_ptr ? buffer_ptr->data() : nullptr;
71
65
  }
72
66
 
73
- const unsigned char* Gosu::ttf_fallback_data()
67
+ const std::uint8_t* Gosu::ttf_fallback_data()
74
68
  {
75
69
  // Prefer Arial Unicode MS as a fallback because it covers a lot of Unicode.
76
- static const unsigned char* arial_unicode = ttf_data_by_name("Arial Unicode MS", 0);
77
- if (arial_unicode) return arial_unicode;
70
+ static const std::uint8_t* arial_unicode = ttf_data_by_name("Arial Unicode MS", 0);
71
+ if (arial_unicode) {
72
+ return arial_unicode;
73
+ }
78
74
 
79
75
  // Otherwise, just try to find *any* font.
80
- static const unsigned char* any_font = ttf_data_by_name("", 0);
81
- if (any_font) return any_font;
76
+ static const std::uint8_t* any_font = ttf_data_by_name("", 0);
77
+ if (any_font) {
78
+ return any_font;
79
+ }
82
80
 
83
81
  static const char* windir = std::getenv("WINDIR");
84
- if (windir) return ttf_data_from_file(std::string(windir) + "/Fonts/arial.ttf");
82
+ if (windir) {
83
+ return ttf_data_from_file(std::string(windir) + "/Fonts/arial.ttf");
84
+ }
85
85
 
86
86
  return ttf_data_from_file("C:/Windows/Fonts/arial.ttf");
87
87
  }
data/src/Utility.cpp CHANGED
@@ -1,25 +1,22 @@
1
1
  #include <Gosu/Platform.hpp>
2
2
  #include <Gosu/Utility.hpp>
3
-
4
- #include <utf8proc.h>
5
-
6
- #include <cstring>
7
3
  #include <memory>
8
4
  #include <stdexcept>
5
+ #include <utf8proc.h>
9
6
 
10
- std::u32string Gosu::utf8_to_composed_utc4(const std::string& utf8)
7
+ std::u32string Gosu::utf8_to_composed_utc4(std::string_view utf8)
11
8
  {
12
9
  std::u32string utc4;
13
10
  utc4.reserve(utf8.size());
14
11
 
15
12
  const auto* current_byte = reinterpret_cast<const utf8proc_uint8_t*>(utf8.data());
16
- auto remaining_length = utf8.length();
13
+ auto remaining_length = static_cast<utf8proc_ssize_t>(utf8.length());
17
14
 
18
15
  // First convert from UTF-8 to UTC-4.
19
16
  utf8proc_int32_t codepoint;
20
17
  while (remaining_length) {
21
18
  auto bytes_read = utf8proc_iterate(current_byte, remaining_length, &codepoint);
22
- if (bytes_read == -1) {
19
+ if (bytes_read < 0) {
23
20
  // Not looking good, skip this byte and retry.
24
21
  current_byte += 1;
25
22
  remaining_length -= 1;
@@ -33,12 +30,15 @@ std::u32string Gosu::utf8_to_composed_utc4(const std::string& utf8)
33
30
 
34
31
  // Now compose characters in-place.
35
32
  auto* utc4_data = reinterpret_cast<utf8proc_int32_t*>(utc4.data());
33
+ auto utc4_length = static_cast<utf8proc_ssize_t>(utc4.length());
36
34
  auto options = static_cast<utf8proc_option_t>(UTF8PROC_NLF2LF | UTF8PROC_COMPOSE);
37
- auto new_length = utf8proc_normalize_utf32(utc4_data, utc4.length(), options);
35
+ auto new_length = utf8proc_normalize_utf32(utc4_data, utc4_length, options);
36
+ // GCOV_EXCL_START: No code path in utf8proc_normalize_utf32 currently returns an error.
38
37
  if (new_length < 0) {
39
- throw std::runtime_error{"Could not normalize '" + utf8 +
40
- "': " + utf8proc_errmsg(new_length)};
38
+ throw std::runtime_error("Could not normalize '" + std::string(utf8)
39
+ + "': " + utf8proc_errmsg(new_length));
41
40
  }
41
+ // GCOV_EXCL_END
42
42
  utc4.resize(new_length);
43
43
 
44
44
  return utc4;
@@ -46,8 +46,13 @@ std::u32string Gosu::utf8_to_composed_utc4(const std::string& utf8)
46
46
 
47
47
  bool Gosu::has_extension(std::string_view filename, std::string_view extension)
48
48
  {
49
+ /// To avoid extra code paths later, we cut off all leading dots.
50
+ if (extension.starts_with('.')) {
51
+ extension.remove_prefix(1);
52
+ }
53
+
49
54
  std::size_t ext_len = extension.length();
50
- if (ext_len > filename.length()) {
55
+ if (ext_len >= filename.length()) {
51
56
  return false;
52
57
  }
53
58
 
@@ -57,13 +62,14 @@ bool Gosu::has_extension(std::string_view filename, std::string_view extension)
57
62
  --filename_iter;
58
63
  --ext_iter;
59
64
 
60
- if (std::tolower(static_cast<int>(*filename_iter)) !=
61
- std::tolower(static_cast<int>(*ext_iter))) {
65
+ if (std::tolower(static_cast<int>(*filename_iter))
66
+ != std::tolower(static_cast<int>(*ext_iter))) {
62
67
  return false;
63
68
  }
64
69
  }
65
-
66
- return true;
70
+ // We have verified the part after the dot, now make sure that the preceding character is a dot.
71
+ --filename_iter;
72
+ return *filename_iter == '.';
67
73
  }
68
74
 
69
75
  #ifdef GOSU_IS_IPHONE
@@ -72,7 +78,7 @@ bool Gosu::has_extension(std::string_view filename, std::string_view extension)
72
78
 
73
79
  std::vector<std::string> Gosu::user_languages()
74
80
  {
75
- static const std::regex language_regex{"([a-z]{2})-([A-Z]{2})([^A-Z].*)?"};
81
+ static const std::regex language_regex("([a-z]{2})-([A-Z]{2})([^A-Z].*)?");
76
82
 
77
83
  std::vector<std::string> user_languages;
78
84
 
@@ -97,13 +103,17 @@ std::vector<std::string> Gosu::user_languages()
97
103
  {
98
104
  std::vector<std::string> user_languages;
99
105
 
100
- std::unique_ptr<SDL_Locale, decltype(SDL_free)*> locales{SDL_GetPreferredLocales(), SDL_free};
101
- if (!locales) return user_languages;
106
+ std::unique_ptr<SDL_Locale, decltype(SDL_free)*> locales(SDL_GetPreferredLocales(), SDL_free);
107
+ // GCOV_EXCL_START: There is no portable way to let SDL_GetPreferredLocales fail.
108
+ if (!locales) {
109
+ return user_languages;
110
+ }
111
+ // GCOV_EXCL_END
102
112
 
103
113
  for (const SDL_Locale* locale = locales.get(); locale->language != nullptr; ++locale) {
104
114
  std::string language = locale->language;
105
115
  if (locale->country) {
106
- language += "_";
116
+ language += '_';
107
117
  language += locale->country;
108
118
  }
109
119
  user_languages.emplace_back(std::move(language));
@@ -118,13 +128,13 @@ std::vector<std::string> Gosu::user_languages()
118
128
 
119
129
  std::vector<std::string> Gosu::user_languages()
120
130
  {
121
- static const std::regex language_regex{"[a-z]{2}_[A-Z]{2}([^A-Z].*)?"};
131
+ static const std::regex language_regex("[a-z]{2}_[A-Z]{2}([^A-Z].*)?");
122
132
 
123
133
  const char* locale = std::getenv("LANG");
124
134
 
125
135
  if (locale && std::regex_match(locale, language_regex)) {
126
136
  // Trim off anything after the language code.
127
- return {std::string{locale, locale + 5}};
137
+ return { std::string(locale, locale + 5) };
128
138
  }
129
139
 
130
140
  return {};
@@ -132,3 +142,56 @@ std::vector<std::string> Gosu::user_languages()
132
142
 
133
143
  #endif
134
144
  #endif
145
+
146
+ bool Gosu::Rect::overlaps(const Rect& other) const
147
+ {
148
+ return x < other.right() && other.x < right() && y < other.bottom() && other.y < bottom();
149
+ }
150
+
151
+ bool Gosu::Rect::contains(const Rect& other) const
152
+ {
153
+ return x <= other.x && other.right() <= right() && y <= other.y && other.bottom() <= bottom();
154
+ }
155
+
156
+ void Gosu::Rect::clip_to(const Rect& bounding_box, int* adjust_x, int* adjust_y)
157
+ {
158
+ // Cut off the area on the left that exceeds the bounding box, if any.
159
+ const int extra_pixels_left = bounding_box.x - x;
160
+ if (extra_pixels_left > 0) {
161
+ x += extra_pixels_left;
162
+ width -= extra_pixels_left;
163
+ if (adjust_x) {
164
+ *adjust_x += extra_pixels_left;
165
+ }
166
+ }
167
+
168
+ // Cut off the area on the right that exceeds the bounding box, if any.
169
+ const int extra_pixels_right = right() - bounding_box.right();
170
+ if (extra_pixels_right > 0) {
171
+ width -= extra_pixels_right;
172
+ }
173
+
174
+ // Cut off the area at the top that exceeds the bounding box, if any.
175
+ const int extra_pixels_top = bounding_box.y - y;
176
+ if (extra_pixels_top > 0) {
177
+ y += extra_pixels_top;
178
+ height -= extra_pixels_top;
179
+ if (adjust_y) {
180
+ *adjust_y += extra_pixels_top;
181
+ }
182
+ }
183
+
184
+ // Cut off the area at the bottom that exceeds the bounding box, if any.
185
+ const int extra_pixels_bottom = bottom() - bounding_box.bottom();
186
+ if (extra_pixels_bottom > 0) {
187
+ height -= extra_pixels_bottom;
188
+ }
189
+
190
+ // Leave 0 instead of negative values for width/height when the rect was clipped away.
191
+ if (width < 0) {
192
+ width = 0;
193
+ }
194
+ if (height < 0) {
195
+ height = 0;
196
+ }
197
+ }
@@ -0,0 +1,45 @@
1
+ #include <Gosu/Platform.hpp>
2
+ #if defined(GOSU_IS_WIN)
3
+
4
+ #include <Gosu/Utility.hpp>
5
+ #include <stdexcept>
6
+ #include <windows.h>
7
+
8
+ std::wstring Gosu::utf8_to_utf16(const std::string& utf8)
9
+ {
10
+ if (utf8.empty()) {
11
+ return std::wstring();
12
+ }
13
+
14
+ // The UTF-16 result can never consist of more codepoints than we have bytes in UTF-8.
15
+ // -> Start with a size that we know is sufficient, then truncate later.
16
+ std::wstring utf16(utf8.size(), '\0');
17
+ int result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.data(), utf8.size(),
18
+ utf16.data(), utf16.size());
19
+ if (result == 0) {
20
+ throw std::runtime_error("Could not convert UTF-8 to UTF-16, GetLastError() = "
21
+ + std::to_string(GetLastError()));
22
+ }
23
+ utf16.resize(result);
24
+ return utf16;
25
+ }
26
+
27
+ std::string Gosu::utf16_to_utf8(const std::wstring& utf16)
28
+ {
29
+ if (utf16.empty()) {
30
+ return std::string();
31
+ }
32
+
33
+ int result = WideCharToMultiByte(CP_UTF8, 0, utf16.c_str(), utf16.size(), nullptr, 0, nullptr,
34
+ nullptr);
35
+ if (result == 0) {
36
+ throw std::runtime_error("Could not convert UTF-16 to UTF-8, GetLastError() = "
37
+ + std::to_string(GetLastError()));
38
+ }
39
+ std::string utf8(result, '\0');
40
+ WideCharToMultiByte(CP_UTF8, MB_ERR_INVALID_CHARS, utf16.c_str(), utf16.size(), utf8.data(),
41
+ utf8.size(), nullptr, nullptr);
42
+ return utf8;
43
+ }
44
+
45
+ #endif