pixelflut 0.0.13 → 0.2.1

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: 46bc6d8430a0a37a766f925558d1279fbf69457f859f9f6286c2fb4a3f3a9e81
4
- data.tar.gz: f634ffe53ec48385c8c9b42f032f3fa0e56d91155cf075a05b2b9c15794d8dbb
3
+ metadata.gz: a4b1d4d4f5be08766e694539cf82507427f901f71ae1ec4c5568cec7344d1345
4
+ data.tar.gz: 65d9ebde1aa48a5989c28340d3da137b8d99d3952a85fbfb904ad7af64420df8
5
5
  SHA512:
6
- metadata.gz: 53bb74856603e0d46563b707726cdb8d5d96f5192ebd641c00ebba463e45ba5165536f4b95ea60d28e0468f8735acb32d3439802541791faf9a2dcbe13b94dab
7
- data.tar.gz: 2f26150a3d40045e6030018cbcbf807dcbb1f415f193f156d1c45d8c4eea5e5c6d56e5205d764b701b6d644346d0892042f5fb1cdd9ce0d9cd6ee5ba4fbad099
6
+ metadata.gz: 377feb36cdde2221e3d6c7739acc9773d771d15cf319e93e379c6f86248189c286af91a25e13acda88fc40e030b956761ffa205215e4bb37ddd9ee64e805e748
7
+ data.tar.gz: 910cd772cedf70ddd4cb01445821ccf360f54d96a3c4076b9845886a0e5e1a232a0b153e640b574e6540c490c77691bd515a6b42bb74ade60f0970e2e201c9f9
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 & client tool collection 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,69 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- def bin_root
5
- File.realdirpath('../', __FILE__).freeze
4
+ help <<~end, 'IMAGE'
5
+ --host ADDRESS target host address
6
+ -p, --port PORT target port (default 1234)
7
+ -c, --connections CONN count of connections (default 4)
8
+ -b, --bytes BYTES send junks of BYTES size
9
+ -x, --transpose-x X transpose image X pixels
10
+ -y, --transpose-y Y transpose image Y pixels
11
+ -s, --scale SCALE scale image by SCALE factor
12
+ -m, --pixel MODE select pixel coding (RGBX | RGBA | RGB)
13
+ -h, --help print this help
6
14
  end
7
15
 
8
- def find_commands
9
- Hash[Dir.glob(File.join(bin_root, 'pxf-*')).map!{ |cmd| [File.basename(cmd)[4..-1], cmd] }]
16
+ main do |opts|
17
+ data = create_junks(opts)
18
+ print("spawn #{data.size}")
19
+ data.size.times do |i|
20
+ next unless fork
21
+ Process.setproctitle(name(format("#{name}-%02d", i + 1)))
22
+ data = data[i].join
23
+ GC.start
24
+ GC.disable
25
+ Pixelflut::Sender.send(opts.address, data) { print('.') }
26
+ end
27
+ rescue SocketError => e
28
+ error(3, e)
29
+ rescue LoadError => e
30
+ error(4, e)
31
+ rescue Errno::ECONNREFUSED
32
+ error(2, 'unable to connect')
33
+ rescue Errno::EPIPE
34
+ error(2, 'connection lost')
10
35
  end
11
36
 
12
- def err(msg)
13
- $stderr.puts("pxf: #{msg}")
14
- exit 1
37
+ def create_junks(opts)
38
+ lines = Pixelflut.convert(**opts.to_h)
39
+ return Pixelflut.slices(lines, count: opts.count) if opts.bytes.zero?
40
+ Pixelflut.junks(lines, bytes: opts.bytes)
15
41
  end
16
42
 
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
43
+ before do
44
+ $stderr.sync = $stdout.sync = true
45
+ Process.setproctitle(name)
46
+ require_relative('../lib/pixelflut')
47
+ end
48
+
49
+ parse_argv do |args|
50
+ Struct.new(:address, :count, :bytes, :x, :y, :mode, :scale, :source) do
51
+ def to_h
52
+ { source: source, x: x, y: y, scale: scale, mode: mode }
53
+ end
54
+ end.new(
55
+ Pixelflut::Sender.address(
56
+ args['ADDRESS'] || '127.0.0.1',
57
+ (args['PORT'] || 1234).to_i
58
+ ),
59
+ (args['CONN'] || 4).to_i,
60
+ args['BYTES'].to_i,
61
+ args['X'].to_i,
62
+ args['Y'].to_i,
63
+ { 'RGBA' => :rgba, 'RGB' => :rgb }[args['MODE']] || :rgbx,
64
+ args.key?('SCALE') ? args['SCALE'].to_f : nil,
65
+ args['IMAGE']
66
+ )
27
67
  end
28
68
 
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)
69
+ BEGIN { require('mini-cli') and include(MiniCli) }
data/bin/pxf-info CHANGED
@@ -1,55 +1,32 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
1
+ #! /bin/sh
3
2
 
4
- def options(cfg)
5
- require 'optparse'
6
- OptionParser.new do |opts|
7
- opts.summary_indent = ' '
8
- opts.banner = 'usage: pxf info [options]'
9
- opts.separator(nil)
10
- opts.separator('valid options:')
11
- opts.on('-h', '--host HOST', String, 'target host name (default localhost)'){ |v| cfg[:host] = v }
12
- opts.on('-p', '--port PORT', Integer, 'target port (default 1234)'){ |v| cfg[:port] = v }
13
- end
14
- end
3
+ set -e
15
4
 
