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