pixelflut 0.0.13 → 0.1.2

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.
@@ -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
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pixelflut
4
- class TextImage
5
- attr_reader :columns, :rows, :to_blob, :changes
6
-
7
- def initialize(width, height)
8
- @columns, @rows = width, height
9
- @row_inc = width * 4
10
- clear
11
- end
12
-
13
- alias width columns
14
- alias height rows
15
-
16
- def clear
17
- @to_blob = Black * (@columns * @rows)
18
- @changes = 1
19
- end
20
-
21
- def changed
22
- @changes = 0
23
- self
24
- end
25
-
26
- # def [](x, y)
27
- # @@to_blob[(4 * (x + @columns * y)) % @data.size, 4].bytes.map! do |b|
28
- # b = b.to_s(16)
29
- # b = '0' + b if b.size == 1
30
- # b
31
- # end.join
32
- # end
33
-
34
- def []=(x, y, rrggbbaa)
35
- @to_blob[(4 * (x + @columns * y)) % @to_blob.size, 4] = as_color(rrggbbaa)
36
- @changes += 1
37
- end
38
-
39
- def draw_rect(x1, y1, x2, y2, rrggbbaa)
40
- x1, x2 = x2, x1 if x1 > x2
41
- y1, y2 = y2, y1 if y1 > y2
42
- color = as_color(rrggbbaa)
43
- pos = (4 * (x1 + @columns * y1)) % @to_blob.size
44
- pattern = color * (x2 - x1 + 1)
45
- (y2 - y1 + 1).times do
46
- @to_blob[pos, pattern.size] = pattern
47
- pos += @row_inc
48
- end
49
- @changes += 1
50
- end
51
-
52
- private
53
-
54
- ZZ = 0.chr.freeze
55
- FF = 0xff.chr.freeze
56
- Black = (ZZ + ZZ + ZZ + FF).freeze
57
-
58
- def as_color(rrggbbaa)
59
- case rrggbbaa.size
60
- when 3 # RGB
61
- (rrggbbaa[0] * 2).to_i(16).chr +
62
- (rrggbbaa[1] * 2).to_i(16).chr +
63
- (rrggbbaa[2] * 2).to_i(16).chr +
64
- FF
65
- when 4 # RGBA
66
- (rrggbbaa[0] * 2).to_i(16).chr +
67
- (rrggbbaa[1] * 2).to_i(16).chr +
68
- (rrggbbaa[2] * 2).to_i(16).chr +
69
- (rrggbbaa[3] * 2).to_i(16).chr
70
- when 6 # RRGGBB
71
- (rrggbbaa[0, 2]).to_i(16).chr +
72
- (rrggbbaa[2, 2]).to_i(16).chr +
73
- (rrggbbaa[4, 2]).to_i(16).chr +
74
- FF
75
- when 8 # RRGGBBAA
76
- (rrggbbaa[0, 2]).to_i(16).chr +
77
- (rrggbbaa[2, 2]).to_i(16).chr +
78
- (rrggbbaa[4, 2]).to_i(16).chr +
79
- (rrggbbaa[6, 2]).to_i(16).chr
80
- else
81
- Black
82
- end
83
- end
84
-
85
- # def pos(x, y)
86
- # (4 * (x + @columns * y)) % @data.size
87
- # end
88
- end
89
- end