tung-tea 0.2.0 → 0.2.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.
@@ -0,0 +1,45 @@
1
+ # Test that anti-aliased lines work as expected.
2
+ # Anti-aliased lines in replace mode should just overwrite the RGBA of the
3
+ # pixels it affects. In blend mode, the line RGB and destination RGB should be
4
+ # blended according to their ratio, while the final alpha should be the sum of
5
+ # the line and destination alpha values.
6
+
7
+ require 'tea'
8
+
9
+ puts <<TEST
10
+ You should see 16 green lines in a wheel, with a green dot in the center,
11
+ against a grey rectangle.
12
+
13
+ Press any key to exit.
14
+ TEST
15
+
16
+ Tea.init
17
+ Tea::Screen.set_mode 400, 300
18
+
19
+ b = Tea::Bitmap.new(250, 250, 0x00000000)
20
+ CENTER_X = b.w / 2
21
+ CENTER_Y = b.h / 2
22
+ LINE_CENTER_CLEARANCE = 10
23
+ LINE_LENGTH = 100
24
+ LINE_COLOR = 0x00ff0080
25
+ SPOKES = 16
26
+
27
+ Tea::Screen.rect 150, 100, 100, 100, 0x404040ff
28
+
29
+ b.line CENTER_X, CENTER_Y, CENTER_X, CENTER_Y, LINE_COLOR, :antialias => true, :mix => :replace
30
+
31
+ # TODO: Enable lines for all spokes.
32
+ SPOKES.times do |n|
33
+ angle = n * Math::PI * 2 / SPOKES
34
+ x1 = CENTER_X + LINE_CENTER_CLEARANCE * Math.cos(angle)
35
+ y1 = CENTER_Y + LINE_CENTER_CLEARANCE * Math.sin(angle)
36
+ x2 = CENTER_X + (LINE_CENTER_CLEARANCE + LINE_LENGTH) * Math.cos(angle)
37
+ y2 = CENTER_Y + (LINE_CENTER_CLEARANCE + LINE_LENGTH) * Math.sin(angle)
38
+ b.line x1, y1, x2, y2, LINE_COLOR, :antialias => true, :mix => n.even? ? :blend : :replace
39
+ end
40
+
41
+ Tea::Screen.blit b, (Tea::Screen.w - b.w) / 2, (Tea::Screen.h - b.h) / 2
42
+ Tea::Screen.update
43
+ begin
44
+ e = Tea::Event.get(true)
45
+ end until e.class == Tea::App::Exit || e.class == Tea::Kbd::Down
@@ -83,10 +83,10 @@ module Tea
83
83
  # destination pixels.
84
84
  def line(x1, y1, x2, y2, color, options=nil)
85
85
  if options == nil
86
- aa = false
86
+ antialias = false
87
87
  mix = :blend
88
88
  else
89
- aa = options[:antialias] || false
89
+ antialias = options[:antialias] || false
90
90
  mix = options[:mix] || :blend
91
91
 
92
92
  unless [:blend, :replace].include?(mix)
@@ -94,12 +94,34 @@ module Tea
94
94
  end
95
95
  end
96
96
 
97
+ mixer = nil
97
98
  case mix
99
+ when :replace
100
+ mixer = lambda do |buffer, x, y, r, g, b, a, intensity|
101
+ return buffer.map_rgba(r, g, b, a * intensity)
102
+ end
98
103
  when :blend
104
+ mixer = lambda do |buffer, x, y, r, g, b, a, intensity|
105
+ br, bg, bb, ba = buffer.get_rgba(buffer[x, y])
106
+ ai = a * intensity
107
+ ratio = ba > 0 ? ai / ba.to_f : 1
108
+ ratio = 1 if ratio > 1
109
+ fr = br + (r - br) * ratio
110
+ fg = bg + (g - bg) * ratio
111
+ fb = bb + (b - bb) * ratio
112
+ fa = (ba + ai < 255) ? (ba + ai) : 255
113
+ return buffer.map_rgba(fr, fg, fb, fa)
114
+ end
115
+ end
116
+
117
+ if antialias
99
118
  r, g, b, a = primitive_hex_to_rgba(color)
100
- primitive_buffer.draw_line x1, y1, x2, y2, primitive_rgba_to_color(r, g, b, 255), aa, a
101
- when :replace
102
- primitive_buffer.draw_line x1, y1, x2, y2, primitive_color(color), aa
119
+ primitive_aa_line x1, y1, x2, y2, r, g, b, a, mixer
120
+ elsif a == 0xff
121
+ primitive_buffer.draw_line x1, y1, x2, y2, primitive_color(color)
122
+ else
123
+ r, g, b, a = primitive_hex_to_rgba(color)
124
+ primitive_line x1, y1, x2, y2, r, g, b, a, mixer
103
125
  end
104
126
  end
105
127
 
@@ -180,6 +202,211 @@ module Tea
180
202
  primitive_buffer.map_rgba(red, green, blue, alpha)
181
203
  end
182
204
 
