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.
- checksums.yaml +4 -4
- data/Gosu/Audio.hpp +15 -11
- data/Gosu/Font.hpp +24 -20
- data/Gosu/Fwd.hpp +1 -1
- data/Gosu/Graphics.hpp +8 -9
- data/Gosu/ImageData.hpp +1 -1
- data/Gosu/Input.hpp +1 -1
- data/Gosu/Math.hpp +0 -18
- data/Gosu/Text.hpp +22 -30
- data/Gosu/TextInput.hpp +13 -0
- data/Gosu/Utility.hpp +2 -0
- data/Gosu/Window.hpp +3 -3
- data/README.md +3 -4
- data/ext/gosu/extconf.rb +7 -9
- data/lib/gosu/swig_patches.rb +1 -4
- data/rdoc/gosu.rb +34 -9
- data/src/Audio.cpp +6 -6
- data/src/AudioImpl.cpp +2 -2
- data/src/Bitmap.cpp +1 -2
- data/src/BitmapIO.cpp +21 -2
- data/src/BlockAllocator.cpp +1 -1
- data/src/Channel.cpp +7 -1
- data/src/ClipRectStack.hpp +4 -1
- data/src/Color.cpp +2 -1
- data/src/DirectoriesWin.cpp +1 -1
- data/src/DrawOp.hpp +8 -4
- data/src/DrawOpQueue.hpp +13 -24
- data/src/FileUnix.cpp +3 -1
- data/src/Font.cpp +92 -96
- data/src/GosuGLView.cpp +59 -31
- data/src/GosuGLView.hpp +14 -0
- data/src/GosuViewController.cpp +21 -21
- data/src/{GosuViewController.h → GosuViewController.hpp} +2 -4
- data/src/Graphics.cpp +71 -38
- data/src/GraphicsImpl.hpp +12 -29
- data/src/Image.cpp +5 -7
- data/src/Input.cpp +7 -5
- data/src/InputUIKit.cpp +19 -37
- data/src/Macro.cpp +10 -2
- data/src/MarkupParser.cpp +241 -0
- data/src/MarkupParser.hpp +61 -0
- data/src/Math.cpp +1 -1
- data/src/OffScreenTarget.cpp +99 -0
- data/src/OffScreenTarget.hpp +23 -0
- data/src/OggFile.hpp +10 -0
- data/src/RenderState.hpp +0 -2
- data/src/Resolution.cpp +2 -2
- data/src/RubyGosu.cxx +457 -244
- data/src/TexChunk.cpp +8 -6
- data/src/Text.cpp +58 -345
- data/src/TextBuilder.cpp +138 -0
- data/src/TextBuilder.hpp +55 -0
- data/src/TextInput.cpp +27 -10
- data/src/Texture.cpp +22 -17
- data/src/Texture.hpp +19 -20
- data/src/TimingApple.cpp +5 -7
- data/src/TimingUnix.cpp +1 -4
- data/src/TimingWin.cpp +4 -1
- data/src/TrueTypeFont.cpp +282 -0
- data/src/TrueTypeFont.hpp +66 -0
- data/src/TrueTypeFontApple.cpp +65 -0
- data/src/TrueTypeFontUnix.cpp +91 -0
- data/src/TrueTypeFontWin.cpp +82 -0
- data/src/Utility.cpp +40 -0
- data/src/Window.cpp +7 -6
- data/src/WindowUIKit.cpp +9 -4
- data/src/stb_truetype.h +4589 -0
- data/src/utf8proc.c +755 -0
- data/src/utf8proc.h +699 -0
- data/src/utf8proc_data.h +14386 -0
- metadata +23 -16
- data/src/FormattedString.cpp +0 -237
- data/src/FormattedString.hpp +0 -47
- data/src/GosuAppDelegate.cpp +0 -30
- data/src/GosuAppDelegate.h +0 -8
- data/src/GosuGLView.h +0 -8
- data/src/TextApple.cpp +0 -212
- data/src/TextTTFWin.cpp +0 -197
- data/src/TextUnix.cpp +0 -280
- data/src/TextWin.cpp +0 -191
data/src/TextBuilder.cpp
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#include "TextBuilder.hpp"
|
2
|
+
#include <Gosu/Text.hpp>
|
3
|
+
#include <cassert>
|
4
|
+
#include <cmath>
|
5
|
+
#include "utf8proc.h"
|
6
|
+
using namespace std;
|
7
|
+
|
8
|
+
Gosu::WordInfo::WordInfo(const string& font_name, double font_height, vector<FormattedString> parts)
|
9
|
+
{
|
10
|
+
assert (! parts.empty());
|
11
|
+
|
12
|
+
auto* properties = utf8proc_get_property(parts.front().text.front());
|
13
|
+
|
14
|
+
// Also check the BiDi class to filter out non-breaking spaces.
|
15
|
+
is_whitespace = properties->category == UTF8PROC_CATEGORY_ZS &&
|
16
|
+
properties->bidi_class == UTF8PROC_BIDI_CLASS_WS;
|
17
|
+
|
18
|
+
is_end_of_line = parts.back().text.back() == '\n';
|
19
|
+
// Remove the trailing backspace character to avoid errors from Gosu::text_width().
|
20
|
+
if (is_end_of_line) parts.back().text.pop_back();
|
21
|
+
|
22
|
+
width = 0;
|
23
|
+
for (const auto& part : parts) {
|
24
|
+
assert (! part.text.empty());
|
25
|
+
|
26
|
+
width += text_width(part.text, font_name, font_height, part.flags);
|
27
|
+
}
|
28
|
+
|
29
|
+
this->parts = move(parts);
|
30
|
+
}
|
31
|
+
|
32
|
+
void Gosu::TextBuilder::flush_current_line(EndOfLineReason reason)
|
33
|
+
{
|
34
|
+
if (current_line.empty()) {
|
35
|
+
if (reason == END_OF_PARAGRAPH) allocate_next_line();
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
|
39
|
+
allocate_next_line();
|
40
|
+
|
41
|
+
// Remove trailing whitespace so that justifying the text across the line works.
|
42
|
+
if (current_line.back().is_whitespace) current_line.pop_back();
|
43
|
+
|
44
|
+
// Shouldn't happen because the first word on a line should never be whitespace.
|
45
|
+
assert (! current_line.empty());
|
46
|
+
|
47
|
+
double words_width = 0, whitespace_width = 0;
|
48
|
+
for (const auto& word : current_line) {
|
49
|
+
(word.is_whitespace ? whitespace_width : words_width) += word.width;
|
50
|
+
}
|
51
|
+
|
52
|
+
double x = 0;
|
53
|
+
if (align == AL_RIGHT) {
|
54
|
+
x = result.width() - words_width - whitespace_width;
|
55
|
+
}
|
56
|
+
else if (align == AL_CENTER) {
|
57
|
+
x = (result.width() - words_width - whitespace_width) / 2.0;
|
58
|
+
}
|
59
|
+
|
60
|
+
double whitespace_factor = 1.0;
|
61
|
+
if (align == AL_JUSTIFY && reason == LINE_TOO_LONG) {
|
62
|
+
whitespace_factor = (result.width() - words_width) / whitespace_width;
|
63
|
+
}
|
64
|
+
|
65
|
+
double y = (used_lines - 1) * (font_height + line_spacing);
|
66
|
+
|
67
|
+
for (const auto& word : current_line) {
|
68
|
+
if (word.is_whitespace) {
|
69
|
+
x += word.width * whitespace_factor;
|
70
|
+
}
|
71
|
+
else {
|
72
|
+
for (const auto& part : word.parts) {
|
73
|
+
draw_text(result, x, y, part.color, part.text, font_name, font_height, part.flags);
|
74
|
+
}
|
75
|
+
x += word.width;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
current_line.clear();
|
80
|
+
current_line_width = 0;
|
81
|
+
}
|
82
|
+
|
83
|
+
void Gosu::TextBuilder::allocate_next_line()
|
84
|
+
{
|
85
|
+
if (used_lines == allocated_lines) {
|
86
|
+
allocated_lines += 10;
|
87
|
+
resize_to_allocated_lines();
|
88
|
+
}
|
89
|
+
|
90
|
+
++used_lines;
|
91
|
+
}
|
92
|
+
|
93
|
+
void Gosu::TextBuilder::resize_to_allocated_lines()
|
94
|
+
{
|
95
|
+
double new_height = font_height * allocated_lines + line_spacing * max(0, allocated_lines - 1);
|
96
|
+
result.resize(result.width(), ceil(new_height));
|
97
|
+
}
|
98
|
+
|
99
|
+
Gosu::TextBuilder::TextBuilder(const string& font_name, int font_height, int line_spacing,
|
100
|
+
int width, Alignment align)
|
101
|
+
: font_name(font_name), font_height(font_height), line_spacing(line_spacing), align(align)
|
102
|
+
{
|
103
|
+
// This class uses result.width() to remember its destination width, so store it in there.
|
104
|
+
result.resize(width, 0);
|
105
|
+
}
|
106
|
+
|
107
|
+
Gosu::Bitmap Gosu::TextBuilder::move_into_bitmap()
|
108
|
+
{
|
109
|
+
flush_current_line(END_OF_TEXT);
|
110
|
+
|
111
|
+
// Shrink to fit the currently used height.
|
112
|
+
allocated_lines = used_lines;
|
113
|
+
resize_to_allocated_lines();
|
114
|
+
return move(result);
|
115
|
+
}
|
116
|
+
|
117
|
+
void Gosu::TextBuilder::feed_word(vector<FormattedString>&& word)
|
118
|
+
{
|
119
|
+
WordInfo new_word(font_name, font_height, word);
|
120
|
+
|
121
|
+
if (current_line_width + new_word.width > result.width()) {
|
122
|
+
// Can't fit it on the same line as before, so flush the last line before adding the word to
|
123
|
+
// the next line.
|
124
|
+
flush_current_line(LINE_TOO_LONG);
|
125
|
+
|
126
|
+
if (new_word.is_whitespace) {
|
127
|
+
// Do not wrap trailing whitespace onto the start of the next line - discard this word.
|
128
|
+
return;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
current_line.emplace_back(new_word);
|
133
|
+
current_line_width += new_word.width;
|
134
|
+
|
135
|
+
if (current_line_width > result.width() || new_word.is_end_of_line) {
|
136
|
+
flush_current_line(new_word.is_end_of_line ? END_OF_PARAGRAPH : LINE_TOO_LONG);
|
137
|
+
}
|
138
|
+
}
|
data/src/TextBuilder.hpp
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "GraphicsImpl.hpp"
|
4
|
+
#include "MarkupParser.hpp"
|
5
|
+
#include <vector>
|
6
|
+
|
7
|
+
namespace Gosu
|
8
|
+
{
|
9
|
+
struct WordInfo
|
10
|
+
{
|
11
|
+
std::vector<FormattedString> parts;
|
12
|
+
bool is_whitespace;
|
13
|
+
bool is_end_of_line;
|
14
|
+
double width;
|
15
|
+
|
16
|
+
WordInfo(const std::string& font_name, double font_height,
|
17
|
+
std::vector<FormattedString> parts);
|
18
|
+
};
|
19
|
+
|
20
|
+
class TextBuilder
|
21
|
+
{
|
22
|
+
// Parameters.
|
23
|
+
std::string font_name;
|
24
|
+
double font_height;
|
25
|
+
double line_spacing;
|
26
|
+
Alignment align;
|
27
|
+
|
28
|
+
enum EndOfLineReason {
|
29
|
+
LINE_TOO_LONG,
|
30
|
+
END_OF_PARAGRAPH,
|
31
|
+
END_OF_TEXT
|
32
|
+
};
|
33
|
+
|
34
|
+
// Input.
|
35
|
+
std::vector<WordInfo> current_line;
|
36
|
+
int current_line_width = 0;
|
37
|
+
void flush_current_line(EndOfLineReason reason);
|
38
|
+
|
39
|
+
// Output.
|
40
|
+
Bitmap result;
|
41
|
+
int used_lines = 0;
|
42
|
+
int allocated_lines = 0;
|
43
|
+
void allocate_next_line();
|
44
|
+
void resize_to_allocated_lines();
|
45
|
+
|
46
|
+
public:
|
47
|
+
TextBuilder(const std::string& font_name, int font_height, int line_spacing,
|
48
|
+
int width, Alignment align);
|
49
|
+
|
50
|
+
void feed_word(std::vector<FormattedString>&& word);
|
51
|
+
|
52
|
+
Bitmap move_into_bitmap();
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
data/src/TextInput.cpp
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
#include <Gosu/Platform.hpp>
|
2
|
-
#if !defined(GOSU_IS_IPHONE)
|
3
|
-
|
4
1
|
#include <Gosu/TextInput.hpp>
|
5
2
|
#include <Gosu/Input.hpp>
|
6
3
|
#include <Gosu/Platform.hpp>
|
4
|
+
|
5
|
+
#ifndef GOSU_IS_IPHONE
|
7
6
|
#include <SDL.h>
|
8
7
|
#include <cctype>
|
8
|
+
#endif
|
9
|
+
|
9
10
|
using namespace std;
|
10
11
|
|
11
12
|
struct Gosu::TextInput::Impl
|
12
13
|
{
|
13
14
|
string text;
|
14
15
|
|
15
|
-
// This is the current IME composition.
|
16
|
+
// This is the current IME composition (not used on iOS).
|
16
17
|
// http://wiki.libsdl.org/Tutorials/TextInput#CandidateList
|
17
18
|
string composition;
|
18
19
|
|
@@ -152,6 +153,7 @@ Gosu::TextInput::TextInput()
|
|
152
153
|
|
153
154
|
Gosu::TextInput::~TextInput()
|
154
155
|
{
|
156
|
+
// TODO: Unset Input::text_input to avoid stale pointers?
|
155
157
|
}
|
156
158
|
|
157
159
|
string Gosu::TextInput::text() const
|
@@ -172,12 +174,12 @@ void Gosu::TextInput::set_text(const string& text)
|
|
172
174
|
|
173
175
|
unsigned Gosu::TextInput::caret_pos() const
|
174
176
|
{
|
175
|
-
return
|
177
|
+
return pimpl->caret_pos;
|
176
178
|
}
|
177
179
|
|
178
|
-
void Gosu::TextInput::set_caret_pos(unsigned
|
180
|
+
void Gosu::TextInput::set_caret_pos(unsigned caret_pos)
|
179
181
|
{
|
180
|
-
pimpl->caret_pos =
|
182
|
+
pimpl->caret_pos = caret_pos;
|
181
183
|
}
|
182
184
|
|
183
185
|
unsigned Gosu::TextInput::selection_start() const
|
@@ -185,11 +187,12 @@ unsigned Gosu::TextInput::selection_start() const
|
|
185
187
|
return pimpl->selection_start;
|
186
188
|
}
|
187
189
|
|
188
|
-
void Gosu::TextInput::set_selection_start(unsigned
|
190
|
+
void Gosu::TextInput::set_selection_start(unsigned selection_start)
|
189
191
|
{
|
190
|
-
pimpl->selection_start =
|
192
|
+
pimpl->selection_start = selection_start;
|
191
193
|
}
|
192
194
|
|
195
|
+
#ifndef GOSU_IS_IPHONE
|
193
196
|
bool Gosu::TextInput::feed_sdl_event(void* event)
|
194
197
|
{
|
195
198
|
const SDL_Event* e = static_cast<SDL_Event*>(event);
|
@@ -275,5 +278,19 @@ bool Gosu::TextInput::feed_sdl_event(void* event)
|
|
275
278
|
|
276
279
|
return false;
|
277
280
|
}
|
278
|
-
|
279
281
|
#endif
|
282
|
+
|
283
|
+
void Gosu::TextInput::insert_text(string text)
|
284
|
+
{
|
285
|
+
pimpl->insert_text(text);
|
286
|
+
}
|
287
|
+
|
288
|
+
void Gosu::TextInput::delete_forward()
|
289
|
+
{
|
290
|
+
pimpl->delete_forward();
|
291
|
+
}
|
292
|
+
|
293
|
+
void Gosu::TextInput::delete_backward()
|
294
|
+
{
|
295
|
+
pimpl->delete_backward();
|
296
|
+
}
|
data/src/Texture.cpp
CHANGED
@@ -11,16 +11,14 @@ namespace Gosu
|
|
11
11
|
bool undocumented_retrofication = false;
|
12
12
|
}
|
13
13
|
|
14
|
-
Gosu::Texture::Texture(unsigned
|
15
|
-
: allocator_(
|
14
|
+
Gosu::Texture::Texture(unsigned width, unsigned height, bool retro)
|
15
|
+
: allocator_(width, height), retro_(retro)
|
16
16
|
{
|
17
17
|
ensure_current_context();
|
18
18
|
|
19
19
|
// Create texture name.
|
20
20
|
glGenTextures(1, &tex_name_);
|
21
|
-
if (tex_name_ == static_cast<GLuint>(-1))
|
22
|
-
throw runtime_error("Couldn't create OpenGL texture");
|
23
|
-
}
|
21
|
+
if (tex_name_ == static_cast<GLuint>(-1)) throw runtime_error("Couldn't create OpenGL texture");
|
24
22
|
|
25
23
|
// Create empty texture.
|
26
24
|
glBindTexture(GL_TEXTURE_2D, tex_name_);
|
@@ -57,9 +55,14 @@ Gosu::Texture::~Texture()
|
|
57
55
|
glDeleteTextures(1, &tex_name_);
|
58
56
|
}
|
59
57
|
|
60
|
-
unsigned Gosu::Texture::
|
58
|
+
unsigned Gosu::Texture::width() const
|
59
|
+
{
|
60
|
+
return allocator_.width();
|
61
|
+
}
|
62
|
+
|
63
|
+
unsigned Gosu::Texture::height() const
|
61
64
|
{
|
62
|
-
return allocator_.
|
65
|
+
return allocator_.height();
|
63
66
|
}
|
64
67
|
|
65
68
|
GLuint Gosu::Texture::tex_name() const
|
@@ -72,19 +75,18 @@ bool Gosu::Texture::retro() const
|
|
72
75
|
return retro_;
|
73
76
|
}
|
74
77
|
|
75
|
-
unique_ptr<Gosu::TexChunk> Gosu::Texture::try_alloc(
|
76
|
-
unsigned padding)
|
78
|
+
unique_ptr<Gosu::TexChunk> Gosu::Texture::try_alloc(const Bitmap& bmp, unsigned padding)
|
77
79
|
{
|
78
80
|
BlockAllocator::Block block;
|
79
81
|
|
80
82
|
if (!allocator_.alloc(bmp.width(), bmp.height(), block)) return nullptr;
|
81
83
|
|
82
|
-
unique_ptr<
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
unique_ptr<TexChunk> result(new TexChunk(shared_from_this(),
|
85
|
+
block.left + padding,
|
86
|
+
block.top + padding,
|
87
|
+
block.width - 2 * padding,
|
88
|
+
block.height - 2 * padding,
|
89
|
+
padding));
|
88
90
|
|
89
91
|
ensure_current_context();
|
90
92
|
|
@@ -108,14 +110,17 @@ void Gosu::Texture::free(unsigned x, unsigned y, unsigned width, unsigned height
|
|
108
110
|
Gosu::Bitmap Gosu::Texture::to_bitmap(unsigned x, unsigned y, unsigned width, unsigned height) const
|
109
111
|
{
|
110
112
|
#ifdef GOSU_IS_OPENGLES
|
113
|
+
// See here for one possible implementation: https://github.com/apitrace/apitrace/issues/70
|
114
|
+
// (Could reuse a lot of code from OffScreenTarget)
|
111
115
|
throw logic_error("Texture::to_bitmap not supported on iOS");
|
112
116
|
#else
|
113
117
|
ensure_current_context();
|
114
118
|
|
115
|
-
|
119
|
+
Bitmap full_texture(this->width(), this->height());
|
116
120
|
glBindTexture(GL_TEXTURE_2D, tex_name());
|
121
|
+
// TODO: There are ways to retrieve only part of a texture, which we should use sooner or later.
|
117
122
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, full_texture.data());
|
118
|
-
|
123
|
+
Bitmap bitmap(width, height);
|
119
124
|
bitmap.insert(full_texture, -int(x), -int(y));
|
120
125
|
|
121
126
|
return bitmap;
|
data/src/Texture.hpp
CHANGED
@@ -5,26 +5,25 @@
|
|
5
5
|
#include "TexChunk.hpp"
|
6
6
|
#include <Gosu/Fwd.hpp>
|
7
7
|
#include <Gosu/Bitmap.hpp>
|
8
|
+
#include <memory>
|
8
9
|
#include <vector>
|
9
10
|
|
10
|
-
|
11
|
+
class Gosu::Texture : public std::enable_shared_from_this<Texture>
|
11
12
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
};
|
30
|
-
}
|
13
|
+
// BlockAllocator can't be copied or moved, so neither can Texture.
|
14
|
+
BlockAllocator allocator_;
|
15
|
+
GLuint tex_name_;
|
16
|
+
bool retro_;
|
17
|
+
|
18
|
+
public:
|
19
|
+
Texture(unsigned width, unsigned height, bool retro);
|
20
|
+
~Texture();
|
21
|
+
unsigned width() const;
|
22
|
+
unsigned height() const;
|
23
|
+
GLuint tex_name() const;
|
24
|
+
bool retro() const;
|
25
|
+
std::unique_ptr<TexChunk> try_alloc(const Bitmap& bmp, unsigned padding);
|
26
|
+
void block(unsigned x, unsigned y, unsigned width, unsigned height);
|
27
|
+
void free(unsigned x, unsigned y, unsigned width, unsigned height);
|
28
|
+
Bitmap to_bitmap(unsigned x, unsigned y, unsigned width, unsigned height) const;
|
29
|
+
};
|
data/src/TimingApple.cpp
CHANGED
@@ -16,16 +16,14 @@ void Gosu::sleep(unsigned milliseconds)
|
|
16
16
|
|
17
17
|
unsigned long Gosu::milliseconds()
|
18
18
|
{
|
19
|
-
static uint64_t first_tick = 0;
|
20
19
|
static mach_timebase_info_data_t info;
|
21
|
-
|
22
|
-
if (first_tick == 0) {
|
20
|
+
static uint64_t first_tick = [] {
|
23
21
|
mach_timebase_info(&info);
|
24
|
-
|
25
|
-
}
|
26
|
-
|
22
|
+
return mach_absolute_time();
|
23
|
+
}();
|
24
|
+
|
27
25
|
uint64_t runtime = mach_absolute_time() - first_tick;
|
28
|
-
return runtime * info.numer / info.denom / 1000000
|
26
|
+
return runtime * info.numer / info.denom / 1000000;
|
29
27
|
}
|
30
28
|
|
31
29
|
#endif
|
data/src/TimingUnix.cpp
CHANGED
@@ -21,10 +21,7 @@ unsigned long Gosu::milliseconds()
|
|
21
21
|
start = tp.tv_usec / 1000UL + tp.tv_sec * 1000UL;
|
22
22
|
}
|
23
23
|
|
24
|
-
|
25
|
-
// have a happy GC on 32-bit systems.
|
26
|
-
// No, don't ask why this is an unsigned long then :)
|
27
|
-
return (tp.tv_usec / 1000UL + tp.tv_sec * 1000UL - start) & 0x1fffffff;
|
24
|
+
return tp.tv_usec / 1000UL + tp.tv_sec * 1000UL - start;
|
28
25
|
}
|
29
26
|
|
30
27
|
#endif
|
data/src/TimingWin.cpp
CHANGED
@@ -12,7 +12,10 @@ void Gosu::sleep(unsigned milliseconds)
|
|
12
12
|
|
13
13
|
unsigned long Gosu::milliseconds()
|
14
14
|
{
|
15
|
-
static unsigned long start =
|
15
|
+
static unsigned long start = [] {
|
16
|
+
timeBeginPeriod(1);
|
17
|
+
return timeGetTime();
|
18
|
+
}();
|
16
19
|
return timeGetTime() - start;
|
17
20
|
}
|
18
21
|
|