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