pixelflut 0.0.4

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9baf1f1bc33ff7224d647d35d142a7688c9bdc98d3c1195fbc247dfa0bd1c68f
4
+ data.tar.gz: 98056e9d11872f5856de38e2fcfe3677d55a117cf9850e0ff62c0f862a8fdd73
5
+ SHA512:
6
+ metadata.gz: 5faac48d602c1d8bdc07b1786b5f74732a34c79370f140769ff3dcbc48156d8f433d05c7a62fbc353ca7bee88d697a820dc192c00a694f2f43df9dfe0c606fe7
7
+ data.tar.gz: e337ed50dfe5e2192f0ee6c16568b8dba2b93a6376ba2e090df8f249a46390e3101797ba7c63f3ebeeeca054453191540a08b27d2c531b107b0338f982961189
@@ -0,0 +1,4 @@
1
+ _local/
2
+ tmp/
3
+ pkg/
4
+ gems.locked
@@ -0,0 +1,56 @@
1
+ # Pixelflut
2
+
3
+ A Pixelflut server written in Ruby.
4
+
5
+ ## Description
6
+
7
+ Based on the idea of a simple server protocol to collaborate on a shared canvas named [Pixel Flut](https://cccgoe.de/wiki/Pixelflut) this gem implements a Ruby version.
8
+
9
+ This gem is an open experiment to write a fast server and canvas in Ruby. You are welcome to fork or create pull requests to find the fastest solution!
10
+
11
+ The gem includes some tools to develop Pixelflut clients and pre-processors.
12
+
13
+ ### Installation
14
+
15
+ Use [Bundler](http://gembundler.com/) to install the gem:
16
+
17
+ ```bash
18
+ $ gem install pixelflut
19
+ ```
20
+
21
+ Now the `pxf` command offers the complete functionality.
22
+
23
+ ### General Help
24
+
25
+ You'll find some help on the command line:
26
+
27
+ ```bash
28
+ $ pxf
29
+ usage: pxf command [options]
30
+
31
+ valid commands:
32
+ generate Execute given generator FILEs.
33
+ help Print help for given COMMAND.
34
+ server Start Pixelflut server.
35
+ version Print version information.
36
+ ```
37
+
38
+ ### Start a server
39
+
40
+ Starting the server on default port `1234` and open a drawing screen is quite simple:
41
+
42
+ ```bash
43
+ $ pxf server
44
+ ```
45
+
46
+ With these options you can configure the server:
47
+
48
+ ```
49
+ -b, --bind ADDR bind to given address
50
+ -p, --port PORT select port(default: 1234)
51
+ -k, --keep-alive set maximum keep-alive time
52
+ -r, --read_buffer SIZE set read buffer size (default: 1024)
53
+ -w, --width WIDTH set canvas width (default: 800)
54
+ -h, --height HEIGHT set canvas height (default: 600)
55
+ -f, --[no-]fullscreen run in fullscreen mode
56
+ ```
data/bin/pxf ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def bin_root
5
+ File.realdirpath('../', __FILE__).freeze
6
+ end
7
+
8
+ def find_commands
9
+ Hash[Dir.glob(File.join(bin_root, 'pxf-*')).map!{ |cmd| [File.basename(cmd)[4..-1], cmd] }]
10
+ end
11
+
12
+ def err(msg)
13
+ $stderr.puts("pxf: #{msg}")
14
+ exit 1
15
+ end
16
+
17
+ def general_help
18
+ puts('usage: pxf command [options]', nil, 'valid commands:')
19
+ map = find_commands.transform_values!{ |cmd| %x(#{cmd} --short-help) }
20
+ cmds = map.keys.sort!
21
+ len = cmds.max_by(&:size).size + 3
22
+ cmds.each do |cmd|
23
+ print cmd.ljust(len)
24
+ puts(map[cmd])
25
+ end
26
+ exit
27
+ end
28
+
29
+ general_help if ARGV.empty? || ARGV[0] == '--help' || ARGV[0] == '-h'
30
+ cmd = ARGV.shift
31
+ fname = File.join(bin_root, 'pxf-' + cmd)
32
+ err("no such command - #{cmd}") unless File.executable?(fname)
33
+ exec(fname, *ARGV)
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def help(short)
5
+ puts('usage: pxf generate FILE [FILE2, ...]', nil) unless short
6
+ puts('Execute given generator FILEs.')
7
+ end
8
+
9
+ def extract_options(args)
10
+ files, options = [], {}
11
+ while !args.empty?
12
+ arg = args.shift
13
+ next files << arg unless arg[0] == '-'
14
+ next options.merge!(Hash[arg[1..-1].chars.map!{ |c| [c.to_sym, true] }]) unless arg[1] == '-'
15
+ options[arg[2..-1].to_sym] = args.shift
16
+ end
17
+ args[0, 0] = files
18
+ options
19
+ end
20
+
21
+ case ARGV[0]
22
+ when '--help'
23
+ help(false)
24
+ when '--short-help'
25
+ help(true)
26
+ else
27
+ $options = extract_options(ARGV)
28
+ require File.realdirpath('../../lib/pixelflut/canvas/streamed.rb', __FILE__)
29
+ begin
30
+ ARGV.each{ |file| Pixelflut::Canvas::Streamed.new.instance_eval(IO.read(file), file, 0) }
31
+ print "QUIT\n"
32
+ rescue Interrupt
33
+ exit
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def help(short)
5
+ puts('usage: pxf help COMMAND', nil) unless short
6
+ puts('Print help for given COMMAND.')
7
+ end
8
+
9
+ def err(msg)
10
+ $stderr.puts("pxf: #{msg}")
11
+ exit 1
12
+ end
13
+
14
+ case ARGV[0]
15
+ when '--help'
16
+ help(false)
17
+ when '--short-help'
18
+ help(true)
19
+ else
20
+ root = File.realdirpath('../', __FILE__).freeze
21
+ exec(File.join(root, 'pxf')) if ARGV.empty?
22
+ ARGV.each do |cmd|
23
+ fname = File.join(root, 'pxf-' + cmd)
24
+ err("no such command - #{cmd}") unless File.executable?(fname)
25
+ system(fname, '--help')
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def options(cfg)
5
+ require 'optparse'
6
+ OptionParser.new do |opts|
7
+ opts.summary_indent = ' '
8
+ opts.banner = 'usage: pxf server [options]'
9
+ opts.separator(nil)
10
+ opts.separator('valid options:')
11
+ opts.on('-b', '--bind ADDR', String, 'bind to given address'){ |v| cfg.server.host = v }
12
+ opts.on('-p', '--port PORT', Integer, 'select port(default: 1234)'){ |v| cfg.server.port = v }
13
+ opts.on('-k', '--keep-alive', Float, 'set maximum keep-alive time'){ |v| cfg.server.keep_alive_time = v }
14
+ opts.on('-r', '--read_buffer SIZE', Integer, 'set read buffer size (default: 1024)'){ |v| cfg.server.read_buffer_size = v }
15
+ opts.on('-w', '--width WIDTH', Integer, 'set canvas width (default: 800)'){ |v| cfg.width = v }
16
+ opts.on('-h', '--height HEIGHT', Integer, 'set canvas height (default: 600)'){ |v| cfg.height = v }
17
+ opts.on('-f', '--[no-]fullscreen', 'run in fullscreen mode'){ |v| cfg.fullscreen = v }
18
+ end
19
+ end
20
+
21
+ def help(short)
22
+ puts(options(nil), nil) unless short
23
+ puts('Start Pixelflut server.')
24
+ exit
25
+ end
26
+
27
+ help(false) if ARGV[0] == '--help'
28
+ help(true) if ARGV[0] == '--short-help'
29
+
30
+ def create_configuration
31
+ cfg = Pixelflut::App::Configuration.default
32
+ options(cfg).parse!
33
+ cfg
34
+ rescue OptionParser::ParseError => e
35
+ $stderr.puts("pxf: #{e}")
36
+ exit 1
37
+ end
38
+
39
+ begin
40
+ require 'optparse'
41
+ require File.realdirpath('../../lib/pixelflut.rb', __FILE__)
42
+ Pixelflut::App.run(create_configuration)
43
+ rescue Interrupt
44
+ exit
45
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def help(short)
5
+ puts('usage: pxf version', nil) unless short
6
+ puts('Print version information.')
7
+ end
8
+
9
+ case ARGV[0]
10
+ when '--help'
11
+ help(false)
12
+ when '--short-help'
13
+ help(true)
14
+ else
15
+ require File.realdirpath('../../lib/pixelflut/version.rb', __FILE__).freeze
16
+ puts("pxf #{Pixelflut::VERSION}")
17
+ end
data/gems.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ source('https://rubygems.org'){ gemspec }
@@ -0,0 +1,7 @@
1
+ module Pixelflut
2
+ LibDir = File.realdirpath('../pixelflut', __FILE__).freeze
3
+ autoload :App, File.join(LibDir, 'app.rb')
4
+ autoload :Server, File.join(LibDir, 'server.rb')
5
+ autoload :Canvas, File.join(LibDir, 'canvas.rb')
6
+ autoload :VERSION, File.join(LibDir, 'version.rb')
7
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gosu'
4
+ require_relative 'server'
5
+ require_relative 'text_image'
6
+
7
+ module Pixelflut
8
+ class App < Gosu::Window
9
+ Configuration = Struct.new(:width, :height, :fullscreen, :server) do
10
+ def self.default
11
+ new(nil, nil, false, Server::Configuration.default)
12
+ end
13
+ end
14
+
15
+ def self.run(configuration = Configuration.default)
16
+ new(configuration).show
17
+ end
18
+
19
+ def initialize(configuration)
20
+ if configuration.fullscreen
21
+ super(configuration.width || Gosu.screen_width, configuration.height || Gosu.screen_height, fullscreen: true)
22
+ else
23
+ super(configuration.width || 800, configuration.height || 600)
24
+ end
25
+ Process.setproctitle('pxflut')
26
+ @image = TextImage.new(width, height)
27
+ @server = Server.new(@image, configuration.server)
28
+ log(self.caption = "Pixelflut@#{configuration.server.host}:#{configuration.server.port}")
29
+ reset!
30
+ end
31
+
32
+ def show
33
+ @server.run
34
+ super
35
+ end
36
+
37
+ def reset!
38
+ @image.clear
39
+ log("clean image: #{@image.width}x#{@image.height}")
40
+ end
41
+
42
+ def update
43
+ @draw_image = nil unless 0 == @image.changes
44
+ end
45
+
46
+ def draw
47
+ (@draw_image ||= Gosu::Image.new(@image.changed, tileable: true, retro: true)).draw(0, 0, 0)
48
+ end
49
+
50
+ def log(*args)
51
+ print("[#{Time.now}] ")
52
+ puts(*args)
53
+ end
54
+
55
+ def button_down(id)
56
+ return close! if Gosu::Button::KbEscape == id
57
+ return reset! if Gosu::Button::KbSpace == id
58
+ return log("connections: #{@server.connection_count}") if Gosu::Button::KbC == id
59
+ end
60
+
61
+ def close
62
+ close!
63
+ end
64
+
65
+ def needs_redraw?
66
+ nil == @draw_image
67
+ end
68
+
69
+ def needs_cursor?
70
+ false
71
+ end
72
+ end
73
+ end
74
+
75
+ begin
76
+ Pixelflut::App.run
77
+ rescue Interrupt
78
+ exit
79
+ end if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'canvas/streamed'
4
+ require_relative 'canvas/buffered'
@@ -0,0 +1,101 @@
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
@@ -0,0 +1,29 @@
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
+ return to_enum(__method__) unless block
14
+ @lines.each(&block)
15
+ yield "QUIT\n"
16
+ end
17
+
18
+ def to_s
19
+ @lines.join + "QUIT\n"
20
+ end
21
+
22
+ private
23
+
24
+ def out(str)
25
+ @lines << str
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,20 @@
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
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module Pixelflut
6
+ class Server
7
+ Configuration = Struct.new(
8
+ :host,
9
+ :port,
10
+ :keep_alive_time,
11
+ :read_buffer_size
12
+ ) do
13
+ def self.default
14
+ new(nil, 1234, 1, 1024)
15
+ end
16
+ end
17
+
18
+ attr_reader :config
19
+
20
+ def initialize(canvas, config = Configuration.default)
21
+ @canvas, @config = canvas, config
22
+ @socket, @connections = nil, {}
23
+ @on_end = ->(conn){ @connections.delete(conn) }
24
+ @size = "SIZE #{canvas.width} #{canvas.height}\n".freeze
25
+ end
26
+
27
+ def connection_count
28
+ @connections.size
29
+ end
30
+
31
+ def update
32
+ return create_socket unless @socket
33
+ now = Time.now.to_f
34
+ incoming = @socket.accept_nonblock(exception: false)
35
+ create_connection(incoming, now) unless Symbol === incoming
36
+ @connections.keys.each{ |con| con.update(now) }
37
+ end
38
+
39
+ def run
40
+ Thread.new do
41
+ loop{ update }
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def create_connection(incoming, now)
48
+ con = Connection.new(incoming, now, @config, @canvas, @size, @on_end)
49
+ @connections[con] = con
50
+ end
51
+
52
+ def create_socket
53
+ @socket = @config.host ? TCPServer.new(@config.host, @config.port) : TCPServer.new(@config.port)
54
+ @socket.listen(255)
55
+ end
56
+
57
+ class Connection
58
+ def initialize(socket, now, config, canvas, size, on_end)
59
+ @socket, @last_tm, @config, @canvas, @size, @on_end = socket, now, config, canvas, size, on_end
60
+ # @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
61
+ @buffer = ''
62
+ end
63
+
64
+ def close(_reason)
65
+ socket, @socket = @socket, nil
66
+ return unless socket
67
+ socket.close
68
+ @on_end.call(self)
69
+ end
70
+
71
+ def update(now)
72
+ index = @buffer.index("\n")
73
+ return process_buffer(index, now) if index
74
+ read_size = @config.read_buffer_size - @buffer.size
75
+ return close(:buffer_exceeded) if read_size <= 0
76
+ str = @socket.recv_nonblock(read_size, exception: false)
77
+ return (now - @last_tm > @config.keep_alive_time ? close(:timeout) : nil) if Symbol === str
78
+ return close(:closed_by_peer) if 0 == str.size
79
+ @buffer += str
80
+ @last_tm = now
81
+ end
82
+
83
+ private
84
+
85
+ def next_command(index, now)
86
+ @buffer = @buffer[index, @buffer.size - index]
87
+ @last_tm = now
88
+ end
89
+
90
+ def command_size(index, now)
91
+ @socket.sendmsg_nonblock(@size)
92
+ next_command(index, now)
93
+ end
94
+
95
+ def command_px(command, index, now)
96
+ _, x, y, color = command.split(' ', 4)
97
+ return close(:color_expected) unless color
98
+ @canvas[x.to_i, y.to_i] = color
99
+ next_command(index, now)
100
+ end
101
+
102
+ def command_rc(command, index, now)
103
+ _, x1, y1, x2, y2, color = command.split(' ', 6)
104
+ return close(:color_expected) unless color
105
+ @canvas.draw_rect(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
106
+ next_command(index, now)
107
+ end
108
+
109
+ def process_buffer(index, now)
110
+ return close(:max_command_size_exceeded) if index > 31 # 'RC 9999 9999 9999 9999 RRGGBBAA'.size
111
+ command = @buffer[0, index]
112
+ index += 1
113
+ return command_size(index, now) if command == 'SIZE'
114
+ return close(:quit) if command == 'QUIT'
115
+ return command_px(command, index, now) if command.start_with?('PX ')
116
+ return command_rc(command, index, now) if command.start_with?('RC ')
117
+ close(:bad_command)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,89 @@
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
+ # @data[(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)) % @data.size
44
+ pattern = color * (x2 - x1 + 1)
45
+ (y2 - y1 + 1).times do
46
+ @data[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
@@ -0,0 +1,3 @@
1
+ module Pixelflut
2
+ VERSION = '0.0.4'.freeze
3
+ end
Binary file
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default do
4
+ exec 'rake --tasks'
5
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pixelflut
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Mike Blumtritt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gosu
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.16.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.16.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 12.3.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 12.3.0
55
+ description: |
56
+ Based on the idea of a simple server protocol to collaborate on a shared canvas named
57
+ [Pixel Flut](https://cccgoe.de/wiki/Pixelflut) this gem implements a Ruby version.
58
+ email: mike.blumtritt@invision.de
59
+ executables:
60
+ - pxf
61
+ - pxf-generate
62
+ - pxf-server
63
+ - pxf-help
64
+ - pxf-version
65
+ extensions: []
66
+ extra_rdoc_files:
67
+ - README.md
68
+ files:
69
+ - ".gitignore"
70
+ - README.md
71
+ - bin/pxf
72
+ - bin/pxf-generate
73
+ - bin/pxf-help
74
+ - bin/pxf-server
75
+ - bin/pxf-version
76
+ - gems.rb
77
+ - lib/pixelflut.rb
78
+ - lib/pixelflut/app.rb
79
+ - lib/pixelflut/canvas.rb
80
+ - lib/pixelflut/canvas/base.rb
81
+ - lib/pixelflut/canvas/buffered.rb
82
+ - lib/pixelflut/canvas/color.rb
83
+ - lib/pixelflut/canvas/streamed.rb
84
+ - lib/pixelflut/server.rb
85
+ - lib/pixelflut/text_image.rb
86
+ - lib/pixelflut/version.rb
87
+ - pixelflut.gemspec
88
+ - rakefile.rb
89
+ homepage: https://github.com/mblumtritt/pixelflut
90
+ licenses: []
91
+ metadata:
92
+ issue_tracker: https://github.com/mblumtritt/pixelflut/issues
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 2.5.0
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 1.3.6
107
+ requirements: []
108
+ rubyforge_project: pixelflut
109
+ rubygems_version: 2.7.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: A Pixelflut server written in Ruby.
113
+ test_files: []