pixelflut 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []