tea 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/README.rdoc +93 -0
  4. data/doc/example/bitmap_draw.rb +21 -0
  5. data/doc/example/bitmap_load.rb +14 -0
  6. data/doc/example/bitmap_new.rb +19 -0
  7. data/doc/example/circle.rb +24 -0
  8. data/doc/example/circle_alpha.rb +45 -0
  9. data/doc/example/circle_alpha_bitmap.rb +51 -0
  10. data/doc/example/circle_bitmap.rb +18 -0
  11. data/doc/example/clip.rb +46 -0
  12. data/doc/example/event_app.rb +45 -0
  13. data/doc/example/event_keyboard.rb +43 -0
  14. data/doc/example/event_mouse.rb +85 -0
  15. data/doc/example/font_hello.rb +22 -0
  16. data/doc/example/font_word_wrap.rb +44 -0
  17. data/doc/example/grab.rb +28 -0
  18. data/doc/example/init.rb +10 -0
  19. data/doc/example/lines.rb +49 -0
  20. data/doc/example/lines_aa.rb +44 -0
  21. data/doc/example/lines_alpha.rb +33 -0
  22. data/doc/example/point.rb +26 -0
  23. data/doc/example/rect.rb +15 -0
  24. data/doc/example/rect_alpha.rb +75 -0
  25. data/doc/example/screen_set_mode.rb +18 -0
  26. data/doc/example/screen_update.rb +14 -0
  27. data/doc/example/sfont_hello.rb +22 -0
  28. data/doc/example/smile.png +0 -0
  29. data/doc/example/smile_bounce.rb +44 -0
  30. data/doc/example/smile_move.rb +58 -0
  31. data/doc/example/smile_move_2.rb +78 -0
  32. data/doc/example/sound.rb +101 -0
  33. data/doc/example/state_app.rb +33 -0
  34. data/doc/example/state_keyboard.rb +23 -0
  35. data/doc/example/state_mouse.rb +60 -0
  36. data/doc/key_constants.textile +129 -0
  37. data/doc/key_modifiers.textile +19 -0
  38. data/doc/reference.textile +421 -0
  39. data/lib/tea.rb +34 -0
  40. data/lib/tea/c_bitmap.rb +122 -0
  41. data/lib/tea/c_error.rb +11 -0
  42. data/lib/tea/c_font.rb +302 -0
  43. data/lib/tea/c_sound.rb +144 -0
  44. data/lib/tea/m_color.rb +50 -0
  45. data/lib/tea/m_event.rb +65 -0
  46. data/lib/tea/m_event_app.rb +96 -0
  47. data/lib/tea/m_event_dispatch.rb +54 -0
  48. data/lib/tea/m_event_keyboard.rb +311 -0
  49. data/lib/tea/m_event_mouse.rb +189 -0
  50. data/lib/tea/mix_blitting.rb +31 -0
  51. data/lib/tea/mix_clipping.rb +71 -0
  52. data/lib/tea/mix_grabbing.rb +86 -0
  53. data/lib/tea/mix_image_saving.rb +70 -0
  54. data/lib/tea/mix_primitive.rb +613 -0
  55. data/lib/tea/o_screen.rb +98 -0
  56. metadata +137 -0
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ # This file holds the Error class.
2
+
3
+ #
4
+ module Tea
5
+
6
+ # Error is the exception class raised when Bad Things happen in any of
7
+ # Tea's objects or methods.
8
+ class Error < StandardError
9
+ end
10
+
11
+ end
@@ -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