pixelflut 0.0.13 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'canvas/streamed'
4
- require_relative 'canvas/buffered'
@@ -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
@@ -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
@@ -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
@@ -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