pixelflut 0.0.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53fa839b0f1e655d481296ff80e16c50bcb14cb8b65002508a6cbdb6cf602d18
4
- data.tar.gz: dc278f90adf043c2acfa484ea1beaadb04c12dd70e18e7ed2daa88db69ea1f28
3
+ metadata.gz: d060b7c6e1888c61141aa6f24fae4e2255f4335d8e63ff8dcf1a8710f6d749a4
4
+ data.tar.gz: 81cb2846a78285887dab175ab3cb45bc9dcb4d9b0cf7d01add3d607431d537c5
5
5
  SHA512:
6
- metadata.gz: 1bbf4d13ed7bc852d20000103456680ececcfc4286225b0fb7be74afce25ad89dff6fdcb5e89120737c599645591400c5761e4c0172e632c742599975ee5a5da
7
- data.tar.gz: 53368ac74848597f441952344f40e30744517da98db162405d7837f461d15096a66a37e0291763672d60119a8fe1d8266ee91c2ca7764bd7a2eef71620b3ec95
6
+ metadata.gz: d30c42ab6b714404eff2f5dfb3c861c96010266086a497ba2684acb7e6c8ca2b3d79999e41fc10b44fb89a62c1375ed0b68b6da74afa79dfe40cc775c0031d47
7
+ data.tar.gz: 58ab45066aeff1c2b1162ff225ba24498a966a7a75caae0219f849695c1cf7d1b997ddbfba605ed118ffea2c9a72a222e666525b2936a618c6563b083b8f4208
data/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
- _local/
2
1
  tmp/
3
2
  pkg/
4
3
  gems.locked
data/README.md CHANGED
@@ -1,14 +1,12 @@
1
1
  # Pixelflut
2
2
 
3
- A Pixelflut server written in Ruby.
3
+ A Pixelflut client written in Ruby.
4
4
 
5
5
  ## Description
