gosu 0.7.10.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.txt +29 -0
- data/Gosu/Async.hpp +48 -0
- data/Gosu/Audio.hpp +145 -0
- data/Gosu/AutoLink.hpp +16 -0
- data/Gosu/Bitmap.hpp +85 -0
- data/Gosu/ButtonsMac.hpp +114 -0
- data/Gosu/ButtonsWin.hpp +111 -0
- data/Gosu/ButtonsX.hpp +115 -0
- data/Gosu/Color.hpp +172 -0
- data/Gosu/Directories.hpp +36 -0
- data/Gosu/Font.hpp +59 -0
- data/Gosu/Fwd.hpp +31 -0
- data/Gosu/Gosu.hpp +26 -0
- data/Gosu/Graphics.hpp +86 -0
- data/Gosu/GraphicsBase.hpp +45 -0
- data/Gosu/IO.hpp +255 -0
- data/Gosu/Image.hpp +148 -0
- data/Gosu/ImageData.hpp +45 -0
- data/Gosu/Input.hpp +116 -0
- data/Gosu/Math.hpp +95 -0
- data/Gosu/Platform.hpp +61 -0
- data/Gosu/RotFlip.hpp +116 -0
- data/Gosu/Sockets.hpp +129 -0
- data/Gosu/Text.hpp +47 -0
- data/Gosu/TextInput.hpp +57 -0
- data/Gosu/Timing.hpp +16 -0
- data/Gosu/Utility.hpp +24 -0
- data/Gosu/WinUtility.hpp +76 -0
- data/Gosu/Window.hpp +84 -0
- data/GosuImpl/Async.cpp +37 -0
- data/GosuImpl/AudioFmod.cpp +417 -0
- data/GosuImpl/AudioSDL.cpp +255 -0
- data/GosuImpl/DirectoriesMac.mm +38 -0
- data/GosuImpl/DirectoriesUnix.cpp +48 -0
- data/GosuImpl/DirectoriesWin.cpp +42 -0
- data/GosuImpl/FileUnix.cpp +100 -0
- data/GosuImpl/FileWin.cpp +83 -0
- data/GosuImpl/Graphics/Bitmap.cpp +116 -0
- data/GosuImpl/Graphics/BitmapBMP.cpp +232 -0
- data/GosuImpl/Graphics/BitmapColorKey.cpp +39 -0
- data/GosuImpl/Graphics/BitmapPNG.cpp +276 -0
- data/GosuImpl/Graphics/BitmapUtils.cpp +67 -0
- data/GosuImpl/Graphics/BlockAllocator.cpp +127 -0
- data/GosuImpl/Graphics/BlockAllocator.hpp +34 -0
- data/GosuImpl/Graphics/Color.cpp +126 -0
- data/GosuImpl/Graphics/Common.hpp +21 -0
- data/GosuImpl/Graphics/DrawOp.hpp +154 -0
- data/GosuImpl/Graphics/Font.cpp +110 -0
- data/GosuImpl/Graphics/Graphics.cpp +295 -0
- data/GosuImpl/Graphics/Image.cpp +159 -0
- data/GosuImpl/Graphics/LargeImageData.cpp +115 -0
- data/GosuImpl/Graphics/LargeImageData.hpp +37 -0
- data/GosuImpl/Graphics/RotFlip.cpp +184 -0
- data/GosuImpl/Graphics/TexChunk.cpp +77 -0
- data/GosuImpl/Graphics/TexChunk.hpp +40 -0
- data/GosuImpl/Graphics/Text.cpp +223 -0
- data/GosuImpl/Graphics/TextMac.cpp +242 -0
- data/GosuImpl/Graphics/TextPangoFT.cpp +186 -0
- data/GosuImpl/Graphics/TextWin.cpp +172 -0
- data/GosuImpl/Graphics/Texture.cpp +104 -0
- data/GosuImpl/Graphics/Texture.hpp +34 -0
- data/GosuImpl/IO.cpp +48 -0
- data/GosuImpl/InputMac.mm +677 -0
- data/GosuImpl/InputWin.cpp +444 -0
- data/GosuImpl/InputX.cpp +158 -0
- data/GosuImpl/MacUtility.hpp +48 -0
- data/GosuImpl/Math.cpp +49 -0
- data/GosuImpl/RubyGosu.swg +474 -0
- data/GosuImpl/RubyGosuStub.mm +17 -0
- data/GosuImpl/RubyGosu_DllMain.cxx +30 -0
- data/GosuImpl/RubyGosu_wrap.cxx +8521 -0
- data/GosuImpl/RubyGosu_wrap.h +31 -0
- data/GosuImpl/Sockets/CommSocket.cpp +304 -0
- data/GosuImpl/Sockets/ListenerSocket.cpp +60 -0
- data/GosuImpl/Sockets/MessageSocket.cpp +136 -0
- data/GosuImpl/Sockets/Socket.cpp +145 -0
- data/GosuImpl/Sockets/Sockets.hpp +66 -0
- data/GosuImpl/TextInputMac.mm +207 -0
- data/GosuImpl/TextInputWin.cpp +197 -0
- data/GosuImpl/TextInputX.cpp +201 -0
- data/GosuImpl/TextTTFWin.cpp +247 -0
- data/GosuImpl/TimingUnix.cpp +17 -0
- data/GosuImpl/TimingWin.cpp +28 -0
- data/GosuImpl/Utility.cpp +140 -0
- data/GosuImpl/WinMain.cpp +69 -0
- data/GosuImpl/WinUtility.cpp +137 -0
- data/GosuImpl/WindowMac.mm +466 -0
- data/GosuImpl/WindowWin.cpp +447 -0
- data/GosuImpl/WindowX.cpp +392 -0
- data/GosuImpl/X11vroot.h +118 -0
- data/README.txt +13 -0
- data/Rakefile +178 -0
- data/examples/ChipmunkIntegration.rb +275 -0
- data/examples/CptnRuby.rb +231 -0
- data/examples/MoreChipmunkAndRMagick.rb +155 -0
- data/examples/OpenGLIntegration.rb +232 -0
- data/examples/RMagickIntegration.rb +449 -0
- data/examples/TextInput.cpp +170 -0
- data/examples/TextInput.rb +139 -0
- data/examples/Tutorial.cpp +215 -0
- data/examples/Tutorial.rb +137 -0
- data/examples/media/Beep.wav +0 -0
- data/examples/media/CptnRuby Gem.png +0 -0
- data/examples/media/CptnRuby Map.txt +25 -0
- data/examples/media/CptnRuby Tileset.png +0 -0
- data/examples/media/CptnRuby.png +0 -0
- data/examples/media/Cursor.png +0 -0
- data/examples/media/Earth.png +0 -0
- data/examples/media/Explosion.wav +0 -0
- data/examples/media/LargeStar.png +0 -0
- data/examples/media/Sky.jpg +0 -0
- data/examples/media/Smoke.png +0 -0
- data/examples/media/Soldier.png +0 -0
- data/examples/media/Space.png +0 -0
- data/examples/media/Star.png +0 -0
- data/examples/media/Starfighter.bmp +0 -0
- data/linux/Makefile.in +98 -0
- data/linux/configure +5658 -0
- data/linux/configure.ac +126 -0
- data/linux/extconf.rb +11 -0
- data/mac/English.lproj/InfoPlist.strings +0 -0
- data/mac/Gosu-Info.plist +26 -0
- data/mac/Gosu.xcodeproj/project.pbxproj +1194 -0
- data/mac/RubyGosu Template-Info.plist +26 -0
- data/mac/libboost_thread_1_34_1_universal.a +0 -0
- data/mac/libboost_thread_d_1_34_1_universal.a +0 -0
- data/mac/libfmod_universal.a +0 -0
- data/mac/libpng_universal.a +0 -0
- data/mac/libz_universal.a +0 -0
- data/reference/Async_8hpp-source.html +70 -0
- data/reference/Audio_8hpp-source.html +114 -0
- data/reference/Audio_8hpp.html +50 -0
- data/reference/AutoLink_8hpp-source.html +38 -0
- data/reference/AutoLink_8hpp.html +34 -0
- data/reference/Bitmap_8hpp-source.html +85 -0
- data/reference/Bitmap_8hpp.html +58 -0
- data/reference/ButtonsMac_8hpp-source.html +133 -0
- data/reference/ButtonsWin_8hpp-source.html +133 -0
- data/reference/ButtonsX_8hpp-source.html +134 -0
- data/reference/Color_8hpp-source.html +169 -0
- data/reference/Color_8hpp.html +85 -0
- data/reference/Directories_8hpp-source.html +42 -0
- data/reference/Directories_8hpp.html +46 -0
- data/reference/Font_8hpp-source.html +65 -0
- data/reference/Font_8hpp.html +41 -0
- data/reference/Fwd_8hpp-source.html +52 -0
- data/reference/Fwd_8hpp.html +37 -0
- data/reference/Gosu_8hpp-source.html +48 -0
- data/reference/Gosu_8hpp.html +34 -0
- data/reference/GraphicsBase_8hpp-source.html +57 -0
- data/reference/GraphicsBase_8hpp.html +56 -0
- data/reference/Graphics_8hpp-source.html +96 -0
- data/reference/Graphics_8hpp.html +53 -0
- data/reference/IO_8hpp-source.html +255 -0
- data/reference/IO_8hpp.html +74 -0
- data/reference/ImageData_8hpp-source.html +62 -0
- data/reference/ImageData_8hpp.html +43 -0
- data/reference/Image_8hpp-source.html +126 -0
- data/reference/Image_8hpp.html +48 -0
- data/reference/Input_8hpp-source.html +118 -0
- data/reference/Input_8hpp.html +50 -0
- data/reference/Math_8hpp-source.html +92 -0
- data/reference/Math_8hpp.html +74 -0
- data/reference/Platform_8hpp-source.html +83 -0
- data/reference/Platform_8hpp.html +73 -0
- data/reference/RotFlip_8hpp-source.html +138 -0
- data/reference/RotFlip_8hpp.html +77 -0
- data/reference/Sockets_8hpp-source.html +130 -0
- data/reference/Sockets_8hpp.html +66 -0
- data/reference/TextInput_8hpp-source.html +64 -0
- data/reference/TextInput_8hpp.html +41 -0
- data/reference/Text_8hpp-source.html +51 -0
- data/reference/Text_8hpp.html +46 -0
- data/reference/Timing_8hpp-source.html +36 -0
- data/reference/Timing_8hpp.html +42 -0
- data/reference/Utility_8hpp-source.html +44 -0
- data/reference/Utility_8hpp.html +48 -0
- data/reference/WinUtility_8hpp-source.html +79 -0
- data/reference/WinUtility_8hpp.html +64 -0
- data/reference/Window_8hpp-source.html +91 -0
- data/reference/Window_8hpp.html +41 -0
- data/reference/annotated.html +51 -0
- data/reference/classGosu_1_1Audio-members.html +34 -0
- data/reference/classGosu_1_1Audio.html +46 -0
- data/reference/classGosu_1_1Bitmap-members.html +44 -0
- data/reference/classGosu_1_1Bitmap.html +263 -0
- data/reference/classGosu_1_1Buffer-members.html +44 -0
- data/reference/classGosu_1_1Buffer.html +78 -0
- data/reference/classGosu_1_1Buffer.png +0 -0
- data/reference/classGosu_1_1Button-members.html +36 -0
- data/reference/classGosu_1_1Button.html +143 -0
- data/reference/classGosu_1_1Color-members.html +56 -0
- data/reference/classGosu_1_1Color.html +387 -0
- data/reference/classGosu_1_1File-members.html +41 -0
- data/reference/classGosu_1_1File.html +69 -0
- data/reference/classGosu_1_1File.png +0 -0
- data/reference/classGosu_1_1Font-members.html +39 -0
- data/reference/classGosu_1_1Font.html +309 -0
- data/reference/classGosu_1_1Graphics-members.html +50 -0
- data/reference/classGosu_1_1Graphics.html +234 -0
- data/reference/classGosu_1_1Image-members.html +45 -0
- data/reference/classGosu_1_1Image.html +518 -0
- data/reference/classGosu_1_1ImageData-members.html +37 -0
- data/reference/classGosu_1_1ImageData.html +60 -0
- data/reference/classGosu_1_1Input-members.html +44 -0
- data/reference/classGosu_1_1Input.html +223 -0
- data/reference/classGosu_1_1MessageSocket-members.html +40 -0
- data/reference/classGosu_1_1MessageSocket.html +233 -0
- data/reference/classGosu_1_1Resource-members.html +39 -0
- data/reference/classGosu_1_1Resource.html +116 -0
- data/reference/classGosu_1_1Resource.png +0 -0
- data/reference/classGosu_1_1Sample-members.html +37 -0
- data/reference/classGosu_1_1Sample.html +200 -0
- data/reference/classGosu_1_1SampleInstance-members.html +38 -0
- data/reference/classGosu_1_1SampleInstance.html +169 -0
- data/reference/classGosu_1_1Song-members.html +43 -0
- data/reference/classGosu_1_1Song.html +260 -0
- data/reference/classGosu_1_1TextInput-members.html +38 -0
- data/reference/classGosu_1_1TextInput.html +121 -0
- data/reference/classGosu_1_1Window-members.html +50 -0
- data/reference/classGosu_1_1Window.html +271 -0
- data/reference/doxyfile +233 -0
- data/reference/doxygen.css +433 -0
- data/reference/doxygen.png +0 -0
- data/reference/files.html +54 -0
- data/reference/functions.html +236 -0
- data/reference/functions_enum.html +45 -0
- data/reference/functions_func.html +227 -0
- data/reference/functions_vars.html +47 -0
- data/reference/hierarchy.html +53 -0
- data/reference/index.html +26 -0
- data/reference/namespaceGosu.html +2890 -0
- data/reference/namespaceGosu_1_1Colors.html +70 -0
- data/reference/namespaceGosu_1_1Win.html +275 -0
- data/reference/namespacemembers.html +216 -0
- data/reference/namespacemembers_enum.html +52 -0
- data/reference/namespacemembers_eval.html +54 -0
- data/reference/namespacemembers_func.html +185 -0
- data/reference/namespacemembers_type.html +46 -0
- data/reference/namespacemembers_vars.html +46 -0
- data/reference/namespaces.html +35 -0
- data/reference/tab_b.gif +0 -0
- data/reference/tab_l.gif +0 -0
- data/reference/tab_r.gif +0 -0
- data/reference/tabs.css +102 -0
- data/windows/Gosu.sln +29 -0
- data/windows/Gosu.vcproj +553 -0
- data/windows/RubyGosu.vcproj +138 -0
- metadata +305 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
#include <Gosu/Text.hpp>
|
2
|
+
#include <Gosu/Bitmap.hpp>
|
3
|
+
#include <Gosu/Graphics.hpp>
|
4
|
+
#include <Gosu/Image.hpp>
|
5
|
+
#include <Gosu/Math.hpp>
|
6
|
+
#include <boost/bind.hpp>
|
7
|
+
#include <boost/algorithm/string.hpp>
|
8
|
+
#include <boost/tokenizer.hpp>
|
9
|
+
#include <cassert>
|
10
|
+
#include <cmath>
|
11
|
+
#include <algorithm>
|
12
|
+
#include <vector>
|
13
|
+
using namespace std;
|
14
|
+
|
15
|
+
namespace Gosu
|
16
|
+
{
|
17
|
+
namespace
|
18
|
+
{
|
19
|
+
struct WordInfo
|
20
|
+
{
|
21
|
+
wstring text;
|
22
|
+
unsigned width;
|
23
|
+
};
|
24
|
+
typedef vector<WordInfo> Words;
|
25
|
+
|
26
|
+
// Local helper class which manages building the bitmap from the
|
27
|
+
// collected words.
|
28
|
+
class TextBlockBuilder
|
29
|
+
{
|
30
|
+
Bitmap bmp;
|
31
|
+
unsigned usedLines, allocatedLines;
|
32
|
+
|
33
|
+
wstring fontName;
|
34
|
+
unsigned fontHeight, fontFlags, lineSpacing;
|
35
|
+
TextAlign align;
|
36
|
+
|
37
|
+
unsigned spaceWidth;
|
38
|
+
|
39
|
+
void allocNextLine()
|
40
|
+
{
|
41
|
+
++usedLines;
|
42
|
+
if (usedLines == allocatedLines)
|
43
|
+
{
|
44
|
+
allocatedLines += 10;
|
45
|
+
bmp.resize(bmp.width(),
|
46
|
+
(lineSpacing + fontHeight) * allocatedLines,
|
47
|
+
Colors::none);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
public:
|
52
|
+
TextBlockBuilder(const wstring& fontName, unsigned fontHeight,
|
53
|
+
unsigned fontFlags, unsigned lineSpacing, unsigned width,
|
54
|
+
TextAlign align)
|
55
|
+
{
|
56
|
+
usedLines = 0;
|
57
|
+
allocatedLines = 10;
|
58
|
+
|
59
|
+
bmp.resize(width, (lineSpacing + fontHeight) * allocatedLines, 0x00ffffff);
|
60
|
+
|
61
|
+
this->fontName = fontName;
|
62
|
+
this->fontHeight = fontHeight;
|
63
|
+
this->fontFlags = fontFlags;
|
64
|
+
this->lineSpacing = lineSpacing;
|
65
|
+
this->align = align;
|
66
|
+
|
67
|
+
spaceWidth = textWidth(L" ");
|
68
|
+
}
|
69
|
+
|
70
|
+
unsigned width() const
|
71
|
+
{
|
72
|
+
return bmp.width();
|
73
|
+
}
|
74
|
+
|
75
|
+
unsigned textWidth(const std::wstring& text) const
|
76
|
+
{
|
77
|
+
return Gosu::textWidth(text, fontName, fontHeight, fontFlags);
|
78
|
+
}
|
79
|
+
|
80
|
+
void addLine(Words::const_iterator begin, Words::const_iterator end,
|
81
|
+
unsigned wordsWidth, bool overrideAlign)
|
82
|
+
{
|
83
|
+
allocNextLine();
|
84
|
+
|
85
|
+
unsigned words = end - begin;
|
86
|
+
|
87
|
+
// Where does the line start? (y)
|
88
|
+
unsigned top = (usedLines - 1) * (fontHeight + lineSpacing);
|
89
|
+
|
90
|
+
// Where does the line start? (x)
|
91
|
+
double pos;
|
92
|
+
switch (align)
|
93
|
+
{
|
94
|
+
// Start so that the text touches the right border.
|
95
|
+
case taRight:
|
96
|
+
pos = bmp.width() - wordsWidth - (words - 1) * spaceWidth;
|
97
|
+
break;
|
98
|
+
|
99
|
+
// Start so that the text is centered.
|
100
|
+
case taCenter:
|
101
|
+
pos = bmp.width() - wordsWidth - (words - 1) * spaceWidth;
|
102
|
+
pos /= 2;
|
103
|
+
break;
|
104
|
+
|
105
|
+
// Just start at the left border.
|
106
|
+
default:
|
107
|
+
pos = 0;
|
108
|
+
}
|
109
|
+
|
110
|
+
// How much space is between each word?
|
111
|
+
double spacing;
|
112
|
+
if (align == taJustify && !overrideAlign)
|
113
|
+
spacing = (bmp.width() - wordsWidth) / (words - 1.0);
|
114
|
+
else
|
115
|
+
spacing = spaceWidth;
|
116
|
+
|
117
|
+
for (Words::const_iterator cur = begin; cur != end; ++cur)
|
118
|
+
{
|
119
|
+
drawText(bmp, cur->text, trunc(pos), trunc(top),
|
120
|
+
Colors::white, fontName, fontHeight, fontFlags);
|
121
|
+
pos += cur->width + spacing;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
Bitmap result() const
|
126
|
+
{
|
127
|
+
Bitmap result = bmp;
|
128
|
+
result.resize(result.width(),
|
129
|
+
usedLines * (lineSpacing + fontHeight));
|
130
|
+
return result;
|
131
|
+
}
|
132
|
+
};
|
133
|
+
|
134
|
+
void processWords(TextBlockBuilder& builder, const Words& words)
|
135
|
+
{
|
136
|
+
// Index into words to the first word in the current line.
|
137
|
+
Words::const_iterator lineBegin = words.begin();
|
138
|
+
|
139
|
+
const unsigned spaceWidth = builder.textWidth(L" "); // IMPR.
|
140
|
+
|
141
|
+
// Used width, in pixels, of the words [lineBegin..w[.
|
142
|
+
unsigned wordsWidth = 0;
|
143
|
+
|
144
|
+
// Used width of the spaces between (w-lineBegin) words.
|
145
|
+
unsigned spacesWidth = 0;
|
146
|
+
|
147
|
+
for (Words::const_iterator w = words.begin(); w != words.end(); ++w)
|
148
|
+
{
|
149
|
+
unsigned newWordsWidth = wordsWidth + w->width;
|
150
|
+
|
151
|
+
if (newWordsWidth + spacesWidth <= builder.width())
|
152
|
+
{
|
153
|
+
// There's enough space for the words [lineBegin..w] plus
|
154
|
+
// the spaces between them: Proceed with the next word.
|
155
|
+
wordsWidth = newWordsWidth;
|
156
|
+
spacesWidth += spaceWidth;
|
157
|
+
}
|
158
|
+
else
|
159
|
+
{
|
160
|
+
// No, this word wouldn't fit into the current line: Draw
|
161
|
+
// the current line, then start a new line with the current
|
162
|
+
// word.
|
163
|
+
builder.addLine(lineBegin, w, wordsWidth, false);
|
164
|
+
|
165
|
+
lineBegin = w;
|
166
|
+
wordsWidth = w->width;
|
167
|
+
spacesWidth = spaceWidth;
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
// Draw the last line as well.
|
172
|
+
if (words.empty() || lineBegin != words.end())
|
173
|
+
builder.addLine(lineBegin, words.end(), wordsWidth, true);
|
174
|
+
}
|
175
|
+
|
176
|
+
typedef boost::tokenizer<boost::char_separator<wchar_t>,
|
177
|
+
wstring::const_iterator, wstring> Tokenizer;
|
178
|
+
|
179
|
+
void processParagraph(TextBlockBuilder& builder,
|
180
|
+
const wstring& paragraph)
|
181
|
+
{
|
182
|
+
Words collectedWords;
|
183
|
+
|
184
|
+
boost::char_separator<wchar_t> sep(L" ");
|
185
|
+
Tokenizer words(paragraph, sep);
|
186
|
+
for (Tokenizer::iterator i = words.begin(); i != words.end(); ++i)
|
187
|
+
{
|
188
|
+
WordInfo newWord;
|
189
|
+
newWord.text = *i;
|
190
|
+
newWord.width = builder.textWidth(newWord.text);
|
191
|
+
collectedWords.push_back(newWord);
|
192
|
+
}
|
193
|
+
|
194
|
+
processWords(builder, collectedWords);
|
195
|
+
}
|
196
|
+
|
197
|
+
void processText(TextBlockBuilder& builder, const wstring& text)
|
198
|
+
{
|
199
|
+
vector<wstring> paragraphs;
|
200
|
+
wstring processedText = boost::replace_all_copy(text, L"\r\n", L"\n");
|
201
|
+
boost::split(paragraphs, processedText,
|
202
|
+
boost::is_any_of(L"\r\n"));
|
203
|
+
for_each(paragraphs.begin(), paragraphs.end(),
|
204
|
+
boost::bind(processParagraph, boost::ref(builder), _1));
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
Gosu::Bitmap Gosu::createText(const std::wstring& text,
|
210
|
+
const std::wstring& fontName, unsigned fontHeight, unsigned lineSpacing,
|
211
|
+
unsigned maxWidth, TextAlign align, unsigned fontFlags)
|
212
|
+
{
|
213
|
+
// Set up the builder object which will manage all the drawing and
|
214
|
+
// conversions for us.
|
215
|
+
TextBlockBuilder builder(fontName, fontHeight, fontFlags,
|
216
|
+
lineSpacing, maxWidth, align);
|
217
|
+
|
218
|
+
// Let the process* functions draw everything.
|
219
|
+
processText(builder, text);
|
220
|
+
|
221
|
+
// Done!
|
222
|
+
return builder.result();
|
223
|
+
}
|
@@ -0,0 +1,242 @@
|
|
1
|
+
#include <Gosu/Bitmap.hpp>
|
2
|
+
#include <Gosu/Text.hpp>
|
3
|
+
#include <Gosu/Utility.hpp>
|
4
|
+
#include <Gosu/IO.hpp>
|
5
|
+
#include <boost/utility.hpp>
|
6
|
+
#include <boost/cstdint.hpp>
|
7
|
+
#include <cmath>
|
8
|
+
#include <stdexcept>
|
9
|
+
#include <map>
|
10
|
+
#include <vector>
|
11
|
+
#include <ApplicationServices/ApplicationServices.h>
|
12
|
+
|
13
|
+
std::wstring Gosu::defaultFontName()
|
14
|
+
{
|
15
|
+
// OF COURSE Helvetica is better - but the dots above my capital umlauts get
|
16
|
+
// eaten when I use it with Gosu. Until this is fixed, keep Arial. (TODO)
|
17
|
+
return L"Arial";
|
18
|
+
}
|
19
|
+
|
20
|
+
namespace Gosu
|
21
|
+
{
|
22
|
+
std::vector<unsigned short> wstringToUniChars(const std::wstring& ws);
|
23
|
+
}
|
24
|
+
|
25
|
+
#include <iostream>
|
26
|
+
#include <ostream>
|
27
|
+
#include <sstream>
|
28
|
+
void throwError(OSStatus status, unsigned line)
|
29
|
+
{
|
30
|
+
std::ostringstream str;
|
31
|
+
str << "Error on line " << line << " (Code " << status << "): "
|
32
|
+
<< GetMacOSStatusErrorString(status)
|
33
|
+
<< " (" << GetMacOSStatusCommentString(status) << ")";
|
34
|
+
throw std::runtime_error(str.str());
|
35
|
+
}
|
36
|
+
|
37
|
+
#define checkErr(status) if (!(status)) {} else throwError(status, __LINE__)
|
38
|
+
|
39
|
+
namespace
|
40
|
+
{
|
41
|
+
class MacBitmap : boost::noncopyable
|
42
|
+
{
|
43
|
+
boost::uint32_t* buf;
|
44
|
+
unsigned width, height;
|
45
|
+
CGContextRef ctx;
|
46
|
+
|
47
|
+
public:
|
48
|
+
MacBitmap(boost::uint32_t* buf, unsigned width, unsigned height)
|
49
|
+
: buf(buf), width(width), height(height)
|
50
|
+
{
|
51
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceUserRGB);
|
52
|
+
ctx = CGBitmapContextCreate (buf, width, height, 8, width * 4, colorSpace,
|
53
|
+
kCGImageAlphaPremultipliedLast);
|
54
|
+
|
55
|
+
CGColorSpaceRelease( colorSpace );
|
56
|
+
}
|
57
|
+
|
58
|
+
~MacBitmap()
|
59
|
+
{
|
60
|
+
CGContextRelease(ctx);
|
61
|
+
}
|
62
|
+
|
63
|
+
CGContextRef context() const
|
64
|
+
{
|
65
|
+
return ctx;
|
66
|
+
}
|
67
|
+
};
|
68
|
+
|
69
|
+
struct CachedFontInfo
|
70
|
+
{
|
71
|
+
ATSUFontID fontId;
|
72
|
+
double heightAt1Pt;
|
73
|
+
double descentAt1Pt;
|
74
|
+
};
|
75
|
+
CachedFontInfo& getFont(const std::wstring& fontName)
|
76
|
+
{
|
77
|
+
static std::map<std::wstring, CachedFontInfo> fonts;
|
78
|
+
|
79
|
+
if (fonts.count(fontName))
|
80
|
+
return fonts[fontName];
|
81
|
+
|
82
|
+
// Get reference to font, loaded from the system or a file.
|
83
|
+
ATSFontRef atsRef;
|
84
|
+
if (fontName.find(L"/") == std::wstring::npos)
|
85
|
+
{
|
86
|
+
// System font
|
87
|
+
CFStringRef cfName = CFStringCreateWithCString(NULL, Gosu::narrow(fontName).c_str(), kCFStringEncodingASCII);
|
88
|
+
atsRef = ATSFontFindFromName(cfName, kATSOptionFlagsDefault);
|
89
|
+
if (!atsRef)
|
90
|
+
throw std::runtime_error("Cannot find font " + Gosu::narrow(fontName));
|
91
|
+
CFRelease(cfName);
|
92
|
+
}
|
93
|
+
else
|
94
|
+
{
|
95
|
+
// Filename to font
|
96
|
+
Gosu::Buffer buf;
|
97
|
+
Gosu::loadFile(buf, fontName);
|
98
|
+
|
99
|
+
ATSFontContainerRef container;
|
100
|
+
checkErr( ATSFontActivateFromMemory(buf.data(), buf.size(),
|
101
|
+
kATSFontContextLocal, kATSFontFormatUnspecified,
|
102
|
+
NULL, kATSOptionFlagsDefault, &container) );
|
103
|
+
|
104
|
+
ATSFontRef fontRefs[1024];
|
105
|
+
ItemCount fontCount;
|
106
|
+
checkErr( ATSFontFindFromContainer(container, kATSOptionFlagsDefault,
|
107
|
+
1024, fontRefs, &fontCount) );
|
108
|
+
if (fontCount == 0)
|
109
|
+
throw std::runtime_error("No font found in " + Gosu::narrow(fontName));
|
110
|
+
|
111
|
+
atsRef = fontRefs[0];
|
112
|
+
}
|
113
|
+
|
114
|
+
// Calculate metrics (for space allocations) and create CachedFontInfo entry.
|
115
|
+
CachedFontInfo newFont;
|
116
|
+
newFont.fontId = FMGetFontFromATSFontRef(atsRef);
|
117
|
+
|
118
|
+
ATSFontMetrics metrics;
|
119
|
+
checkErr(ATSFontGetHorizontalMetrics(newFont.fontId, kATSOptionFlagsDefault, &metrics));
|
120
|
+
newFont.heightAt1Pt = metrics.ascent - metrics.descent;
|
121
|
+
newFont.descentAt1Pt = -metrics.descent;
|
122
|
+
fonts[fontName] = newFont;
|
123
|
+
return fonts[fontName];
|
124
|
+
}
|
125
|
+
|
126
|
+
class ATSULayoutAndStyle
|
127
|
+
{
|
128
|
+
ATSUStyle style;
|
129
|
+
ATSUTextLayout layout;
|
130
|
+
std::vector<UniChar> utf16; // More like UCS-2-INTERNAL.
|
131
|
+
|
132
|
+
template<typename T>
|
133
|
+
void setAttribute(ATSUAttributeTag tag, T value)
|
134
|
+
{
|
135
|
+
ByteCount size = sizeof value;
|
136
|
+
ATSUAttributeValuePtr ptr = &value;
|
137
|
+
checkErr( ATSUSetAttributes(style, 1, &tag, &size, &ptr) );
|
138
|
+
}
|
139
|
+
|
140
|
+
template<typename T>
|
141
|
+
void setLayoutControl(ATSUAttributeTag tag, T value)
|
142
|
+
{
|
143
|
+
ByteCount size = sizeof value;
|
144
|
+
ATSUAttributeValuePtr ptr = &value;
|
145
|
+
checkErr( ATSUSetLayoutControls(layout, 1, &tag, &size, &ptr) );
|
146
|
+
}
|
147
|
+
|
148
|
+
public:
|
149
|
+
ATSULayoutAndStyle(const std::wstring& text, const std::wstring& fontName, unsigned fontHeightPx, unsigned fontFlags)
|
150
|
+
{
|
151
|
+
utf16 = Gosu::wstringToUniChars(text);
|
152
|
+
|
153
|
+
checkErr( ATSUCreateStyle(&style) );
|
154
|
+
|
155
|
+
CachedFontInfo& font = getFont(fontName);
|
156
|
+
|
157
|
+
setAttribute<ATSUFontID>(kATSUFontTag, font.fontId);
|
158
|
+
|
159
|
+
setAttribute<Fixed>(kATSUSizeTag, X2Fix(fontHeightPx / font.heightAt1Pt));
|
160
|
+
if (fontFlags & Gosu::ffBold)
|
161
|
+
setAttribute<Boolean>(kATSUQDBoldfaceTag, TRUE);
|
162
|
+
if (fontFlags & Gosu::ffItalic)
|
163
|
+
setAttribute<Boolean>(kATSUQDItalicTag, TRUE);
|
164
|
+
if (fontFlags & Gosu::ffUnderline)
|
165
|
+
setAttribute<Boolean>(kATSUQDUnderlineTag, TRUE);
|
166
|
+
|
167
|
+
UniCharCount runLength = utf16.size();
|
168
|
+
checkErr( ATSUCreateTextLayoutWithTextPtr(&utf16[0], kATSUFromTextBeginning,
|
169
|
+
kATSUToTextEnd, utf16.size(), 1, &runLength, &style, &layout) );
|
170
|
+
}
|
171
|
+
|
172
|
+
~ATSULayoutAndStyle()
|
173
|
+
{
|
174
|
+
checkErr( ATSUDisposeStyle(style) );
|
175
|
+
checkErr( ATSUDisposeTextLayout(layout) );
|
176
|
+
}
|
177
|
+
|
178
|
+
Rect textExtents() const
|
179
|
+
{
|
180
|
+
Rect rect;
|
181
|
+
checkErr( ATSUSetTransientFontMatching(layout, TRUE) );
|
182
|
+
checkErr( ATSUMeasureTextImage(layout, kATSUFromTextBeginning,
|
183
|
+
kATSUToTextEnd, X2Fix(0), X2Fix(0), &rect) );
|
184
|
+
return rect;
|
185
|
+
}
|
186
|
+
|
187
|
+
void drawToContext(Fixed x, Fixed y, CGContextRef context)
|
188
|
+
{
|
189
|
+
// Always draw in black - recoloring to white happens in drawText itself.
|
190
|
+
// Reason: Text drawn using fallback fonts seems to always be black anyway :(
|
191
|
+
|
192
|
+
RGBColor color = { 0, 0, 0 };
|
193
|
+
setAttribute<RGBColor>(kATSUColorTag, color);
|
194
|
+
setLayoutControl<CGContextRef>(kATSUCGContextTag, context);
|
195
|
+
checkErr( ATSUSetTransientFontMatching(layout, TRUE) );
|
196
|
+
checkErr( ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, x, y) );
|
197
|
+
}
|
198
|
+
};
|
199
|
+
}
|
200
|
+
|
201
|
+
unsigned Gosu::textWidth(const std::wstring& text,
|
202
|
+
const std::wstring& fontName, unsigned fontHeight, unsigned fontFlags)
|
203
|
+
{
|
204
|
+
// TODO: special case :///7
|
205
|
+
if (text == L" ")
|
206
|
+
return fontHeight / 3;
|
207
|
+
|
208
|
+
ATSULayoutAndStyle atlas(text, fontName, fontHeight, fontFlags);
|
209
|
+
Rect rect = atlas.textExtents();
|
210
|
+
return rect.right + 1 - rect.left;
|
211
|
+
}
|
212
|
+
|
213
|
+
void Gosu::drawText(Bitmap& bitmap, const std::wstring& text, int x, int y,
|
214
|
+
Color c, const std::wstring& fontName, unsigned fontHeight,
|
215
|
+
unsigned fontFlags)
|
216
|
+
{
|
217
|
+
CachedFontInfo& font = getFont(fontName);
|
218
|
+
|
219
|
+
ATSULayoutAndStyle atlas(text, fontName, fontHeight, fontFlags);
|
220
|
+
Rect rect = atlas.textExtents();
|
221
|
+
unsigned width = rect.right + 1 - rect.left;
|
222
|
+
std::vector<boost::uint32_t> buf(width * fontHeight);
|
223
|
+
{
|
224
|
+
MacBitmap helper(&buf[0], width, fontHeight);
|
225
|
+
atlas.drawToContext(X2Fix(-rect.left), X2Fix(fontHeight / font.heightAt1Pt * font.descentAt1Pt),
|
226
|
+
helper.context());
|
227
|
+
}
|
228
|
+
|
229
|
+
Bitmap wholeText;
|
230
|
+
wholeText.resize(width, fontHeight);
|
231
|
+
for (unsigned relY = 0; relY < fontHeight; ++relY)
|
232
|
+
for (unsigned relX = 0; relX < width; ++relX)
|
233
|
+
{
|
234
|
+
#ifdef __BIG_ENDIAN__
|
235
|
+
Color::Channel alpha = buf[relY * width + relX];
|
236
|
+
#else
|
237
|
+
Color::Channel alpha = Color(buf[relY * width + relX]).alpha();
|
238
|
+
#endif
|
239
|
+
wholeText.setPixel(relX, relY, Color(alpha, 0xff, 0xff, 0xff));
|
240
|
+
}
|
241
|
+
bitmap.insert(wholeText, x, y);
|
242
|
+
}
|