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.
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