6
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.
7
+ Based on the idea of a simple server protocol to collaborate on a shared canvas named [Pixelflut](https://cccgoe.de/wiki/Pixelflut) this gem implements a Ruby version of a client.
8
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.
9
+ This gem is an open experiment. You are welcome to fork or create pull requests to find the fastest solution!
12
10
 
13
11
  ### Installation
14
12
 
@@ -26,76 +24,16 @@ You'll find some help on the command line:
26
24
 
27
25
  ```bash
28
26
  $ pxf
29
- usage: pxf command [options]
30
-
31
- valid commands:
32
- convert Convert given IMAGE file to Pixelflut ASCII format.
33
- generate Execute given generator FILEs.
34
- help Print help for given COMMAND.
35
- send Send given Pixelflut ASCII file to a server.
36
- server Start Pixelflut server.
37
- version Print version information.
38
- ```
39
-
40
- ### Start a Sserver
41
-
42
- Starting the server on default port `1234` and open a drawing screen is quite simple:
43
-
44
- ```bash
45
- $ pxf server
46
- ```
47
-
48
- With these options you can configure the server:
49
-
50
- ```
51
- -b, --bind ADDR bind to given address
52
- -p, --port PORT select port(default: 1234)
53
- -k, --keep-alive set maximum keep-alive time
54
- -r, --read_buffer SIZE set read buffer size (default: 1024)
55
- -w, --width WIDTH set canvas width (default: 800)
56
- -h, --height HEIGHT set canvas height (default: 600)
57
- -f, --[no-]fullscreen run in fullscreen mode
27
+ Usage: pxf [OPTIONS] IMAGE
28
+
29
+ Options:
30
+ --host ADDRESS target host address
31
+ -p, --port PORT target port (default 1234)
32
+ -c, --connections CONN count of connections (default 4)
33
+ -b, --bytes BYTES send junks of BYTES size
34
+ -x, --transpose-x X transpose image X pixels
35
+ -y, --transpose-y Y transpose image Y pixels
36
+ -s, --scale SCALE scale image by SCALE factor
37
+ -m, --pixel MODE select pixel coding (RGBX | RGBA | RGB)
38
+ -h, --help print this help
58
39
  ```
59
-
60
- ### Convert an Image
61
-
62
- There is a conversion command which can be used to convert a given image to the Pixelflut ASCII text format:
63
-
64
- ```bash
65
- $ pxf convert image.png
66
- ```
67
-
68
- The result can send directly to a server (sample assumes the server runs on localhost port 1234):
69
-
70
- ```bash
71
- $ pxf convert image.png | pxf send
72
- ```
73
-
74
- The converter can help you to resize and positioning the image at the servers canvas. These options are avail:
75
-
76
- ```
77
- -x, --transpose-x X transpose image X pixels
78
- -y, --transpose-y Y transpose image Y pixels
79
- -w, --width WIDTH resize the image to given WIDTH
80
- -h, --height HEIGHT resize the image to given HEIGHT
81
- ```
82
-
83
- It maybe faster to pre-process your images and send them later:
84
-
85
- ```bash
86
- $ pxf convert -x 50 -y 100 --width 100 image1.png > image1.px
87
- $ pxf convert -x 150 --width 2100 image2.png > image2.px
88
- $ pxf send -h pixel_host image1.px image2.px
89
- ```
90
-
91
- ### Send Pixelflut ASCII Text
92
-
93
- The `pxf send` command can be used to send given Pixelflut ASCII text files to a server. It uses several connections at the same time to fasten the transmission.
94
-
95
- Next sample will send the Pixelflut ASCII text file named `pixels.px` to the server on port 1234 at address `pixelhost` using 12 connections:
96
-
97
- ```bash
98
- $ pxf send -h pixelhost -p 1234 -c 12 pixels.px
99
- ```
100
-
101
- If no file is given the STDIN will be read and send afterwards to the server.
data/bin/pxf CHANGED
@@ -1,33 +1,67 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- def bin_root
5
- File.realdirpath('../', __FILE__).freeze
6
- end
4
+ require 'mini-cli'
5
+ include MiniCli
7
6
 
8
- def find_commands
9
- Hash[Dir.glob(File.join(bin_root, 'pxf-*')).map!{ |cmd| [File.basename(cmd)[4..-1], cmd] }]
7
+ help <<~end, 'IMAGE'
8
+ --host ADDRESS target host address
9
+ -p, --port PORT target port (default 1234)
10
+ -c, --connections CONN count of connections (default 4)
11
+ -b, --bytes BYTES send junks of BYTES size
12
+ -x, --transpose-x X transpose image X pixels
13
+ -y, --transpose-y Y transpose image Y pixels
14
+ -s, --scale SCALE scale image by SCALE factor
15
+ -m, --pixel MODE select pixel coding (RGBX | RGBA | RGB)
16
+ -h, --help print this help
10
17
  end
11
18
 
12
- def err(msg)
13
- $stderr.puts("pxf: #{msg}")
14
- exit 1
19
+ Process.setproctitle(name)
20
+ $stderr.sync = $stdout.sync = true
21
+
22
+ parse_argv do |args|
23
+ Struct.new(:host, :port, :count, :bytes, :x, :y, :mode, :scale, :source) do
24
+ def to_h
25
+ { source: source, x: x, y: y, scale: scale, mode: mode }
26
+ end
27
+ end.new(
28
+ args['ADDRESS'] || '127.0.0.1',
29
+ (args['PORT'] || 1234).to_i,
30
+ (args['CONN'] || 4).to_i,
31
+ args['BYTES'].to_i,
32
+ args['X'].to_i,
33
+ args['Y'].to_i,
34
+ { 'RGBA' => :rgba, 'RGB' => :rgb }[args['MODE']] || :rgbx,
35
+ args.key?('SCALE') ? args['SCALE'].to_f : nil,
36
+ args['IMAGE']
37
+ )
15
38
  end
16
39
 
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])
40
+ main do |opts|
41
+ require_relative '../lib/pixelflut'
42
+ address = Pixelflut::Sender.address(opts.host, opts.port)
43
+ data = create_data(opts)
44
+ print("spawn #{data.size}")
45
+ data.size.times do |i|
46
+ next unless fork
47
+ Process.setproctitle(name(format("#{name}-%02d", i + 1)))
48
+ data = data[i].join
49
+ GC.start
50
+ GC.disable
51
+ Pixelflut::Sender.send(address, data) { print('.') }
25
52
  end
26
- exit
53
+ rescue SocketError => e
54
+ error(3, e)
55
+ rescue LoadError => e
56
+ error(4, e)
57
+ rescue Errno::ECONNREFUSED
58
+ error(2, 'unable to connect')
59
+ rescue Errno::EPIPE
60
+ error(2, 'connection lost')
27
61
  end
