pixelflut 0.0.6 → 0.0.8

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: bbd5e580104dccc3e1a1123ccc063dde0ae530d7443129bc044e9b73a9c71293
4
- data.tar.gz: 6590f3d4f9491a10aec353701cde1f53a26158ba8c655be0952ea3be0129d500
3
+ metadata.gz: 62efcd05717e96f42f60e21cbc7a569277fed6bb2e56d24d57d3fe11ff37cb1b
4
+ data.tar.gz: 7c8fd4eb2542668dd25abd6b5cccc12818c790cf111f1b2a8e38bbda44527f4d
5
5
  SHA512:
6
- metadata.gz: ac78e76fc92be0e1891012cd1d1fe5b0cb5f388f736f75c2a4a193a294ce339cd1763ce2d1dcc055fa997b6feed20a0fba30d57447695cf8512db229fdcd6306
7
- data.tar.gz: 20efde215a0528fc028980fa8edc3c8d40a3acff446aeb3f3599ca7cbd69a87dcc1eac135a5d844e2746a58651c011f8f64a38b2c35733029d382d5c832fe8a0
6
+ metadata.gz: 94241373ff17f9e0cbbc1b1b2e51f55db1da4beb032c18f4985590e6c50a1ba1bd9313fc7858caa9be76f84593b8fc7374337226afc72641a1c40f767b7fa66a
7
+ data.tar.gz: 00f6b49748585411187665ef9c2c3a519ca77641c7a3c7aa281b65f37a0c5b8fea7e53df82b49b58cc88401b72698f551b6752915c9d7177c9de5d2b1be9f3f3
data/README.md CHANGED
@@ -29,6 +29,7 @@ $ pxf
29
29
  usage: pxf command [options]
30
30
 
31
31
  valid commands:
32
+ convert Convert given IMAGE file to Pixelflut format.
32
33
  generate Execute given generator FILEs.
33
34
  help Print help for given COMMAND.
34
35
  server Start Pixelflut server.
@@ -54,3 +55,33 @@ With these options you can configure the server:
54
55
  -h, --height HEIGHT set canvas height (default: 600)
55
56
  -f, --[no-]fullscreen run in fullscreen mode
