gosu 0.13.3 → 0.14.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
}
|