28
62
 
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)
63
+ def create_data(opts)
64
+ data = Pixelflut.convert(**opts.to_h)
65
+ return Pixelflut.slices(data, count: opts.count) if opts.bytes.zero?
66
+ Pixelflut.packages(data, bytes: opts.bytes)
67
+ end
data/bin/pxf-info ADDED
@@ -0,0 +1,32 @@
1
+ #! /bin/sh
2
+
3
+ set -e
4
+
5
+ if [ "$1" = '--help' ]
6
+ then
7
+ echo "Usage: ${0##*/} [-p|--port PORT] ADDRESS
8
+
9
+ Requests screen size of Pixelflut server at ADDRESS.
10
+ "
11
+ exit 0
12
+ fi
13
+
14
+ PORT="1234"
15
+ ADDRESS=""
16
+
17
+ while [ $# -gt 0 ]
18
+ do
19
+ key="$1"
20
+ case $key in
21
+ -p|--port)
22
+ PORT="$2"
23
+ shift; shift
24
+ ;;
25
+ *)
26
+ ADDRESS="$key"
27
+ shift
28
+ ;;
29
+ esac
30
+ done
31
+
32
+ echo "SIZE\nQUIT\n" | nc "$ADDRESS" "$PORT"
data/gems.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source('https://rubygems.org'){ gemspec }
3
+ source('https://rubygems.org') { gemspec }
data/lib/pixelflut.rb CHANGED
@@ -1,8 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'pixelflut/image'
4
+ require_relative 'pixelflut/sender'
5
+
1
6
  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 :Converter, File.join(LibDir, 'converter.rb')
7
- autoload :VERSION, File.join(LibDir, 'version.rb')
7
+ class << self
8
+ def convert(source:, x: 0, y: 0, scale: nil, mode: :rgbx)
9
+ _convert(as_image(source, scale), x, y, &as_cvt(mode))
10
+ end
11
+
12
+ def slices(lines, count: 4)
13
+ Array.new(count) { [] }.tap do |ret|
14
+ lines.each_with_index { |line, idx| ret[idx % count] << line }
15
+ end
16
+ end
17
+
18
+ def packages(lines, bytes:)
19
+ ret = [current = []]
20
+ size = 0
21
+ lines.each do |line|
22
+ next current << line if (size += line.bytesize) < bytes
23
+ ret << (current = [line])
24
+ size = line.bytesize
25
+ end
26
+ ret
27
+ end
28
+
29
+ private
30
+
31
+ def _convert(image, dx, dy)
32
+ image.each_pixel.to_a.map! do |x, y, px|
33
+ "PX #{x + dx} #{y + dy} #{yield(px)}\n"
34
+ end.shuffle!
35
+ end
36
+
37
+ def as_image(source, scale)
38
+ Image.new(source).tap { |image| image.scale(scale) if scale }
39
+ end
40
+
41
+ def as_cvt(mode)
42
+ case mode
43
+ when :rgb
44
+ lambda { |px| px.to_color(Magick::AllCompliance, false, 8, true)[1, 6] }
45
+ when :rgba
46
+ lambda { |px| px.to_color(Magick::AllCompliance, true, 8, true)[1, 8] }
47
+ else
48
+ lambda do |px|
49
+ px.to_color(Magick::AllCompliance, false, 8, true)[
50
+ 1,
51
+ px.alpha >= 65_535 ? 6 : 8
52
+ ]
53
+ end
54
+ end
55
+ end
56
+ end
8
57
  end
