pixelflut 0.0.10 → 0.2.0

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,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'color'
4
-
5
- module Pixelflut
6
- module Canvas
7
- class Base
8
- attr_accessor :offset_x, :offset_y
9
-
10
- def initialize
11
- clear!
12
- end
13
-
14
- def clear!
15
- @offset_x = @offset_y = 0
16
- @color = Color::Black
17
- end
18
-
19
- def translate(x, y)
20
- ox, oy = @offset_x, @offset_y
21
- @offset_x += x
22
- @offset_y += y
23
- yield
24
- ensure
25
- @offset_x, @offset_y = ox, oy
26
- end
27
-
28
- def color(color)
29
- oc = @color
30
- @color = color
31
- yield
32
- ensure
33
- @color = oc
34
- end
35
-
36
- def []=(x, y, color)
37
- out("PX #{x(x)} #{y(y)} #{color}\n")
38
- end
39
-
40
- def pix(x, y, color = @color)
41
- out("PX #{x(x)} #{y(y)} #{color}\n")
42
- end
43
-
44
- def rect(x1, y1, x2, y2, color = @color)
45
- out("RC #{x(x1)} #{y(y1)} #{x(x2)} #{y(y2)} #{color}\n")
46
- end
47
-
48
- def line(x1, y1, x2, y2, color = @color)
49
- return rect(x1, y1, x2, y2, color) if x1 == x2 || y1 == y2
50
- x, y, curpixel = x1, y1, 0
51
- deltax = (x2 - x1).abs
52
- deltay = (y2 - y1).abs
53
- xinc1 = xinc2 = x2 >= x1 ? 1 : -1
54
- yinc1 = yinc2 = y2 >= y1 ? 1 : -1
55
- if deltax >= deltay
56
- xinc1 = yinc2 = 0
57
- den, numadd, numpixels, num = deltax, deltay, deltax, deltax / 2
58
- else
59
- xinc2 = yinc1 = 0
60
- den, numadd, numpixels, num = deltay, deltax, deltay, deltay / 2
61
- end
62
- while curpixel <= numpixels
63
- num += numadd
64
- if num >= den
65
- num -= den
66
- x += xinc1
67
- y += yinc1
68
- end
69
- x += xinc2
70
- y += yinc2
71
- pix(x, y, color)
72
- curpixel += 1
73
- end
74
- end
75
-
76
- def ascii(x, y, dim = 10, color = @color, pic = nil)
77
- pic ||= yield
78
- sx = x
79
- pic.each_line do |line|
80
- line.chomp!
81
- line.each_char do |c|
82
- rect(x, y, x + dim, y + dim, color) if ' ' != c && '_' != c
83
- x += dim
84
- end
85
- x = sx
86
- y += dim
87
- end
88
- end
89
-
90
- protected
91
-
92
- def x(x)
93
- x + @offset_x
94
- end
95
-
96
- def y(y)
97
- y + @offset_y
98
- end
99
- end
100
- end
101
- 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,32 +0,0 @@
1
- module Pixelflut
2
- module Canvas
3
- class Base
4
- module Color
5
- class << self
6
- def from_rgb(r, g, b)
7
- from_rgba(r, g, b, 0xff)
8
- end
9
-
10
- def from_rgba(r, g, b, a)
11
- as_hex(r) + as_hex(g) + as_hex(b) + as_hex(a)
12
- end
13
-
14
- private
15
-
16
- def as_hex(int)
17
- ret = int.to_s(16)
18
- ret = '0' + ret if 1 == ret.size
19
- ret
20
- end
21
- end
22
-
23
- Black = from_rgba(0, 0, 0, 0xff)
24
- White = from_rgba(0xff, 0xff, 0xff, 0xff)
25
- Red = from_rgba(0xff, 0, 0, 0xff)
26
- Green = from_rgba(0, 0xff, 0, 0xff)
27
- Blue = from_rgba(0, 0, 0xff, 0xff)
28
- Yellow = from_rgba(0xff, 0xff, 0, 0xff)
29
- end
30
- end
31
- end
32
- 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| Socket.new(address, data) }
33
- end
34
- end
35
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
-
5
- module Pixelflut
6
- class Client
7
- class Socket < ::Socket
8
- attr_reader :state
9
-
10
- def initialize(address, data)
11
- super(address.ipv6? ? :INET6 : :INET, :STREAM)
12
- configure
13
- @addr = ::Socket.pack_sockaddr_in(address.ip_port, address.ip_address)
14
- @data = data
15
- @size = data.bytesize
16
- @state = :not_connected
17
- end
18
-
19
- def call
20
- case @state
21
- when :not_connected
22
- do_connect
23
- when :wait_connect
24
- do_wait_for_connect
25
- when :write
26
- do_write
27
- else
28
- close
29
- @state = :closed
30
- end
31
- end
32
-
33
- private
34
-
35
- def do_connect
36
- @state = :wait_writable === connect_nonblock(@addr, exception: false) ? :wait_connect : :write
37
- end
38
-
39
- def do_wait_for_connect
40
- @state = :write unless wait_writable(0.1).nil?
41
- end
42
-
43
- def do_write
44
- written = write_nonblock(@data, exception: false)
45
- return if Symbol === written
46
- @size -= written
47
- return @state = :write_finished if @size <= 0
48
- @data = @data.byteslice(written, @data.bytesize - written)
49
- end
50
-
51
- def configure
52
- self.sync = true
53
- setsockopt(:TCP, :NODELAY, 1)
54
- setsockopt(:SOCKET, :KEEPALIVE, 0)
55
- self.do_not_reverse_lookup = true
56
- end
57
- end
58
- end
59
- end
@@ -1,46 +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 do |color, x, y|
26
- yield(x, y, color.to_color(Magick::AllCompliance, true, 8, true)[1, 8]) unless 0xffff == color.opacity
27
- end
28
- end
29
- end
30
-
31
- def draw(canvas)
32
- each_pixel{ |x, y, rgba| canvas[x, y] = rgba }
33
- end
34
-
35
- private
36
-
37
- def load_images(file_name)
38
- Magick::ImageList.new(file_name)
39
- rescue Magick::ImageMagickError => e
40
- raise(Error, e.message)
41
- end
42
- end
43
- rescue LoadError
44
- Converter::AVAIL = false
45
- end
46
- 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 0 == str.size
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