16
- def help(short)
17
- puts(options(nil), nil) unless short
18
- puts('Get info about a Pixelflut server.')
19
- exit
20
- end
5
+ if [ "$1" = '--help' ]
6
+ then
7
+ echo "Usage: ${0##*/} [-p|--port PORT] ADDRESS
21
8
 
22
- def err(msg, code = 1)
23
- $stderr.puts("pxf: #{msg}")
24
- exit(code)
25
- end
9
+ Requests screen size of Pixelflut server at ADDRESS.
10
+ "
11
+ exit 0
12
+ fi
26
13
 
27
- def create_options
28
- {port: 1234}.tap{ |cfg| options(cfg).parse! }
29
- rescue OptionParser::ParseError => e
30
- err(e)
31
- end
14
+ PORT="1234"
15
+ ADDRESS=""
32
16
 
33
- help(false) if '--help' == ARGV[0]
34
- help(true) if '--short-help' == ARGV[0]
35
- options = create_options
36
- require File.realdirpath('../../lib/pixelflut/client/socket.rb', __FILE__)
37
- begin
38
- socket = Pixelflut::Client::Socket.new(Addrinfo.tcp(options[:host], options[:port]))
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
39
31
 
40
-
41
- err("unable to connect - #{options[:host]}:options[:port])", 2) unless socket.connect? || socket.wait_writable(5)
42
- err("server busy - #{options[:host]}:options[:port])", 3) unless socket.write_with_timout("SIZE\n", 5)
43
- line = socket.readline_with_timeout(5)
44
- socket.close
45
- err("server does not respond - #{options[:host]}:options[:port])", 4) unless line
46
- mark, width, height = line.split(' ', 3)
47
- width = width.to_i
48
- height = height.to_i
49
- err("no Pixelflut server - #{options[:host]}:options[:port])", 5) if mark != 'SIZE' || width <= 0 || height <= 0
50
- puts "canvas size: #{width}x#{height}"
51
- rescue SocketError, SystemCallError => e
52
- err(e.message, 2)
53
- rescue Interrupt
54
- exit
55
- end
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
- LIB_DIR = File.realdirpath('../pixelflut', __FILE__).freeze
3
- autoload :App, File.join(LIB_DIR, 'app.rb')
4
- autoload :Server, File.join(LIB_DIR, 'server.rb')
5
- autoload :Canvas, File.join(LIB_DIR, 'canvas.rb')
6
- autoload :Converter, File.join(LIB_DIR, 'converter.rb')
7
- autoload :VERSION, File.join(LIB_DIR, '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 junks(lines, bytes:)
19
+ size, ret = 0, [current = []]
20
+ lines.each do |line|
21
+ next current << line if (size += line.bytesize) < bytes
22
+ ret << (current = [line])
23
+ size = line.bytesize
24
+ end
25
+ ret
26
+ end
27
+ alias packages junks # backward compatibility
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.13'.freeze
4
+ VERSION = '0.2.1'
3
5
  end
data/pixelflut.gemspec CHANGED
@@ -2,40 +2,33 @@
2
2
 
3
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 & client tool collection written in Ruby.'
8
+ spec.author = 'Mike Blumtritt'
9
+
10
+ spec.required_ruby_version = '>= 2.7.2'
11
+
12
+ spec.summary = 'A fast Pixelflut client written in Ruby.'
9
13
  spec.description = <<~DESCRIPTION
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.
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.
12
17
  DESCRIPTION
13
- spec.author = 'Mike Blumtritt'
14
- spec.email = 'mike.blumtritt@invision.de'
15
18
  spec.homepage = 'https://github.com/mblumtritt/pixelflut'
16
- spec.metadata = {
17
- 'issue_tracker' => 'https://github.com/mblumtritt/pixelflut/issues'
18
- }
19
- spec.rubyforge_project = spec.name
20
19
 
21
- spec.add_runtime_dependency 'gosu', '>= 0.13.2'
22
- spec.add_development_dependency 'bundler', '>= 1.16.0'
23
- 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'
24
23
 
25
- spec.platform = Gem::Platform::RUBY
26
- spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
27
- spec.required_ruby_version = '>= 2.5.0'
24
+ spec.add_runtime_dependency 'rmagick'
25
+ spec.add_runtime_dependency 'mini-cli', '>= 0.4.0'
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'rake'
28
28
 
29
- spec.require_paths = %w[lib]
30
29
  spec.bindir = 'bin'
31
- spec.executables =
32
- Dir
33
- .glob(File.expand_path('../bin/*', __FILE__))
34
- .map!{ |fn| File.basename(fn) }
35
-
36
- all_files = %x(git ls-files -z).split(0.chr)
37
- spec.test_files = all_files.grep(%r{^(spec|test)/})
38
- spec.files = all_files - spec.test_files
30
+ spec.executables = %w[pxf pxf-info]
39
31
 
32
+ spec.files = Dir.chdir(__dir__) { `git ls-files -z`.split(0.chr) }
40
33
  spec.extra_rdoc_files = %w[README.md]
41
34
  end