pixelflut 0.0.10 → 0.2.0

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,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