56
57
  ```
58
+
59
+ ### Convert an image
60
+
61
+ There is a conversion command which can be used to convert a given image to the Pixelflut text format:
62
+
63
+ ```bash
64
+ $ pxf convert image.png
65
+ ```
66
+
67
+ The result can directly send to a running server (sample assumes the server runs on localhost port 1234):
68
+
69
+ ```bash
70
+ $ pxf convert image.png | netcat localhost 1234
71
+ ```
72
+
73
+ The converter can help you to resize and positioning the image and the servers canvas. These options are avail:
74
+
75
+ ```
76
+ -x, --transpose-x X transpose image X pixels
77
+ -y, --transpose-y Y transpose image Y pixels
78
+ -w, --width WIDTH resize the image to given WIDTH
79
+ -h, --height HEIGHT resize the image to given HEIGHT
80
+ ```
81
+
82
+ It maybe faster to pre-process your image(s) and send later:
83
+
84
+ ```bash
85
+ $ pxf convert -x 50 -y 100 --width 640 image.png > image.px
86
+ $ cat image.px | netcat localhost 1234
87
+ ```
data/TODO.md CHANGED
@@ -5,5 +5,5 @@
5
5
  - [x] limit connections _per IP_
6
6
  - [x] allow 10 (pixel-)actions per client
7
7
  - [ ] extend `pxf generate`
8
- - [ ] add a pre-processor for images
8
+ - [x] add a pre-processor for images
9
9
  - [ ] mention the Gosu gem in the README
data/bin/pxf-convert ADDED
@@ -0,0 +1,54 @@
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 convert [options] IMAGE'
9
+ opts.separator(nil)
10
+ opts.separator('valid options:')
11
+ opts.on('-x', '--transpose-x X', Integer, 'transpose image X pixels'){ |v| cfg[:x_offset] = v }
12
+ opts.on('-y', '--transpose-y Y', Integer, 'transpose image Y pixels'){ |v| cfg[:y_offset] = v }
13
+ opts.on('-w', '--width WIDTH', Integer, 'resize the image to given WIDTH'){ |v| cfg[:width] = v }
14
+ opts.on('-h', '--height HEIGHT', Integer, 'resize the image to given HEIGHT'){ |v| cfg[:height] = v }
15
+ end
16
+ end
17
+
18
+ def help(short)
19
+ puts(options(nil), nil) unless short
20
+ puts('Convert given IMAGE file to Pixelflut format.')
21
+ exit
22
+ end
23
+
24
+ def err(msg, code = 1)
25
+ $stderr.puts("pxf: #{msg}")
26
+ exit(code)
27
+ end
28
+
29
+ def create_options
30
+ cfg = {}
31
+ options(cfg).parse!
32
+ cfg
33
+ rescue OptionParser::ParseError => e
34
+ err(e)
35
+ end
36
+
37
+ help(false) if '--help' == ARGV[0]
38
+ help(true) if '--short-help' == ARGV[0]
39
+ err('too few arguments') if ARGV.empty?
40
+ options = create_options
41
+ require File.realdirpath('../../lib/pixelflut/converter.rb', __FILE__)
42
+ begin
43
+ ARGV.each do |file_name|
44
+ cvt = Pixelflut::Converter.new(file_name)
45
+ cvt.x_offset = options[:x_offset] if options[:x_offset]
46
+ cvt.y_offset = options[:y_offset] if options[:y_offset]
47
+ cvt.resize_to(options[:width], options[:height]) if options[:width]
48
+ cvt.each_line{ |line| puts(line) }
49
+ end
50
+ rescue Pixelflut::Converter::Error => e
51
+ err(e.message, 2)
52
+ rescue Interrupt
53
+ exit
54
+ end
data/bin/pxf-help CHANGED
@@ -8,7 +8,7 @@ end
8
8
 
9
9
  def err(msg)
10
10
  $stderr.puts("pxf: #{msg}")
11
- exit 1
11
+ exit(1)
12
12
  end
13
13
 
14
14
  case ARGV[0]
data/bin/pxf-server CHANGED
@@ -20,7 +20,7 @@ def options(cfg)
20
20
  opts.on('-r', '--read_buffer SIZE', Integer, 'set read buffer size (default: 1024)') do |v|
21
21
  cfg.server.read_buffer_size = v
22
22
  end
23
- opts.on('-l', '--peer-limit NUMBER', Integer, 'limit number connections per peer (default: 8)') do |v|
23
+ opts.on('-l', '--peer-limit NUMBER', Integer, 'limit number of connections per peer (default: 8)') do |v|
24
24
  cfg.server.peer_limit = v
25
25
  end
26
26
  opts.on('-c', '--command-limit NUMBER', Integer, 'limit of continious processed commands (default: 10)') do |v|
@@ -53,11 +53,10 @@ def create_configuration
53
53
  cfg
54
54
  rescue OptionParser::ParseError => e
55
55
  $stderr.puts("pxf: #{e}")
56
- exit 1
56
+ exit(1)
57
57
  end
58
58
 
59
59
  begin
60
- require 'optparse'
61
60
  require File.realdirpath('../../lib/pixelflut.rb', __FILE__)
62
61
  Pixelflut::App.run(create_configuration)
63
62
  rescue Interrupt
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pixelflut
4
+ class Converter
5
+ Error = Class.new(RuntimeError)
6
+
7
+ attr_accessor :x_offset, :y_offset
8
+
9
+ def initialize(file_name)
10
+ load_rmagick
11
+ @images = load_image(file_name)
12
+ @x_offset = @y_offset = 0
13
+ end
14
+
15
+ def resize_to(width, height = nil)
16
+ @images.each{ |image| image.resize_to_fit!(width, height) }
17
+ end
18
+
19
+ def each_pixel
20
+ return to_enum(__method__) unless block_given?
21
+ @images.each do |image|
22
+ image.each_pixel do |color, x, y|
23
+ yield(
24
+ x + @x_offset,
25
+ y + @y_offset,
26
+ color.to_color(Magick::AllCompliance, true, 8, true)[1, 8]
27
+ ) unless 0xffff == color.opacity
28
+ end
29
+ end
30
+ end
31
+
32
+ def each_line
33
+ return to_enum(__method__) unless block_given?
34
+ each_pixel{ |x, y, rgba| yield "PX #{x} #{y} #{rgba}" }
35
+ end
36
+
37
+ private
38
+
39
+ def load_image(file_name)
40
+ Magick::ImageList.new(file_name)
41
+ rescue Magick::ImageMagickError => e
42
+ raise(Error, e.message)
43
+ end
44
+
45
+ def load_rmagick
46
+ require 'rmagick'
47
+ rescue LoadError
48
+ raise(Error, 'unable to load gem - "RMagick" not installed')
49
+ end
50
+
51
+ end
52
+ end
@@ -1,30 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'socket'
4
+ require_relative 'server/connection'
5
+ require_relative 'server/configuration'
4
6
 
5
7
  module Pixelflut
6
8
  class Server
7
- Configuration = Struct.new(
8
- :host,
9
- :port,
10
- :keep_alive_time,
11
- :read_buffer_size,
12
- :command_limit,
13
- :peer_limit
14
- ) do
15
- def self.default
16
- new(nil, 1234, 1, 1024, 10, 8)
17
- end
18
-
19
- def to_s
20
- "bind: #{host}:#{port}"\
21
- ", keep-alive-time: #{keep_alive_time}"\
22
- ", read-buffer-size: #{read_buffer_size}"\
23
- ", command-limit: #{command_limit}"\
24
- ", peer-limit: #{peer_limit}"
25
- end
26
- end
27
-
28
9
  attr_reader :config
29
10
 
30
11
  def initialize(canvas, config = Configuration.default)
@@ -73,92 +54,5 @@ module Pixelflut
73
54
  @socket = @config.host ? TCPServer.new(@config.host, @config.port) : TCPServer.new(@config.port)
74
55
  @socket.listen(255)
75
56
  end
76
-
77
- class Connection
78
- Configuration = Struct.new(
79
- :keep_alive_time,
80
- :read_buffer_size,
81
- :command_limit,
82
- :canvas,
83
- :size_result,
84
- :on_end,
85
- keyword_init: true
86
- )
87
-
88
- attr_reader :peeraddr
89
-
90
- def initialize(socket, peeraddr, config)
91
- @socket, @peeraddr, @config = socket, peeraddr, config
92
- @last_tm, @buffer = Time.now.to_f, ''
93
- end
94
-
95
- def close(_reason)
96
- socket, @socket = @socket, nil
97
- return unless socket
98
- socket.close
99
- @config.on_end.call(self)
100
- false
101
- end
102
-
103
- def update
104
- index = @buffer.index("\n")
105
- return process_loop(index) if index
106
- read_size = @config.read_buffer_size - @buffer.size
107
- return close(:buffer_exceeded) if read_size <= 0
108
- str = @socket.recv_nonblock(read_size, exception: false)
109
- now = Time.now.to_f
110
- return (now - @last_tm > @config.keep_alive_time ? close(:timeout) : nil) if Symbol === str
111
- return close(:closed_by_peer) if 0 == str.size
112
- @buffer += str
113
- @last_tm = now
114
- end
115
-
116
- private
117
-
118
- def next_command(index)
119
- @buffer = @buffer[index, @buffer.size - index]
120
- @last_tm = Time.now.to_f
121
- true
122
- end
123
-
124
- def command_size(index)
125
- @socket.sendmsg_nonblock(@config.size_result)
126
- next_command(index)
127
- end
128
-
129
- def command_px(command, index)
130
- _, x, y, color = command.split(' ', 4)
131
- return close(:color_expected) unless color
132
- @config.canvas[x.to_i, y.to_i] = color
133
- next_command(index)
134
- end
135
-
136
- def command_rc(command, index)
137
- _, x1, y1, x2, y2, color = command.split(' ', 6)
138
- return close(:color_expected) unless color
139
- @config.canvas.draw_rect(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
140
- next_command(index)
141
- end
142
-
143
- def process_loop(index)
144
- command_count = @config.command_limit
145
- while process_buffer(index)
146
- index = @buffer.index("\n") or return
147
- command_count -= 1
148
- break if command_count <= 0
149
- end
150
- end
151
-
152
- def process_buffer(index)
153
- return close(:max_command_size_exceeded) if index > 31 # 'RC 9999 9999 9999 9999 RRGGBBAA'.size
154
- command = @buffer[0, index]
155
- index += 1
156
- return command_px(command, index) if command.start_with?('PX ')
157
- return command_rc(command, index) if command.start_with?('RC ')
158
- return close(:quit) if command == 'QUIT'
159
- return command_size(index) if command == 'SIZE'
160
- close(:bad_command)
161
- end
162
- end
163
57
  end
164
58
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pixelflut
4
+ class Server
5
+ Configuration = Struct.new(
6
+ :host,
7
+ :port,
8
+ :keep_alive_time,
9
+ :read_buffer_size,
10
+ :command_limit,
11
+ :peer_limit
12
+ ) do
13
+ def self.default
14
+ new(nil, 1234, 1, 1024, 10, 8)
15
+ end
16
+
17
+ def to_s
18
+ "bind: #{host}:#{port}"\
19
+ ", keep-alive-time: #{keep_alive_time}"\
20
+ ", read-buffer-size: #{read_buffer_size}"\
21
+ ", command-limit: #{command_limit}"\
22
+ ", peer-limit: #{peer_limit}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pixelflut
4
+ class Server
5
+ class Connection
6
+ Configuration = Struct.new(
7
+ :keep_alive_time,
8
+ :read_buffer_size,
9
+ :command_limit,
10
+ :canvas,
11
+ :size_result,
12
+ :on_end,
13
+ keyword_init: true
14
+ )
15
+
16
+ attr_reader :peeraddr
17
+
18
+ def initialize(socket, peeraddr, config)
19
+ @socket, @peeraddr, @config = socket, peeraddr, config
20
+ @last_tm, @buffer = Time.now.to_f, ''
21
+ end
22
+
23
+ def close(_reason)
24
+ socket, @socket = @socket, nil
25
+ return unless socket
26
+ socket.close
27
+ @config.on_end.call(self)
28
+ false
29
+ end
30
+
31
+ def update
32
+ index = @buffer.index("\n")
33
+ return process_loop(index) if index
34
+ read_size = @config.read_buffer_size - @buffer.size
35
+ return close(:buffer_exceeded) if read_size <= 0
36
+ str = @socket.recv_nonblock(read_size, exception: false)
37
+ now = Time.now.to_f
38
+ return (now - @last_tm > @config.keep_alive_time ? close(:timeout) : nil) if Symbol === str
39
+ return close(:closed_by_peer) if 0 == str.size
40
+ @buffer += str
41
+ @last_tm = now
42
+ rescue Errno::ECONNRESET
43
+ close(:closed_by_peer)
44
+ end
45
+
46
+ private
47
+
48
+ def next_command(index)
49
+ @buffer = @buffer[index, @buffer.size - index]
50
+ @last_tm = Time.now.to_f
51
+ true
52
+ end
53
+
54
+ def command_size(index)
55
+ @socket.sendmsg_nonblock(@config.size_result)
56
+ next_command(index)
57
+ end
58
+
59
+ def command_px(command, index)
60
+ _, x, y, color = command.split(' ', 4)
61
+ return close(:color_expected) unless color
62
+ @config.canvas[x.to_i, y.to_i] = color
63
+ next_command(index)
64
+ end
65
+
66
+ def command_rc(command, index)
67
+ _, x1, y1, x2, y2, color = command.split(' ', 6)
68
+ return close(:color_expected) unless color
69
+ @config.canvas.draw_rect(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
70
+ next_command(index)
71
+ end
72
+
73
+ def process_loop(index)
74
+ command_count = @config.command_limit
75
+ while process_buffer(index)
76
+ index = @buffer.index("\n") or return
77
+ command_count -= 1
78
+ break if command_count <= 0
79
+ end
80
+ end
81
+
82
+ def process_buffer(index)
83
+ return close(:max_command_size_exceeded) if index > 31 # 'RC 9999 9999 9999 9999 RRGGBBAA'.size
84
+ command = @buffer[0, index]
85
+ index += 1
86
+ return command_px(command, index) if command.start_with?('PX ')
87
+ return command_rc(command, index) if command.start_with?('RC ')
88
+ return close(:quit) if command == 'QUIT'
89
+ return command_size(index) if command == 'SIZE'
90
+ close(:bad_command)
91
+ end
92
+ end
93
+
94
+ private_constant(:Connection)
95
+ end
96
+ end
@@ -24,7 +24,7 @@ module Pixelflut
24
24
  end
25
25
 
26
26
  # def [](x, y)
27
- # @data[(4 * (x + @columns * y)) % @data.size, 4].bytes.map! do |b|
27
+ # @@to_blob[(4 * (x + @columns * y)) % @data.size, 4].bytes.map! do |b|
28
28
  # b = b.to_s(16)
29
29
  # b = '0' + b if b.size == 1
30
30
  # b
@@ -40,10 +40,10 @@ module Pixelflut
40
40
  x1, x2 = x2, x1 if x1 > x2
41
41
  y1, y2 = y2, y1 if y1 > y2
42
42
  color = as_color(rrggbbaa)
43
- pos = (4 * (x1 + @columns * y1)) % @data.size
43
+ pos = (4 * (x1 + @columns * y1)) % @to_blob.size
44
44
  pattern = color * (x2 - x1 + 1)
45
45
  (y2 - y1 + 1).times do
46
- @data[pos, pattern.size] = pattern
46
+ @to_blob[pos, pattern.size] = pattern
47
47
  pos += @row_inc
48
48
  end
49
49
  @changes += 1
@@ -1,3 +1,3 @@
1
1
  module Pixelflut
2
- VERSION = '0.0.6'.freeze
2
+ VERSION = '0.0.8'.freeze
3
3
  end
data/pixelflut.gemspec CHANGED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixelflut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-04 00:00:00.000000000 Z
11
+ date: 2018-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gosu
@@ -60,6 +60,7 @@ executables:
60
60
  - pxf
61
61
  - pxf-generate
62
62
  - pxf-server
63
+ - pxf-convert
63
64
  - pxf-help
64
65
  - pxf-version
65
66
  extensions: []
@@ -70,6 +71,7 @@ files:
70
71
  - README.md
71
72
  - TODO.md
72
73
  - bin/pxf
74
+ - bin/pxf-convert
73
75
  - bin/pxf-generate
74
76
  - bin/pxf-help
75
77
  - bin/pxf-server
@@ -82,7 +84,10 @@ files:
82
84
  - lib/pixelflut/canvas/buffered.rb
83
85
  - lib/pixelflut/canvas/color.rb
84
86
  - lib/pixelflut/canvas/streamed.rb
87
+ - lib/pixelflut/converter.rb
85
88
  - lib/pixelflut/server.rb
89
+ - lib/pixelflut/server/configuration.rb
90
+ - lib/pixelflut/server/connection.rb
86
91
  - lib/pixelflut/text_image.rb
87
92
  - lib/pixelflut/version.rb
88
93
  - pixelflut.gemspec