pixelflut 0.0.13 → 0.2.1
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 +4 -4
- data/.gitignore +0 -1
- data/README.md +15 -77
- data/bin/pxf +58 -22
- data/bin/pxf-info +26 -49
- data/gems.rb +1 -1
- data/lib/pixelflut.rb +55 -6
- data/lib/pixelflut/image.rb +32 -0
- data/lib/pixelflut/sender.rb +29 -0
- data/lib/pixelflut/version.rb +3 -1
- data/pixelflut.gemspec +18 -25
- data/rakefile.rb +5 -3
- metadata +38 -45
- data/TODO.md +0 -11
- data/bin/pxf-convert +0 -55
- data/bin/pxf-generate +0 -35
- data/bin/pxf-help +0 -23
- data/bin/pxf-send +0 -44
- data/bin/pxf-server +0 -64
- data/bin/pxf-version +0 -17
- data/lib/pixelflut/app.rb +0 -81
- data/lib/pixelflut/canvas.rb +0 -4
- data/lib/pixelflut/canvas/base.rb +0 -99
- data/lib/pixelflut/canvas/buffered.rb +0 -27
- data/lib/pixelflut/canvas/streamed.rb +0 -20
- data/lib/pixelflut/client.rb +0 -35
- data/lib/pixelflut/client/socket.rb +0 -93
- data/lib/pixelflut/converter.rb +0 -53
- data/lib/pixelflut/server.rb +0 -58
- data/lib/pixelflut/server/configuration.rb +0 -26
- data/lib/pixelflut/server/connection.rb +0 -98
- data/lib/pixelflut/text_image.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4b1d4d4f5be08766e694539cf82507427f901f71ae1ec4c5568cec7344d1345
|
4
|
+
data.tar.gz: 65d9ebde1aa48a5989c28340d3da137b8d99d3952a85fbfb904ad7af64420df8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 377feb36cdde2221e3d6c7739acc9773d771d15cf319e93e379c6f86248189c286af91a25e13acda88fc40e030b956761ffa205215e4bb37ddd9ee64e805e748
|
7
|
+
data.tar.gz: 910cd772cedf70ddd4cb01445821ccf360f54d96a3c4076b9845886a0e5e1a232a0b153e640b574e6540c490c77691bd515a6b42bb74ade60f0970e2e201c9f9
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
# Pixelflut
|
2
2
|
|
3
|
-
A Pixelflut
|
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 [
|
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
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
5
|
-
|
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
|
-
|
9
|
-
|
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
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
-
|
2
|
-
# frozen_string_literal: true
|
1
|
+
#! /bin/sh
|
3
2
|
|
4
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
exit
|
20
|
-
end
|
5
|
+
if [ "$1" = '--help' ]
|
6
|
+
then
|
7
|
+
echo "Usage: ${0##*/} [-p|--port PORT] ADDRESS
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
exit
|
25
|
-
|
9
|
+
Requests screen size of Pixelflut server at ADDRESS.
|
10
|
+
"
|
11
|
+
exit 0
|
12
|
+
fi
|
26
13
|
|
27
|
-
|
28
|
-
|
29
|
-
rescue OptionParser::ParseError => e
|
30
|
-
err(e)
|
31
|
-
end
|
14
|
+
PORT="1234"
|
15
|
+
ADDRESS=""
|
32
16
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
data/lib/pixelflut/version.rb
CHANGED
data/pixelflut.gemspec
CHANGED
@@ -2,40 +2,33 @@
|
|
2
2
|
|
3
3
|
require_relative 'lib/pixelflut/version'
|
4
4
|
|
5
|
-
|
5
|
+
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'pixelflut'
|
7
7
|
spec.version = Pixelflut::VERSION
|
8
|
-
spec.
|
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
|
11
|
-
[
|
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.
|
22
|
-
spec.
|
23
|
-
|
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.
|
26
|
-
spec.
|
27
|
-
spec.
|
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
|