205
+ # Fractional part of x, for primitive_aa_line.
206
+ def primitive_fpart(x)
207
+ x - x.truncate
208
+ end
209
+
210
+ # Inverse fractional part of x, for primitive_aa_line.
211
+ def primitive_rfpart(x)
212
+ 1 - primitive_fpart(x)
213
+ end
214
+
215
+ # Run a block with the primitive buffer locked.
216
+ def primitive_buffer_with_lock
217
+ buffer = primitive_buffer
218
+ if SDL::Surface.auto_lock?
219
+ auto_lock = true
220
+ SDL::Surface.auto_lock_off
221
+ end
222
+ buffer.lock if buffer.must_lock?
223
+ begin
224
+ yield
225
+ ensure
226
+ buffer.unlock if buffer.must_lock?
227
+ SDL::Surface.auto_lock_on if auto_lock
228
+ end
229
+ end
230
+
231
+ # Draw a line from (x1, y1) to (x2, y2) of color (red, green, blue). The
232
+ # +alpha+ is passed to the +mixer+ proc to determine how the line and
233
+ # bitmap colours should be mixed.
234
+ #
235
+ # mixer = { |buffer, x, y, red, green, blue, alpha| ... }
236
+ def primitive_line(x1, y1, x2, y2, red, green, blue, alpha, mixer)
237
+
238
+ buffer = primitive_buffer
239
+ dx = x2 - x1
240
+ dy = y2 - y1
241
+
242
+ case
243
+ when dx == 0 && dy == 0 # point
244
+ buffer[x1, y1] = mixer.call buffer, x1, y1, red, green, blue, alpha, 1.0
245
+ when dx == 0 && dy != 0 # vertical line
246
+ primitive_buffer_with_lock do
247
+ for y in (y1.to_i)..(y2.to_i)
248
+ buffer[x1, y] = mixer.call buffer, x1, y, red, green, blue, alpha, 1.0
249
+ end
250
+ end
251
+ when dx != 0 && dy == 0 # horizontal line
252
+ primitive_buffer_with_lock do
253
+ for x in (x1.to_i)..(x2.to_i)
254
+ buffer[x, y1] = mixer.call buffer, x, y1, red, green, blue, alpha, 1.0
255
+ end
256
+ end
257
+ else # Use Bresenham's line algorithm, from John Hall's Programming Linux Games.
258
+
259
+ # Figure out the x and y spans of the line.
260
+ xspan = dx + 1
261
+ yspan = dy + 1
262
+
263
+ # Figure out the correct increment for the major axis.
264
+ # Account for negative spans (x2 < x1, for instance).
265
+ if xspan < 0
266
+ xinc = -1
267
+ xspan = -xspan
268
+ else
269
+ xinc = 1
270
+ end
271
+ if yspan < 0
272
+ yinc = -1
273
+ yspan = -yspan
274
+ else
275
+ yinc = 1
276
+ end
277
+
278
+ x = x1
279
+ y = y1
280
+ error = 0
281
+
282
+ primitive_buffer_with_lock do
283
+ if xspan < yspan # Draw a mostly vertical line.
284
+ for step in 0..yspan
285
+ buffer[x, y] = mixer.call buffer, x, y, red, green, blue, alpha, 1.0
286
+ error += xspan
287
+ if error >= yspan
288
+ x += xinc
289
+ error -= yspan
290
+ end
291
+ y += yinc
292
+ end
293
+ else # Draw a mostly horizontal line.
294
+ for step in 0..xspan
295
+ buffer[x, y] = mixer.call buffer, x, y, red, green, blue, alpha, 1.0
296
+ error += yspan
297
+ if error >= xspan
298
+ y += yinc
299
+ error -= xspan
300
+ end
301
+ x += xinc
302
+ end
303
+ end
304
+ end
305
+
306
+ end
307
+ end
308
+
309
+ # Draw an antialiased line from (x1, y1) to (x2, y2) of colour (red, green,
310
+ # blue). The +alpha+ is passed to the +mixer+ proc to determine how the
311
+ # line and bitmap colours should be mixed.
312
+ #
313
+ # mixer = { |buffer, x, y, red, green, blue, alpha, intensity| ... }
314
+ def primitive_aa_line(x1, y1, x2, y2, red, green, blue, alpha, mixer)
315
+
316
+ buffer = primitive_buffer
317
+ dx = x2 - x1
318
+ dy = y2 - y1
319
+
320
+ case
321
+ when dx == 0 && dy == 0 # point
322
+ buffer[x1, y1] = mixer.call buffer, x1, y1, red, green, blue, alpha, 1.0
323
+ when dx == 0 && dy != 0 # vertical line
324
+ primitive_buffer_with_lock do
325
+ for y in (y1.to_i)..(y2.to_i)
326
+ buffer[x1, y] = mixer.call buffer, x1, y, red, green, blue, alpha, 1.0
327
+ end
328
+ end
329
+ when dx != 0 && dy == 0 # horizontal line
330
+ primitive_buffer_with_lock do
331
+ for x in (x1.to_i)..(x2.to_i)
332
+ buffer[x, y1] = mixer.call buffer, x, y1, red, green, blue, alpha, 1.0
333
+ end
334
+ end
335
+ else # Use Xiaolin Wu's line algorithm, described on Wikipedia.
336
+
337
+ if dx.abs > dy.abs # Draw a mostly horizontal line.
338
+ if x2 < x1
339
+ x1, x2 = x2, x1
340
+ y1, y2 = y2, y1
341
+ end
342
+ gradient = dy.to_f / dx
343
+
344
+ # Handle first endpoint.
345
+ xend = x1.round
346
+ yend = y1 + gradient * (xend - x1)
347
+ xgap = primitive_rfpart(x1 + 0.5)
348
+ xpxl1 = xend # This will be used in the main loop.
349
+ ypxl1 = yend.truncate
350
+ buffer[xpxl1, ypxl1] = mixer.call buffer, xpxl1, ypxl1, red, green, blue, alpha, primitive_rfpart(yend) * xgap
351
+ buffer[xpxl1, ypxl1 + 1] = mixer.call buffer, xpxl1, ypxl1 + 1, red, green, blue, alpha, primitive_fpart(yend) * xgap
352
+ intery = yend + gradient # First y-intersection for the main loop.
353
+
354
+ # Handle second endpoint.
355
+ xend = x2.round
356
+ yend = y2 + gradient * (xend - x2)
357
+ xgap = primitive_fpart(x2 + 0.5)
358
+ xpxl2 = xend # This will be used in the main loop.
359
+ ypxl2 = yend.truncate
360
+ buffer[xpxl2, ypxl2] = mixer.call buffer, xpxl2, ypxl2, red, green, blue, alpha, primitive_rfpart(yend) * xgap
361
+ buffer[xpxl2, ypxl2 + 1] = mixer.call buffer, xpxl2, ypxl2 + 1, red, green, blue, alpha, primitive_fpart(yend) * xgap
362
+
363
+ primitive_buffer_with_lock do
364
+ for x in (xpxl1 + 1)..(xpxl2 - 1)
365
+ intery_int = intery.truncate
366
+ buffer[x, intery_int] = mixer.call buffer, x, intery_int, red, green, blue, alpha, primitive_rfpart(intery)
367
+ buffer[x, intery_int + 1] = mixer.call buffer, x, intery_int + 1, red, green, blue, alpha, primitive_fpart(intery)
368
+ intery += gradient
369
+ end
370
+ end
371
+ else # Draw a mostly vertical line.
372
+ if y2 < y1
373
+ y1, y2 = y2, y1
374
+ x1, x2 = x2, x1
375
+ end
376
+ gradient = dx.to_f / dy
377
+
378
+ # Handle first endpoint.
379
+ yend = y1.round
380
+ xend = x1 + gradient * (yend - y1)
381
+ ygap = primitive_rfpart(y1 + 0.5)
382
+ ypxl1 = yend # This will be used in the main loop.
383
+ xpxl1 = xend.truncate
384
+ buffer[xpxl1, ypxl1] = mixer.call buffer, xpxl1, ypxl1, red, green, blue, alpha, primitive_rfpart(xend) * ygap
385
+ buffer[xpxl1 + 1, ypxl1] = mixer.call buffer, xpxl1 + 1, ypxl1, red, green, blue, alpha, primitive_fpart(xend) * ygap
386
+ interx = xend + gradient # First x-intersection for the main loop.
387
+
388
+ # Handle second endpoint.
389
+ yend = y2.round
390
+ xend = x2 + gradient * (yend - y2)
391
+ ygap = primitive_fpart(y2 + 0.5)
392
+ ypxl2 = yend # This will be used in the main loop.
393
+ xpxl2 = xend.truncate
394
+ buffer[xpxl2, ypxl2] = mixer.call buffer, xpxl2, ypxl2, red, green, blue, alpha, primitive_rfpart(xend) * ygap
395
+ buffer[xpxl2 + 1, ypxl2] = mixer.call buffer, xpxl2 + 1, ypxl2, red, green, blue, alpha, primitive_fpart(xend) * ygap
396
+
397
+ primitive_buffer_with_lock do
398
+ for y in (ypxl1 + 1)..(ypxl2 - 1)
399
+ interx_int = interx.truncate
400
+ buffer[interx_int, y] = mixer.call buffer, interx_int, y, red, green, blue, alpha, primitive_rfpart(interx)
401
+ buffer[interx_int + 1, y] = mixer.call buffer, interx_int + 1, y, red, green, blue, alpha, primitive_fpart(interx)
402
+ interx += gradient
403
+ end
404
+ end
405
+ end
406
+
407
+ end
408
+ end
409
+
183
410
  end
184
411
 
185
412
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tung-tea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-31 00:00:00 -07:00
12
+ date: 2009-08-02 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -44,6 +44,7 @@ files:
44
44
  - doc/example/event_mouse.rb
45
45
  - doc/example/init.rb
46
46
  - doc/example/lines.rb
47
+ - doc/example/lines_aa.rb
47
48
  - doc/example/lines_alpha.rb
48
49
  - doc/example/point.rb
49
50
  - doc/example/rect.rb