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.
- data/doc/example/lines_aa.rb +45 -0
- data/lib/tea/m_primitive_drawing.rb +232 -5
- metadata +3 -2
@@ -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
|
-
|
86
|
+
antialias = false
|
87
87
|
mix = :blend
|
88
88
|
else
|
89
|
-
|
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
|
-
|
101
|
-
|
102
|
-
primitive_buffer.draw_line x1, y1, x2, y2, primitive_color(color)
|
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.
|
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-
|
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
|