pixelflut 0.0.13 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/README.md +15 -77
- data/bin/pxf +58 -22
- data/bin/pxf-info +26 -49
- data/gems.rb +1 -1
- data/lib/pixelflut.rb +55 -6
- data/lib/pixelflut/image.rb +32 -0
- data/lib/pixelflut/sender.rb +29 -0
- data/lib/pixelflut/version.rb +3 -1
- data/pixelflut.gemspec +18 -25
- data/rakefile.rb +5 -3
- metadata +38 -45
- data/TODO.md +0 -11
- data/bin/pxf-convert +0 -55
- data/bin/pxf-generate +0 -35
- data/bin/pxf-help +0 -23
- data/bin/pxf-send +0 -44
- data/bin/pxf-server +0 -64
- data/bin/pxf-version +0 -17
- data/lib/pixelflut/app.rb +0 -81
- data/lib/pixelflut/canvas.rb +0 -4
- data/lib/pixelflut/canvas/base.rb +0 -99
- data/lib/pixelflut/canvas/buffered.rb +0 -27
- data/lib/pixelflut/canvas/streamed.rb +0 -20
- data/lib/pixelflut/client.rb +0 -35
- data/lib/pixelflut/client/socket.rb +0 -93
- data/lib/pixelflut/converter.rb +0 -53
- data/lib/pixelflut/server.rb +0 -58
- data/lib/pixelflut/server/configuration.rb +0 -26
- data/lib/pixelflut/server/connection.rb +0 -98
- data/lib/pixelflut/text_image.rb +0 -89
data/lib/pixelflut/canvas.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
module Canvas
|
5
|
-
class Base
|
6
|
-
attr_accessor :offset_x, :offset_y
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
clear!
|
10
|
-
end
|
11
|
-
|
12
|
-
def clear!
|
13
|
-
@offset_x = @offset_y = 0
|
14
|
-
@color = 'ffffffff'
|
15
|
-
end
|
16
|
-
|
17
|
-
def translate(x, y)
|
18
|
-
ox, oy = @offset_x, @offset_y
|
19
|
-
@offset_x += x
|
20
|
-
@offset_y += y
|
21
|
-
yield
|
22
|
-
ensure
|
23
|
-
@offset_x, @offset_y = ox, oy
|
24
|
-
end
|
25
|
-
|
26
|
-
def color(color)
|
27
|
-
oc = @color
|
28
|
-
@color = color
|
29
|
-
yield
|
30
|
-
ensure
|
31
|
-
@color = oc
|
32
|
-
end
|
33
|
-
|
34
|
-
def []=(x, y, color)
|
35
|
-
out("PX #{x(x)} #{y(y)} #{color}\n")
|
36
|
-
end
|
37
|
-
|
38
|
-
def pix(x, y, color = @color)
|
39
|
-
out("PX #{x(x)} #{y(y)} #{color}\n")
|
40
|
-
end
|
41
|
-
|
42
|
-
def rect(x1, y1, x2, y2, color = @color)
|
43
|
-
out("RC #{x(x1)} #{y(y1)} #{x(x2)} #{y(y2)} #{color}\n")
|
44
|
-
end
|
45
|
-
|
46
|
-
def line(x1, y1, x2, y2, color = @color)
|
47
|
-
return rect(x1, y1, x2, y2, color) if x1 == x2 || y1 == y2
|
48
|
-
x, y, curpixel = x1, y1, 0
|
49
|
-
deltax = (x2 - x1).abs
|
50
|
-
deltay = (y2 - y1).abs
|
51
|
-
xinc1 = xinc2 = x2 >= x1 ? 1 : -1
|
52
|
-
yinc1 = yinc2 = y2 >= y1 ? 1 : -1
|
53
|
-
if deltax >= deltay
|
54
|
-
xinc1 = yinc2 = 0
|
55
|
-
den, numadd, numpixels, num = deltax, deltay, deltax, deltax / 2
|
56
|
-
else
|
57
|
-
xinc2 = yinc1 = 0
|
58
|
-
den, numadd, numpixels, num = deltay, deltax, deltay, deltay / 2
|
59
|
-
end
|
60
|
-
while curpixel <= numpixels
|
61
|
-
num += numadd
|
62
|
-
if num >= den
|
63
|
-
num -= den
|
64
|
-
x += xinc1
|
65
|
-
y += yinc1
|
66
|
-
end
|
67
|
-
x += xinc2
|
68
|
-
y += yinc2
|
69
|
-
pix(x, y, color)
|
70
|
-
curpixel += 1
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def ascii(x, y, dim = 10, color = @color, pic = nil)
|
75
|
-
pic ||= yield
|
76
|
-
sx = x
|
77
|
-
pic.each_line do |line|
|
78
|
-
line.chomp!
|
79
|
-
line.each_char do |c|
|
80
|
-
rect(x, y, x + dim, y + dim, color) if ' ' != c && '_' != c
|
81
|
-
x += dim
|
82
|
-
end
|
83
|
-
x = sx
|
84
|
-
y += dim
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
protected
|
89
|
-
|
90
|
-
def x(x)
|
91
|
-
x + @offset_x
|
92
|
-
end
|
93
|
-
|
94
|
-
def y(y)
|
95
|
-
y + @offset_y
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
module Canvas
|
7
|
-
class Buffered < Base
|
8
|
-
def clear!
|
9
|
-
@lines = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def each(&block)
|
13
|
-
block ? @lines.each(&block) : to_enum(__method__)
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
@lines.join
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def out(str)
|
23
|
-
@lines << str
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
module Canvas
|
7
|
-
class Streamed < Base
|
8
|
-
def initialize(stream = $stdout)
|
9
|
-
super()
|
10
|
-
@stream = stream
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def out(str)
|
16
|
-
@stream.print(str)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/pixelflut/client.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'client/socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Client
|
7
|
-
def initialize(host, port, data, socket_count)
|
8
|
-
@sockets = create_sockets(Addrinfo.tcp(host, port), sliced(data, socket_count))
|
9
|
-
end
|
10
|
-
|
11
|
-
def run
|
12
|
-
loop{ break if call }
|
13
|
-
end
|
14
|
-
|
15
|
-
def call
|
16
|
-
@sockets.size == @sockets.count do |socket|
|
17
|
-
socket.call
|
18
|
-
socket.state == :closed
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def sliced(data, count)
|
25
|
-
data = data.split("\n") if data.is_a?(String)
|
26
|
-
data = data.each_slice(data.size / count).to_a
|
27
|
-
data[-2] += data.pop unless (data.size % count).zero?
|
28
|
-
data.map!(&:join)
|
29
|
-
end
|
30
|
-
|
31
|
-
def create_sockets(address, slices)
|
32
|
-
slices.map{ |data| NonblockSocket.new(address, data) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Client
|
7
|
-
class Socket < ::Socket
|
8
|
-
def initialize(address)
|
9
|
-
super(address.ipv6? ? :INET6 : :INET, :STREAM)
|
10
|
-
@addr = ::Socket.pack_sockaddr_in(address.ip_port, address.ip_address)
|
11
|
-
configure
|
12
|
-
end
|
13
|
-
|
14
|
-
def write_with_timout(data, timeout)
|
15
|
-
return true if write_nonblock(data, exception: false) == data.bytesize
|
16
|
-
return false unless wait_writable(timeout)
|
17
|
-
write_nonblock(data, exception: false) == data.bytesize
|
18
|
-
end
|
19
|
-
|
20
|
-
def readline_with_timeout(timeout)
|
21
|
-
deadline = Time.now + timeout
|
22
|
-
ret = ''
|
23
|
-
loop do
|
24
|
-
got = read_nonblock(16, exception: false)
|
25
|
-
if got == :wait_readable
|
26
|
-
remaining_time = deadline - Time.now
|
27
|
-
return nil if remaining_time <= 0 || wait_readable(remaining_time).nil?
|
28
|
-
next
|
29
|
-
end
|
30
|
-
idx = got.index("\n")
|
31
|
-
next ret += got unless idx
|
32
|
-
return ret + got[0, idx]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def connect?
|
37
|
-
:wait_writable != connect_nonblock(@addr, exception: false)
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def configure
|
43
|
-
self.sync = true
|
44
|
-
setsockopt(:TCP, :NODELAY, 1)
|
45
|
-
setsockopt(:SOCKET, :KEEPALIVE, 0)
|
46
|
-
self.do_not_reverse_lookup = true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class NonblockSocket < Socket
|
51
|
-
attr_reader :state
|
52
|
-
|
53
|
-
def initialize(address, data)
|
54
|
-
super(address)
|
55
|
-
@state = :not_connected
|
56
|
-
@data = data
|
57
|
-
@size = data.bytesize
|
58
|
-
end
|
59
|
-
|
60
|
-
def call
|
61
|
-
case @state
|
62
|
-
when :not_connected
|
63
|
-
do_connect
|
64
|
-
when :wait_connect
|
65
|
-
do_wait_for_connect
|
66
|
-
when :write
|
67
|
-
do_write
|
68
|
-
else
|
69
|
-
close
|
70
|
-
@state = :closed
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def do_connect
|
77
|
-
@state = connect? ? :write : :wait_connect
|
78
|
-
end
|
79
|
-
|
80
|
-
def do_wait_for_connect
|
81
|
-
@state = :not_connected unless wait_writable(0.1).nil?
|
82
|
-
end
|
83
|
-
|
84
|
-
def do_write
|
85
|
-
written = write_nonblock(@data, exception: false)
|
86
|
-
return written if Symbol === written
|
87
|
-
@size -= written
|
88
|
-
return @state = :write_finished if @size <= 0
|
89
|
-
@data = @data.byteslice(written, @data.bytesize - written)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
data/lib/pixelflut/converter.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
class Converter
|
5
|
-
Error = Class.new(RuntimeError)
|
6
|
-
end
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'rmagick'
|
10
|
-
|
11
|
-
class Converter
|
12
|
-
AVAIL = true
|
13
|
-
|
14
|
-
def initialize(file_name)
|
15
|
-
@images = load_images(file_name)
|
16
|
-
end
|
17
|
-
|
18
|
-
def resize_to(width, height = nil)
|
19
|
-
@images.each{ |image| image.resize_to_fit!(width, height) }
|
20
|
-
end
|
21
|
-
|
22
|
-
def each_pixel
|
23
|
-
return to_enum(__method__) unless block_given?
|
24
|
-
@images.each do |image|
|
25
|
-
image.each_pixel{ |color, x, y| yield(x, y, color) }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def each_rgba_pixel
|
30
|
-
return to_enum(__method__) unless block_given?
|
31
|
-
each_pixel{ |x, y, color| yield(x, y, as_rgba(color)) unless 0xffff == color.opacity }
|
32
|
-
end
|
33
|
-
|
34
|
-
def draw(canvas)
|
35
|
-
each_rgba_pixel{ |x, y, rgba| canvas[x, y] = rgba }
|
36
|
-
end
|
37
|
-
|
38
|
-
def as_rgba(color)
|
39
|
-
color.to_color(Magick::AllCompliance, true, 8, true)[1, 8]
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def load_images(file_name)
|
45
|
-
Magick::ImageList.new(file_name)
|
46
|
-
rescue Magick::ImageMagickError => e
|
47
|
-
raise(Error, e.message)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
rescue LoadError
|
51
|
-
Converter::AVAIL = false
|
52
|
-
end
|
53
|
-
end
|
data/lib/pixelflut/server.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
require_relative 'server/connection'
|
5
|
-
require_relative 'server/configuration'
|
6
|
-
|
7
|
-
module Pixelflut
|
8
|
-
class Server
|
9
|
-
attr_reader :config
|
10
|
-
|
11
|
-
def initialize(canvas, config = Configuration.default)
|
12
|
-
@canvas, @config = canvas, config
|
13
|
-
@socket, @connections = nil, {}
|
14
|
-
@peers = Hash.new{ |h, k| h[k] = 0 }
|
15
|
-
@ccfg = Connection::Configuration.new(
|
16
|
-
keep_alive_time: config.keep_alive_time,
|
17
|
-
read_buffer_size: config.read_buffer_size,
|
18
|
-
command_limit: config.command_limit,
|
19
|
-
canvas: canvas,
|
20
|
-
size_result: "SIZE #{canvas.width} #{canvas.height}\n".freeze,
|
21
|
-
on_end: ->(conn){ @peers[conn.peeraddr] -= 1 if @connections.delete(conn) }
|
22
|
-
).freeze
|
23
|
-
end
|
24
|
-
|
25
|
-
def connection_count
|
26
|
-
@connections.size
|
27
|
-
end
|
28
|
-
|
29
|
-
def update
|
30
|
-
return create_socket unless @socket
|
31
|
-
incoming = @socket.accept_nonblock(exception: false)
|
32
|
-
create_connection(incoming) unless Symbol === incoming
|
33
|
-
@connections.keys.each(&:update)
|
34
|
-
end
|
35
|
-
|
36
|
-
def run
|
37
|
-
Thread.new do
|
38
|
-
loop{ update }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def create_connection(socket)
|
45
|
-
peeraddr = socket.peeraddr(false)[-1]
|
46
|
-
count = @peers[peeraddr] + 1
|
47
|
-
return socket.close if count > @config.peer_limit
|
48
|
-
@peers[peeraddr] = count
|
49
|
-
con = Connection.new(socket, peeraddr, @ccfg)
|
50
|
-
@connections[con] = con
|
51
|
-
end
|
52
|
-
|
53
|
-
def create_socket
|
54
|
-
@socket = @config.host ? TCPServer.new(@config.host, @config.port) : TCPServer.new(@config.port)
|
55
|
-
@socket.listen(255)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
class Server
|
5
|
-
Configuration = Struct.new(
|
6
|
-
:host,
|
7
|
-
:port,
|
8
|
-
:keep_alive_time,
|
9
|
-
:read_buffer_size,
|
10
|
-
:command_limit,
|
11
|
-
:peer_limit
|
12
|
-
) do
|
13
|
-
def self.default
|
14
|
-
new(nil, 1234, 1, 1024, 10, 8)
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_s
|
18
|
-
"bind: #{host}:#{port}"\
|
19
|
-
", keep-alive-time: #{keep_alive_time}"\
|
20
|
-
", read-buffer-size: #{read_buffer_size}"\
|
21
|
-
", command-limit: #{command_limit}"\
|
22
|
-
", peer-limit: #{peer_limit}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Server
|
7
|
-
class Connection
|
8
|
-
Configuration = Struct.new(
|
9
|
-
:keep_alive_time,
|
10
|
-
:read_buffer_size,
|
11
|
-
:command_limit,
|
12
|
-
:canvas,
|
13
|
-
:size_result,
|
14
|
-
:on_end,
|
15
|
-
keyword_init: true
|
16
|
-
)
|
17
|
-
|
18
|
-
attr_reader :peeraddr
|
19
|
-
|
20
|
-
def initialize(socket, peeraddr, config)
|
21
|
-
@socket, @peeraddr, @config = socket, peeraddr, config
|
22
|
-
@last_tm, @buffer = Time.now.to_f, ''
|
23
|
-
end
|
24
|
-
|
25
|
-
def close(_reason)
|
26
|
-
socket, @socket = @socket, nil
|
27
|
-
return unless socket
|
28
|
-
socket.close
|
29
|
-
@config.on_end.call(self)
|
30
|
-
false
|
31
|
-
end
|
32
|
-
|
33
|
-
def update
|
34
|
-
index = @buffer.index("\n")
|
35
|
-
return process_loop(index) if index
|
36
|
-
read_size = @config.read_buffer_size - @buffer.size
|
37
|
-
return close(:buffer_exceeded) if read_size <= 0
|
38
|
-
str = @socket.recv_nonblock(read_size, exception: false)
|
39
|
-
now = Time.now.to_f
|
40
|
-
return (now - @last_tm > @config.keep_alive_time ? close(:timeout) : nil) if Symbol === str
|
41
|
-
return close(:closed_by_peer) if str.size.zero?
|
42
|
-
@buffer += str
|
43
|
-
@last_tm = now
|
44
|
-
rescue Errno::ECONNRESET
|
45
|
-
close(:closed_by_peer)
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def next_command(index)
|
51
|
-
@buffer = @buffer[index, @buffer.size - index]
|
52
|
-
@last_tm = Time.now.to_f
|
53
|
-
true
|
54
|
-
end
|
55
|
-
|
56
|
-
def command_size(index)
|
57
|
-
@socket.sendmsg_nonblock(@config.size_result)
|
58
|
-
next_command(index)
|
59
|
-
end
|
60
|
-
|
61
|
-
def command_px(command, index)
|
62
|
-
_, x, y, color = command.split(' ', 4)
|
63
|
-
return close(:color_expected) unless color
|
64
|
-
@config.canvas[x.to_i, y.to_i] = color
|
65
|
-
next_command(index)
|
66
|
-
end
|
67
|
-
|
68
|
-
def command_rc(command, index)
|
69
|
-
_, x1, y1, x2, y2, color = command.split(' ', 6)
|
70
|
-
return close(:color_expected) unless color
|
71
|
-
@config.canvas.draw_rect(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
72
|
-
next_command(index)
|
73
|
-
end
|
74
|
-
|
75
|
-
def process_loop(index)
|
76
|
-
command_count = @config.command_limit
|
77
|
-
while process_buffer(index)
|
78
|
-
index = @buffer.index("\n") or return
|
79
|
-
command_count -= 1
|
80
|
-
break if command_count <= 0
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def process_buffer(index)
|
85
|
-
return close(:max_command_size_exceeded) if index > 31 # 'RC 9999 9999 9999 9999 RRGGBBAA'.size
|
86
|
-
command = @buffer[0, index]
|
87
|
-
index += 1
|
88
|
-
return command_px(command, index) if command.start_with?('PX ')
|
89
|
-
return command_rc(command, index) if command.start_with?('RC ')
|
90
|
-
return close(:quit) if command == 'QUIT'
|
91
|
-
return command_size(index) if command == 'SIZE'
|
92
|
-
close(:bad_command)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
private_constant(:Connection)
|
97
|
-
end
|
98
|
-
end
|