@@ -0,0 +1,32 @@
1
+ require 'rmagick'
2
+
3
+ module Pixelflut
4
+ class Image
5
+ def initialize(file_name)
6
+ @image = Magick::ImageList.new(file_name)[0]
7
+ rescue Magick::ImageMagickError => e
8
+ raise(LoadError, e.message, cause: e)
9
+ end
10
+
11
+ def width
12
+ @image.columns
13
+ end
14
+
15
+ def height
16
+ @image.rows
17
+ end
18
+
19
+ def resize_to(width, height = nil)
20
+ @image.resize_to_fit!(width, height)
21
+ end
22
+
23
+ def scale(factor)
24
+ @image.scale!(factor)
25
+ end
26
+
27
+ def each_pixel
28
+ return to_enum(__method__) unless block_given?
29
+ @image.each_pixel { |px, x, y| 0 != px.alpha and yield(x, y, px) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ require 'socket'
2
+
3
+ module Pixelflut
4
+ module Sender
5
+ def self.address(host, port)
6
+ Addrinfo.tcp(host, port)
7
+ end
8
+
9
+ def self.send(address, data)
10
+ socket = create_socket(address)
11
+ yield(socket) if block_given?
12
+ loop { socket.write(data) }
13
+ end
14
+
15
+ def self.create_socket(address)
16
+ Socket
17
+ .new(address.ipv6? ? :INET6 : :INET, :STREAM)
18
+ .tap do |socket|
19
+ socket.sync = false
20
+ socket.setsockopt(:TCP, :NODELAY, 0)
21
+ socket.setsockopt(:SOCKET, :KEEPALIVE, 0)
22
+ socket.do_not_reverse_lookup = true
23
+ socket.connect(
24
+ Socket.pack_sockaddr_in(address.ip_port, address.ip_address)
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pixelflut
2
- VERSION = '0.0.10'.freeze
4
+ VERSION = '0.2.0'
3
5
  end
data/pixelflut.gemspec CHANGED
@@ -1,37 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../lib/pixelflut/version', __FILE__)
3
+ require_relative 'lib/pixelflut/version'
4
4
 
5
- GemSpec = Gem::Specification.new do |spec|
5
+ Gem::Specification.new do |spec|
6
6
  spec.name = 'pixelflut'
7
7
  spec.version = Pixelflut::VERSION
8
- spec.summary = 'A Pixelflut server written in Ruby.'
9
- spec.description = <<~EOS
10
- Based on the idea of a simple server protocol to collaborate on a shared canvas named
11
- [Pixel Flut](https://cccgoe.de/wiki/Pixelflut) this gem implements a Ruby version.
12
- EOS
13
8
  spec.author = 'Mike Blumtritt'
14
- spec.email = 'mike.blumtritt@invision.de'
9
+
10
+ spec.required_ruby_version = '>= 2.7.2'
11
+
12
+ spec.summary = 'A fast Pixelflut client written in Ruby.'
13
+ spec.description = <<~DESCRIPTION
14
+ Based on the idea of a simple server protocol to collaborate on a shared
15
+ canvas named [Pixelflut](https://cccgoe.de/wiki/Pixelflut) this gem
16
+ implements a fast Ruby client version.
17
+ DESCRIPTION
15
18
  spec.homepage = 'https://github.com/mblumtritt/pixelflut'
16
- spec.metadata = {'issue_tracker' => 'https://github.com/mblumtritt/pixelflut/issues'}
17
- spec.rubyforge_project = spec.name
18
19
 
19
- spec.add_runtime_dependency 'gosu', '>= 0.13.2'
20
- spec.add_development_dependency 'bundler', '>= 1.16.0'
21
- spec.add_development_dependency 'rake', '>= 12.3.0'
20
+ spec.metadata['source_code_uri'] = 'https://github.com/mblumtritt/pixelflut'
21
+ spec.metadata['bug_tracker_uri'] =
22
+ 'https://github.com/mblumtritt/pixelflut/issues'
22
23
 
23
- spec.platform = Gem::Platform::RUBY
24
- spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
25
- spec.required_ruby_version = '>= 2.5.0'
24
+ spec.add_runtime_dependency 'rmagick'
25
+ spec.add_runtime_dependency 'mini-cli'
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake'
26
28
 
27
- spec.require_paths = %w[lib]
28
29
  spec.bindir = 'bin'
29
- spec.executables = Dir.glob(File.expand_path('../bin/*', __FILE__)).map!{ |fn| File.basename(fn) }
30
-
31
- all_files = %x(git ls-files -z).split(0.chr)
32
- spec.test_files = all_files.grep(%r{^(spec|test)/})
33
- spec.files = all_files - spec.test_files
30
+ spec.executables = %w[pxf pxf-info]
34
31
 
35
- spec.has_rdoc = false
32
+ spec.files = Dir.chdir(__dir__) { `git ls-files -z`.split(0.chr) }
36
33
  spec.extra_rdoc_files = %w[README.md]
37
34
  end