tea 0.6.1
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.
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/README.rdoc +93 -0
- data/doc/example/bitmap_draw.rb +21 -0
- data/doc/example/bitmap_load.rb +14 -0
- data/doc/example/bitmap_new.rb +19 -0
- data/doc/example/circle.rb +24 -0
- data/doc/example/circle_alpha.rb +45 -0
- data/doc/example/circle_alpha_bitmap.rb +51 -0
- data/doc/example/circle_bitmap.rb +18 -0
- data/doc/example/clip.rb +46 -0
- data/doc/example/event_app.rb +45 -0
- data/doc/example/event_keyboard.rb +43 -0
- data/doc/example/event_mouse.rb +85 -0
- data/doc/example/font_hello.rb +22 -0
- data/doc/example/font_word_wrap.rb +44 -0
- data/doc/example/grab.rb +28 -0
- data/doc/example/init.rb +10 -0
- data/doc/example/lines.rb +49 -0
- data/doc/example/lines_aa.rb +44 -0
- data/doc/example/lines_alpha.rb +33 -0
- data/doc/example/point.rb +26 -0
- data/doc/example/rect.rb +15 -0
- data/doc/example/rect_alpha.rb +75 -0
- data/doc/example/screen_set_mode.rb +18 -0
- data/doc/example/screen_update.rb +14 -0
- data/doc/example/sfont_hello.rb +22 -0
- data/doc/example/smile.png +0 -0
- data/doc/example/smile_bounce.rb +44 -0
- data/doc/example/smile_move.rb +58 -0
- data/doc/example/smile_move_2.rb +78 -0
- data/doc/example/sound.rb +101 -0
- data/doc/example/state_app.rb +33 -0
- data/doc/example/state_keyboard.rb +23 -0
- data/doc/example/state_mouse.rb +60 -0
- data/doc/key_constants.textile +129 -0
- data/doc/key_modifiers.textile +19 -0
- data/doc/reference.textile +421 -0
- data/lib/tea.rb +34 -0
- data/lib/tea/c_bitmap.rb +122 -0
- data/lib/tea/c_error.rb +11 -0
- data/lib/tea/c_font.rb +302 -0
- data/lib/tea/c_sound.rb +144 -0
- data/lib/tea/m_color.rb +50 -0
- data/lib/tea/m_event.rb +65 -0
- data/lib/tea/m_event_app.rb +96 -0
- data/lib/tea/m_event_dispatch.rb +54 -0
- data/lib/tea/m_event_keyboard.rb +311 -0
- data/lib/tea/m_event_mouse.rb +189 -0
- data/lib/tea/mix_blitting.rb +31 -0
- data/lib/tea/mix_clipping.rb +71 -0
- data/lib/tea/mix_grabbing.rb +86 -0
- data/lib/tea/mix_image_saving.rb +70 -0
- data/lib/tea/mix_primitive.rb +613 -0
- data/lib/tea/o_screen.rb +98 -0
- metadata +137 -0
data/lib/tea.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Tea is a simple 2D game development library for Ruby.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
require 'tea/c_bitmap'
|
6
|
+
require 'tea/c_error'
|
7
|
+
require 'tea/c_font'
|
8
|
+
require 'tea/c_sound'
|
9
|
+
require 'tea/m_color'
|
10
|
+
require 'tea/m_event'
|
11
|
+
require 'tea/o_screen'
|
12
|
+
|
13
|
+
# The Tea module acts as a namespace for all Tea-related objects and
|
14
|
+
# methods.
|
15
|
+
module Tea
|
16
|
+
|
17
|
+
# Initialise Tea. This needs to be called before using any of Tea's
|
18
|
+
# objects or methods.
|
19
|
+
#
|
20
|
+
# May throw Tea::Error if initialisation fails.
|
21
|
+
def Tea.init
|
22
|
+
SDL.init(SDL::INIT_VIDEO | SDL::INIT_AUDIO)
|
23
|
+
|
24
|
+
# Get typed characters from keys when pressed.
|
25
|
+
SDL::Event.enable_unicode
|
26
|
+
SDL::Mixer.open 44100, SDL::Mixer::DEFAULT_FORMAT, SDL::Mixer::DEFAULT_CHANNELS, 1024
|
27
|
+
rescue SDL::Error => e
|
28
|
+
raise Tea::Error, e.message, e.backtrace
|
29
|
+
end
|
30
|
+
|
31
|
+
rv_match = RUBY_VERSION.match(/(\d+)\.(\d+)\.\d+/)
|
32
|
+
RUBY_1_9 = rv_match && rv_match[1].to_i >= 1 && rv_match[2].to_i >= 9
|
33
|
+
|
34
|
+
end
|
data/lib/tea/c_bitmap.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# This file holds the Bitmap class, which are grids of pixels that hold
|
2
|
+
# graphics.
|
3
|
+
|
4
|
+
require 'sdl'
|
5
|
+
|
6
|
+
require 'tea/mix_blitting'
|
7
|
+
require 'tea/mix_clipping'
|
8
|
+
require 'tea/mix_grabbing'
|
9
|
+
require 'tea/mix_image_saving'
|
10
|
+
require 'tea/mix_primitive'
|
11
|
+
|
12
|
+
#
|
13
|
+
module Tea
|
14
|
+
|
15
|
+
# A Bitmap is a grid of pixels that holds graphics.
|
16
|
+
class Bitmap
|
17
|
+
|
18
|
+
# Create a new Bitmap. This can be done in 2 ways:
|
19
|
+
#
|
20
|
+
# (image_path):: loads from an image
|
21
|
+
# (width, height, color):: creates with the given size and color
|
22
|
+
#
|
23
|
+
# May raise ArgumentError if the arguments passed in don't match one of the
|
24
|
+
# above, or Tea::Error if the Bitmap creation fails.
|
25
|
+
def initialize(*args)
|
26
|
+
case args.length
|
27
|
+
when 1 then from_image(*args)
|
28
|
+
when 3 then fresh(*args)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1 or 3)", caller
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get the width of the Bitmap in pixels.
|
35
|
+
def w
|
36
|
+
@buffer.w
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get the height of the Bitmap in pixels.
|
40
|
+
def h
|
41
|
+
@buffer.h
|
42
|
+
end
|
43
|
+
|
44
|
+
# Convert the Bitmap's internal format to that of the screen to speed up
|
45
|
+
# drawing. Intended for internal use, but calling it manually won't bring
|
46
|
+
# any harm.
|
47
|
+
def optimize_for_screen
|
48
|
+
@buffer = @buffer.display_format_alpha
|
49
|
+
end
|
50
|
+
|
51
|
+
include Blitting
|
52
|
+
def blitting_buffer
|
53
|
+
@buffer
|
54
|
+
end
|
55
|
+
|
56
|
+
include Clipping
|
57
|
+
def clipping_buffer
|
58
|
+
@buffer
|
59
|
+
end
|
60
|
+
|
61
|
+
include Grabbing
|
62
|
+
def grabbing_buffer
|
63
|
+
@buffer
|
64
|
+
end
|
65
|
+
|
66
|
+
include ImageSaving
|
67
|
+
def image_saving_buffer
|
68
|
+
@buffer
|
69
|
+
end
|
70
|
+
|
71
|
+
include Primitive
|
72
|
+
def primitive_buffer
|
73
|
+
@buffer
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Create a new Bitmap from an image file.
|
79
|
+
#
|
80
|
+
# May raise Tea::Error if it fails.
|
81
|
+
def from_image(image_path)
|
82
|
+
@buffer = SDL::Surface.load(image_path)
|
83
|
+
|
84
|
+
if Tea::Screen.mode_set?
|
85
|
+
optimize_for_screen
|
86
|
+
else
|
87
|
+
Tea::Screen.register_bitmap_for_optimizing self
|
88
|
+
end
|
89
|
+
rescue SDL::Error => e
|
90
|
+
raise Tea::Error, e.message, e.backtrace
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create a new Bitmap of the given size and initialized with the color.
|
94
|
+
#
|
95
|
+
# May raise Tea::Error if it fails.
|
96
|
+
def fresh(width, height, color)
|
97
|
+
if width < 1 || height < 1
|
98
|
+
raise Tea::Error, "can't create bitmap smaller than 1x1 (#{width}x#{height})", caller
|
99
|
+
end
|
100
|
+
|
101
|
+
# Default to big endian, as it works better with SGE's anti-aliasing.
|
102
|
+
rmask = 0xff000000
|
103
|
+
gmask = 0x00ff0000
|
104
|
+
bmask = 0x0000ff00
|
105
|
+
amask = 0x000000ff
|
106
|
+
@buffer = SDL::Surface.new(SDL::SWSURFACE,
|
107
|
+
width, height, 32,
|
108
|
+
rmask, gmask, bmask, amask)
|
109
|
+
rect 0, 0, w, h, color, :mix => :replace
|
110
|
+
|
111
|
+
if Tea::Screen.mode_set?
|
112
|
+
optimize_for_screen
|
113
|
+
else
|
114
|
+
Tea::Screen.register_bitmap_for_optimizing self
|
115
|
+
end
|
116
|
+
rescue SDL::Error => e
|
117
|
+
raise Tea::Error, e.message, e.backtrace
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/lib/tea/c_error.rb
ADDED
data/lib/tea/c_font.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
# This file holds the Font class, which is used for drawing text.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
require 'tea/c_error'
|
6
|
+
require 'tea/c_bitmap'
|
7
|
+
require 'tea/m_color'
|
8
|
+
|
9
|
+
#
|
10
|
+
module Tea
|
11
|
+
|
12
|
+
# Fonts are used to draw text. They do this by mapping characters to images
|
13
|
+
# that are loaded when the Font is created.
|
14
|
+
#
|
15
|
+
# Tea supports bitmap fonts, and Karl Bartel's SFont format.
|
16
|
+
#
|
17
|
+
# Bitmap fonts (Tea::Font::BITMAP_FONT) are a single row of 256 characters in
|
18
|
+
# a BMP or PNG image. If the image has intrinsic transparency, it will be
|
19
|
+
# used by default. Otherwise, loading the font with the color transparency
|
20
|
+
# option will consider one color as the 'transparent color' and no such
|
21
|
+
# pixels will be drawn.
|
22
|
+
#
|
23
|
+
# SFont fonts (Tea::Font::SFONT) are also a single row of character images,
|
24
|
+
# but ranging from ASCII 33 to ASCII 127:
|
25
|
+
#
|
26
|
+
# ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @
|
27
|
+
# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ `
|
28
|
+
# a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
|
29
|
+
#
|
30
|
+
# SFont uses variable width characters. Characters are separated by magenta
|
31
|
+
# (255, 0, 255) in the top pixel row, so the pixels directly under the
|
32
|
+
# magenta strips would be the gaps between characters, e.g.:
|
33
|
+
#
|
34
|
+
# ____ ____ ____ ____ <-- magenta strips
|
35
|
+
# .... # .... #### .... ### ....
|
36
|
+
# .... # # .... # # .... # # ....
|
37
|
+
# .... ##### .... #### .... # .... <-- gaps between characters
|
38
|
+
# .... # # .... # # .... # # .... (can be any color)
|
39
|
+
# .... # # .... #### .... ### ....
|
40
|
+
#
|
41
|
+
# The width of a space is not defined in the SFont format, so Tea uses the
|
42
|
+
# width of the first character, '!'.
|
43
|
+
class Font
|
44
|
+
|
45
|
+
# +font_type+ constant for bitmap fonts.
|
46
|
+
BITMAP_FONT = :BITMAP_FONT
|
47
|
+
SFONT = :SFONT
|
48
|
+
|
49
|
+
BITMAP_FONT_START = 0
|
50
|
+
BITMAP_FONT_LENGTH = 256
|
51
|
+
SFONT_START = 33
|
52
|
+
SFONT_LENGTH = 94
|
53
|
+
|
54
|
+
CODE_POINT_SPACE = 32
|
55
|
+
|
56
|
+
# Create a new font from a font file given by +path+.
|
57
|
+
#
|
58
|
+
# +font_type+ is one of Tea::Font::BITMAP_FONT or Tea::Font::SFONT. See
|
59
|
+
# Tea::Font for font format details.
|
60
|
+
#
|
61
|
+
# Optional hash arguments:
|
62
|
+
#
|
63
|
+
# +:transparent_color+:: If +font_type+ is Tea::Font::BITMAP_FONT, this
|
64
|
+
# is the color that should not be drawn when
|
65
|
+
# rendering text. Default: Tea::Color::MAGENTA.
|
66
|
+
#
|
67
|
+
# May raise Tea::Error on failure.
|
68
|
+
def initialize(path, font_type, options=nil)
|
69
|
+
font = SDL::Surface.load(path)
|
70
|
+
transparent_color = options ? options[:transparent_color] : nil
|
71
|
+
@font_type = font_type
|
72
|
+
|
73
|
+
case font_type
|
74
|
+
when BITMAP_FONT
|
75
|
+
@glyphs = letters_from_bitmap_font(font, transparent_color)
|
76
|
+
when SFONT
|
77
|
+
@glyphs = letters_from_sfont(font, transparent_color)
|
78
|
+
end
|
79
|
+
rescue SDL::Error => e
|
80
|
+
raise Tea::Error, e.message, e.backtrace
|
81
|
+
end
|
82
|
+
|
83
|
+
# Calculate the pixel width of a string rendered with this font.
|
84
|
+
def string_w(string)
|
85
|
+
w = 0
|
86
|
+
each_code_point(string) { |pt| w += get_glyph_w(pt) }
|
87
|
+
w
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get the height of the font's characters.
|
91
|
+
def h
|
92
|
+
# Nothing special about 0, all chars are the same height.
|
93
|
+
@glyphs[0].h
|
94
|
+
end
|
95
|
+
|
96
|
+
# Array-like return class of word_wrap().
|
97
|
+
# +end_x+ holds the position of the end of the final line.
|
98
|
+
class WrappedLines < Array
|
99
|
+
attr_accessor :end_x
|
100
|
+
end
|
101
|
+
|
102
|
+
# Split a string into an array of lines such that each line, when rendered
|
103
|
+
# using this font, is no wider than width.
|
104
|
+
#
|
105
|
+
# +initial_left_margin+ sets the starting position for the first line of
|
106
|
+
# wrapping.
|
107
|
+
#
|
108
|
+
# Returns an array of strings, representing the split lines.
|
109
|
+
def word_wrap(string, width, initial_left_margin=0)
|
110
|
+
# This is adapted from the Sphere RPG Engine's font word wrapping code.
|
111
|
+
# Rickety, but battle-tested.
|
112
|
+
space_w = string_w(" ")
|
113
|
+
tab = " " * 4
|
114
|
+
tab_w = string_w(tab)
|
115
|
+
|
116
|
+
x = initial_left_margin
|
117
|
+
|
118
|
+
lines = WrappedLines.new
|
119
|
+
lines.push ""
|
120
|
+
word = ""
|
121
|
+
word_w = 0
|
122
|
+
|
123
|
+
# No good string character iterators for Ruby 1.8, hence crappy O(n) access.
|
124
|
+
string.length.times do |i|
|
125
|
+
# Crappy O(n) access here.
|
126
|
+
char = string[i, 1]
|
127
|
+
|
128
|
+
case char
|
129
|
+
when " ", "\t"
|
130
|
+
if char == " "
|
131
|
+
spacing = " "
|
132
|
+
spacing_w = space_w
|
133
|
+
elsif char == "\t"
|
134
|
+
spacing = tab
|
135
|
+
spacing_w = tab_w
|
136
|
+
end
|
137
|
+
|
138
|
+
if x + word_w + spacing_w > width
|
139
|
+
lines.push word + spacing
|
140
|
+
x = word_w + spacing_w
|
141
|
+
else
|
142
|
+
lines[-1] += word + spacing
|
143
|
+
x += word_w + spacing_w
|
144
|
+
end
|
145
|
+
|
146
|
+
word = ""
|
147
|
+
word_w = 0
|
148
|
+
|
149
|
+
when "\n"
|
150
|
+
lines[-1] += word
|
151
|
+
lines.push ""
|
152
|
+
x = 0
|
153
|
+
word = ""
|
154
|
+
word_w = 0
|
155
|
+
|
156
|
+
else
|
157
|
+
char_w = string_w(char)
|
158
|
+
|
159
|
+
if word_w + char_w > width && x == 0
|
160
|
+
# Word is too long for a single line, so split it.
|
161
|
+
lines[-1] += word
|
162
|
+
lines.push ""
|
163
|
+
word = ""
|
164
|
+
word_w = 0
|
165
|
+
elsif x + word_w + char_w > width
|
166
|
+
# Start a new line.
|
167
|
+
x = 0
|
168
|
+
lines.push ""
|
169
|
+
end
|
170
|
+
|
171
|
+
word += char
|
172
|
+
word_w += char_w
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
lines[-1] += word
|
178
|
+
lines.end_x = x + word_w
|
179
|
+
|
180
|
+
lines
|
181
|
+
end
|
182
|
+
|
183
|
+
# Write the given string onto the bitmap at (x, y).
|
184
|
+
def draw_to(bitmap, x, y, string)
|
185
|
+
draw_x = x
|
186
|
+
each_code_point(string) do |pt|
|
187
|
+
glyph = get_glyph(pt)
|
188
|
+
if glyph
|
189
|
+
bitmap.blit glyph, draw_x, y
|
190
|
+
draw_x += glyph.w
|
191
|
+
else
|
192
|
+
draw_x += get_glyph_w(pt)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
string
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
# Extract an array of letter glyph Bitmaps from a bitmap font.
|
202
|
+
def letters_from_bitmap_font(font_surface, transparent_color)
|
203
|
+
unless font_surface.w % BITMAP_FONT_LENGTH == 0
|
204
|
+
raise Tea::Error, "Bitmap font cannot be evenly divided into #{BITMAP_FONT_LENGTH} glyphs (w == #{font_surface.w})", caller
|
205
|
+
end
|
206
|
+
|
207
|
+
char_w = font_surface.w / BITMAP_FONT_LENGTH
|
208
|
+
char_h = font_surface.h
|
209
|
+
trans_rgba = transparent_color ? Color.split(transparent_color) : nil
|
210
|
+
|
211
|
+
glyphs = Array.new(BITMAP_FONT_LENGTH) do |char|
|
212
|
+
glyph = Bitmap.new(char_w, char_h, Color::CLEAR)
|
213
|
+
for gy in 0...char_h
|
214
|
+
for gx in 0...char_w
|
215
|
+
color = font_surface.get_rgba(font_surface[char_w * char + gx, gy])
|
216
|
+
glyph[gx, gy] = Color.mix(*color) unless color == trans_rgba
|
217
|
+
end
|
218
|
+
end
|
219
|
+
glyph
|
220
|
+
end
|
221
|
+
|
222
|
+
glyphs
|
223
|
+
end
|
224
|
+
|
225
|
+
# Extract an array of letter glyph Bitmaps from an SFont.
|
226
|
+
def letters_from_sfont(font_surface, transparent_color)
|
227
|
+
detect_x = 0
|
228
|
+
glyphs = Array.new(SFONT_LENGTH) do |i|
|
229
|
+
detect_x += 1 while Tea::Color.mix(*font_surface.get_rgba(font_surface[detect_x, 0])) == Tea::Color::MAGENTA && detect_x < font_surface.w
|
230
|
+
raise Tea::Error, "Expected #{SFONT_LENGTH} letters while loading SFont, got #{i}" if detect_x >= font_surface.w
|
231
|
+
|
232
|
+
char_right_x = detect_x
|
233
|
+
char_right_x += 1 while Tea::Color.mix(*font_surface.get_rgba(font_surface[char_right_x, 0])) != Tea::Color::MAGENTA && char_right_x < font_surface.w
|
234
|
+
char_w = char_right_x - detect_x
|
235
|
+
char_h = font_surface.h
|
236
|
+
trans_rgba = transparent_color ? Color.split(transparent_color) : nil
|
237
|
+
|
238
|
+
glyph = Bitmap.new(char_w, char_h, Color::CLEAR)
|
239
|
+
for gy in 0...char_h
|
240
|
+
for gx in 0...char_w
|
241
|
+
color = font_surface.get_rgba(font_surface[detect_x + gx, gy])
|
242
|
+
glyph[gx, gy] = Color.mix(*color) unless color == trans_rgba
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
detect_x = char_right_x
|
247
|
+
|
248
|
+
glyph
|
249
|
+
end
|
250
|
+
|
251
|
+
glyphs
|
252
|
+
end
|
253
|
+
|
254
|
+
# Run a block for each code point of the string.
|
255
|
+
def each_code_point(string)
|
256
|
+
if RUBY_1_9
|
257
|
+
string.codepoints do |pt|
|
258
|
+
yield pt
|
259
|
+
end
|
260
|
+
else
|
261
|
+
string.length.times do |i|
|
262
|
+
pt = string[i]
|
263
|
+
yield pt
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Get the designated width of the glyph associated with the code point.
|
269
|
+
def get_glyph_w(code_point)
|
270
|
+
case @font_type
|
271
|
+
when BITMAP_FONT
|
272
|
+
@glyphs[code_point].w
|
273
|
+
when SFONT
|
274
|
+
if SFONT_START <= code_point && code_point < SFONT_START + SFONT_LENGTH
|
275
|
+
@glyphs[code_point - SFONT_START].w
|
276
|
+
elsif code_point == CODE_POINT_SPACE
|
277
|
+
# Substitute with first char width, like SFont reference library.
|
278
|
+
@glyphs[0].w
|
279
|
+
else
|
280
|
+
0
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Get the glyph bitmap for the given code point. May return nil if the
|
286
|
+
# code point doesn't map to any glyph.
|
287
|
+
def get_glyph(code_point)
|
288
|
+
case @font_type
|
289
|
+
when BITMAP_FONT
|
290
|
+
@glyphs[code_point]
|
291
|
+
when SFONT
|
292
|
+
if SFONT_START <= code_point && code_point < SFONT_START + SFONT_LENGTH
|
293
|
+
@glyphs[code_point - SFONT_START]
|
294
|
+
else
|
295
|
+
nil
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|