shattered_machine 0.0.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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.yardoc/checksums +14 -0
  5. data/.yardoc/complete +0 -0
  6. data/.yardoc/object_types +0 -0
  7. data/.yardoc/objects/root.dat +0 -0
  8. data/.yardoc/proxy_types +0 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE +9 -0
  11. data/README.md +51 -0
  12. data/doc/ShatteredMachine.html +135 -0
  13. data/doc/ShatteredMachine/Brush.html +375 -0
  14. data/doc/ShatteredMachine/ChangeByte.html +367 -0
  15. data/doc/ShatteredMachine/Converter.html +327 -0
  16. data/doc/ShatteredMachine/Defect.html +369 -0
  17. data/doc/ShatteredMachine/Exchange.html +373 -0
  18. data/doc/ShatteredMachine/Glitcher.html +365 -0
  19. data/doc/ShatteredMachine/Io.html +569 -0
  20. data/doc/ShatteredMachine/Io/Paths.html +310 -0
  21. data/doc/ShatteredMachine/PixelSorter.html +381 -0
  22. data/doc/ShatteredMachine/RustyEngine.html +111 -0
  23. data/doc/ShatteredMachine/Sampler.html +361 -0
  24. data/doc/ShatteredMachine/Slim.html +389 -0
  25. data/doc/ShatteredMachine/Transpose.html +369 -0
  26. data/doc/ShatteredMachine/WrongFilter.html +367 -0
  27. data/doc/_index.html +288 -0
  28. data/doc/class_list.html +51 -0
  29. data/doc/css/common.css +1 -0
  30. data/doc/css/full_list.css +58 -0
  31. data/doc/css/style.css +497 -0
  32. data/doc/file_list.html +56 -0
  33. data/doc/frames.html +17 -0
  34. data/doc/index.html +123 -0
  35. data/doc/js/app.js +314 -0
  36. data/doc/js/full_list.js +216 -0
  37. data/doc/js/jquery.js +4 -0
  38. data/doc/method_list.html +275 -0
  39. data/doc/top-level-namespace.html +110 -0
  40. data/lib/rusty_engine/librusty_engine.dll +0 -0
  41. data/lib/rusty_engine/librusty_engine.dylib +0 -0
  42. data/lib/rusty_engine/librusty_engine.so +0 -0
  43. data/lib/rusty_engine/rusty_engine.rb +39 -0
  44. data/lib/shattered_machine.rb +11 -0
  45. data/lib/shattered_machine/brush.rb +28 -0
  46. data/lib/shattered_machine/change_byte.rb +41 -0
  47. data/lib/shattered_machine/converter.rb +20 -0
  48. data/lib/shattered_machine/defect.rb +38 -0
  49. data/lib/shattered_machine/exchange.rb +59 -0
  50. data/lib/shattered_machine/glitcher.rb +35 -0
  51. data/lib/shattered_machine/io.rb +74 -0
  52. data/lib/shattered_machine/pixel_sorter.rb +50 -0
  53. data/lib/shattered_machine/sampler.rb +100 -0
  54. data/lib/shattered_machine/slim.rb +55 -0
  55. data/lib/shattered_machine/transpose.rb +50 -0
  56. data/lib/shattered_machine/wrong_filter.rb +39 -0
  57. data/shattered_machine.gemspec +16 -0
  58. data/spec/brush_spec.rb +28 -0
  59. data/spec/change_byte_spec.rb +26 -0
  60. data/spec/converter_spec.rb +24 -0
  61. data/spec/defect_spec.rb +26 -0
  62. data/spec/exchange_spec.rb +26 -0
  63. data/spec/glitcher_spec.rb +26 -0
  64. data/spec/images/bar.PNG +0 -0
  65. data/spec/images/bar.jpeg +0 -0
  66. data/spec/images/foo.jpg +0 -0
  67. data/spec/images/foo.png +0 -0
  68. data/spec/images/fuzz.JPG +0 -0
  69. data/spec/images/pouet.JPEG +0 -0
  70. data/spec/io_spec.rb +74 -0
  71. data/spec/lib/rusty_engine_spec.rb +20 -0
  72. data/spec/pixel_sorter_spec.rb +26 -0
  73. data/spec/sampler_spec.rb +37 -0
  74. data/spec/slim_spec.rb +26 -0
  75. data/spec/spec_helper.rb +61 -0
  76. data/spec/transpose_spec.rb +26 -0
  77. data/spec/wrong_filter_spec.rb +26 -0
  78. metadata +147 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../rusty_engine/rusty_engine'
4
+
5
+ module ShatteredMachine
6
+ # Sort pixels of a given png image.
7
+ # The logic for the pixel sorter come from the Rusty Engine.
8
+ class PixelSorter
9
+ # @param options [Hash] options for pixel sort algorithm
10
+ def initialize(options = {})
11
+ @direction = options[:direction] || :vertical
12
+ @smart_sorting = (options[:smart_sorting] || true).to_s
13
+ @detection_type = options[:detection_type] || :lightness_range
14
+ @detection_min = options[:detection_min] || '45'
15
+ @detection_max = options[:detection_max] || '60'
16
+ @multiple_ranges = (options[:multiple_ranges] || false).to_s
17
+ @detection_min_2 = options[:detection_min_2] || '75'
18
+ @detection_max_2 = options[:detection_max_2] || '90'
19
+ @sorting_by = options[:sorting_by] || :hue
20
+ end
21
+
22
+ # @param input_image [string] path for image
23
+ # @param output_image [string] path for output pixel sorted image
24
+ # @return [boolean] status of pixel sort
25
+ def call(input_image, output_image)
26
+ RustyEngine.sort(input_image, output_image, rust_formatted_direction, @smart_sorting,
27
+ rust_formatted_detection_type, @detection_min, @detection_max, @multiple_ranges,
28
+ @detection_min_2, @detection_max_2, rust_formatted_sorting_by)
29
+ end
30
+
31
+ private
32
+
33
+ def rust_formatted_direction
34
+ ruby_to_rust_directions = { horizontal: '1', vertical: '2',
35
+ horizontal_inverted: '3',
36
+ vertical_inverted: '4' }
37
+ ruby_to_rust_directions[@direction]
38
+ end
39
+
40
+ def rust_formatted_detection_type
41
+ ruby_to_rust_detection_type = { lightness_range: '0', colors: '1' }
42
+ ruby_to_rust_detection_type[@detection_type]
43
+ end
44
+
45
+ def rust_formatted_sorting_by
46
+ ruby_to_rust_sorting_by = { hue: '0', staturation: '1' }
47
+ ruby_to_rust_sorting_by[@sorting_by]
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pnglitch'
4
+ module ShatteredMachine
5
+ # A simple weay ro run one, many or all glitch algotirhm on one specific image.
6
+ # This create a quick overview of the effect of each algo for the given image.
7
+ class Sampler
8
+ FILTERS = %w[none sub up average paeth].freeze
9
+ SLIM_DIRECTION = %w[up_to_down down_to_up left_to_right right_to_left].freeze
10
+ BRUSH_DIRECTION = %w[vertical vertical_inverted horizontal horizontal_inverted].freeze
11
+ ALL_ALGORITHMS = %w[exchange transpose wrong_filter slim brus change_byte defect]
12
+
13
+ # @param io [ShatteredMachine::Io] Io containing paths for images to sample
14
+ # @param options [Hash] options for specifying which
15
+ def initialize(io, options = {})
16
+ @io = io
17
+ @base_output_filename = options[:base_output_filename] || 'sample'
18
+ @algorithms_to_sample = options[:algorithms_to_sample] || ALL_ALGORITHMS
19
+ end
20
+
21
+ def call
22
+ FILTERS.each do |filter|
23
+ sample_exchange(filter)
24
+ sample_transpose(filter)
25
+ sample_wrong_filter(filter)
26
+ end
27
+ sample_slim
28
+ sample_brush
29
+ sample_change_byte
30
+ sample_defect
31
+ end
32
+
33
+ private
34
+
35
+ def update_io(output_filename_appendix)
36
+ @io.output_filename = "#{@base_output_filename}_#{output_filename_appendix}"
37
+ end
38
+
39
+ def sample_exchange(filter)
40
+ return unless @algorithms_to_sample.include? 'exchange'
41
+
42
+ exchange_options = { filter: filter }
43
+ update_io("exchange_#{filter}")
44
+ Glitcher.new('Exchange', @io, exchange_options).call
45
+ end
46
+
47
+ def sample_transpose(filter)
48
+ return unless @algorithms_to_sample.include? 'transpose'
49
+
50
+ transpose_options = { filter: filter }
51
+ update_io("transpose_#{filter}")
52
+ Glitcher.new('Transpose', @io, transpose_options).call
53
+ end
54
+
55
+ def sample_wrong_filter(filter)
56
+ return unless @algorithms_to_sample.include? 'wrong_filter'
57
+
58
+ wrong_filter_options = { algorithm: 'wrong_filter',
59
+ filter: filter }
60
+ update_io("wrong_filter_#{filter}")
61
+ Glitcher.new('WrongFilter', @io, wrong_filter_options).call
62
+ end
63
+
64
+ def sample_slim
65
+ return unless @algorithms_to_sample.include? 'slim'
66
+
67
+ SLIM_DIRECTION.each do |direction|
68
+ update_io("slim_#{direction}")
69
+ slim_options = { direction: direction.to_sym }
70
+ Glitcher.new('Slim', @io, slim_options).call
71
+ end
72
+ end
73
+
74
+ def sample_brush
75
+ return unless @algorithms_to_sample.include? 'brush'
76
+
77
+ BRUSH_DIRECTION.each do |direction|
78
+ update_io("brush_#{direction}")
79
+ brush_options = { direction: direction.to_sym }
80
+ Glitcher.new('Brush', @io, brush_options).call
81
+ end
82
+ end
83
+
84
+ def sample_change_byte
85
+ return unless @algorithms_to_sample.include? 'change_byte'
86
+
87
+ update_io("change_byte")
88
+ change_byte_options = { algorithm: 'change_byte' }
89
+ Glitcher.new('ChangeByte', @io, change_byte_options).call
90
+ end
91
+
92
+ def sample_defect
93
+ return unless @algorithms_to_sample.include? 'defect'
94
+
95
+ update_io("defect")
96
+ defect_options = { random: true, iterations: 10 }
97
+ Glitcher.new('Defect', @io, defect_options).call
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../rusty_engine/rusty_engine'
4
+
5
+ module ShatteredMachine
6
+ # Repeat pixels of a given png image.
7
+ # The logic for the pixel sorter come from the Rusty Engine.
8
+ class Slim
9
+ ALL_COLORS = %i(white black grey red green blue cyan yellow magenta).freeze
10
+
11
+ # @param options [Hash] options for slim algorithm
12
+ def initialize(options = {})
13
+ @colors = options[:colors] || ALL_COLORS
14
+ @direction = options[:direction] || :up_to_down
15
+ @probability = (options[:probability] || 95).to_s
16
+ @probability_area = options[:probability_area] || 'global'
17
+ @colors_with_proba = colors_with_proba(options[:colors_with_proba])
18
+ end
19
+
20
+ # @param input_image [string] path for image
21
+ # @param output_image [string] path for output slimed image
22
+ # @return [boolean] status of slim
23
+ def call(input_image, output_image)
24
+ RustyEngine.slim(input_image, output_image, @probability, @probability_area,
25
+ rust_formatted_direction, rust_formatted_colors,
26
+ rust_formatted_color_with_proba)
27
+ end
28
+
29
+ private
30
+
31
+ def colors_with_proba(colors_with_proba_options)
32
+ if colors_with_proba_options.nil? || colors_with_proba_options.empty?
33
+ ALL_COLORS.map { |c| [c.to_sym, @probability] }.to_h
34
+ else
35
+ colors_with_proba_options
36
+ end
37
+ end
38
+
39
+ def rust_formatted_direction
40
+ ruby_to_rust_directions = { up_to_down: '1', down_to_up: '2',
41
+ left_to_right: '3', right_to_left: '4' }
42
+ ruby_to_rust_directions[@direction]
43
+ end
44
+
45
+ def rust_formatted_colors
46
+ @colors.join(',')
47
+ end
48
+
49
+ def rust_formatted_color_with_proba
50
+ @colors_with_proba.map do |key, value|
51
+ "#{key}:#{value}"
52
+ end.join(',')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pnglitch'
4
+ module ShatteredMachine
5
+ # Use the transpose algorithm from pnglitch on a given png image.
6
+ class Transpose
7
+ # @param options [Hash] options for transpose algorithm
8
+ def initialize(options = {})
9
+ @filter = define_filter(options[:filter]) || 'average'
10
+ @transpose_force = options[:transpose_force] || 'half'
11
+ end
12
+
13
+ # @param input_image [string] path for image
14
+ # @param output_image [string] path for output transposed image
15
+ # @return [boolean] status of transpose
16
+ def call(input_image, output_image)
17
+ PNGlitch.open(input_image) do |png|
18
+ filtered_glitch(png, @filter).save output_image
19
+ end
20
+ output_image
21
+ end
22
+
23
+ private
24
+
25
+ def define_filter(filter_from_options)
26
+ return filter_from_options unless filter_from_options == 'random'
27
+
28
+ available_filters = %w(none sub up average paeth)
29
+ available_filters[rand(5)]
30
+ end
31
+
32
+ def filtered_glitch(png, custom_filter)
33
+ png.each_scanline do |scanline|
34
+ scanline.change_filter custom_filter
35
+ end
36
+ png.glitch do |data|
37
+ transpose_data(data)
38
+ end
39
+ end
40
+
41
+ def transpose_data(data)
42
+ x = data.size / 4
43
+ if @transpose_force == 'half'
44
+ data[0, x] + data[x * 2, x] + data[x * 1, x] + data[x * 3..-1]
45
+ else
46
+ data[x * 2, x] + data[0, x] + data[x * 3..-1] + data[x * 1, x]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pnglitch'
4
+ module ShatteredMachine
5
+ # Use the wrong filter algorithm from pnglitch on a given png image.
6
+ class WrongFilter
7
+ # @param options [Hash] options for wrong filter algorithm
8
+ def initialize(options = {})
9
+ @filter = options[:filter] || 'average'
10
+ end
11
+
12
+ # @param input_image [string] path for image
13
+ # @param output_image [string] path for output wrong filtered image
14
+ # @return [boolean] status of wrong filter
15
+ def call(input_image, output_image)
16
+ PNGlitch.open(input_image) do |png|
17
+ filtered_glitch(png, @filter).save output_image
18
+ end
19
+ output_image
20
+ end
21
+
22
+ private
23
+
24
+ def define_random_filter
25
+ available_filters = %w(none sub up average paeth)
26
+ available_filters[rand(5)]
27
+ end
28
+
29
+ def filtered_glitch(png, custom_filter)
30
+ png.each_scanline do |scanline|
31
+ if custom_filter == 'random'
32
+ scanline.graft define_random_filter
33
+ else
34
+ scanline.graft custom_filter
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'shattered_machine'
3
+ s.version = '0.0.1'
4
+ s.summary = 'Shattered Machine core engine'
5
+ s.description = 'Shattered Machine is an easy way to glitch PNG images using different algorithms'
6
+ s.authors = ['Flo Girardo']
7
+ s.email = 'florian@barbrousse.net'
8
+ s.files = `git ls-files`.split($/)
9
+ s.test_files = ['spec']
10
+ s.require_paths = ['lib']
11
+ s.homepage =
12
+ 'https://glitchedfactory.com/'
13
+ s.license = 'MIT'
14
+ s.add_development_dependency('rspec', '~> 3')
15
+ s.add_development_dependency('yard', '~> 0.9')
16
+ end
@@ -0,0 +1,28 @@
1
+ # brush_spec.rb
2
+
3
+ # frozen_string_literal: true
4
+
5
+ # brush_spec.rb
6
+ require 'spec_helper'
7
+
8
+ RSpec.describe ShatteredMachine::Brush do
9
+ describe '#call' do
10
+ let(:output_file) { 'spec/images/brush.png' }
11
+ let(:input_file) { 'spec/images/foo.png' }
12
+ let(:io) do
13
+ ShatteredMachine::Io.new(input_file, 'spec/images', 'brush')
14
+ end
15
+ let(:in_img) { io.png_images.first.input }
16
+ let(:out_img) { io.png_images.first.output }
17
+ subject { ShatteredMachine::Brush.new.call(in_img, out_img) }
18
+
19
+ after do
20
+ File.delete(output_file) if File.exist?(output_file)
21
+ end
22
+
23
+ it 'brush image' do
24
+ subject
25
+ expect(File.exist?(output_file)).to be true
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # change_byte_spec.rb
4
+ require 'spec_helper'
5
+
6
+ RSpec.describe ShatteredMachine::ChangeByte do
7
+ describe '#call' do
8
+ let(:output_file) { 'spec/images/change_byte.png' }
9
+ let(:input_file) { 'spec/images/foo.png' }
10
+ let(:io) do
11
+ ShatteredMachine::Io.new(input_file, 'spec/images', 'change_byte')
12
+ end
13
+ let(:in_img) { io.png_images.first.input }
14
+ let(:out_img) { io.png_images.first.output }
15
+ subject { ShatteredMachine::ChangeByte.new.call(in_img, out_img) }
16
+
17
+ after do
18
+ File.delete(output_file) if File.exist?(output_file)
19
+ end
20
+
21
+ it 'change byte on image' do
22
+ subject
23
+ expect(File.exist?(output_file)).to be true
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # converter.rb
4
+ require 'spec_helper'
5
+
6
+ RSpec.describe ShatteredMachine::Converter do
7
+ describe '#call' do
8
+ let(:converted_file) { 'spec/images/converted.png' }
9
+ let(:input_file) { 'spec/images/foo.jpg' }
10
+ let(:io) do
11
+ ShatteredMachine::Io.new(input_file, 'spec/images', 'converted')
12
+ end
13
+ subject { ShatteredMachine::Converter.new(io).call }
14
+
15
+ after do
16
+ File.delete(converted_file) if File.exist?(converted_file)
17
+ end
18
+
19
+ it 'convert image' do
20
+ subject
21
+ expect(File.exist?(converted_file)).to be true
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # defect_spec.rb
4
+ require 'spec_helper'
5
+
6
+ RSpec.describe ShatteredMachine::Defect do
7
+ describe '#call' do
8
+ let(:output_file) { 'spec/images/defect.png' }
9
+ let(:input_file) { 'spec/images/foo.png' }
10
+ let(:io) do
11
+ ShatteredMachine::Io.new(input_file, 'spec/images', 'defect')
12
+ end
13
+ let(:in_img) { io.png_images.first.input }
14
+ let(:out_img) { io.png_images.first.output }
15
+ subject { ShatteredMachine::Defect.new.call(in_img, out_img) }
16
+
17
+ after do
18
+ File.delete(output_file) if File.exist?(output_file)
19
+ end
20
+
21
+ it 'defect image' do
22
+ subject
23
+ expect(File.exist?(output_file)).to be true
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # exchange_spec.rb
4
+ require 'spec_helper'
5
+
6
+ RSpec.describe ShatteredMachine::Exchange do
7
+ describe '#call' do
8
+ let(:output_file) { 'spec/images/exchange.png' }
9
+ let(:input_file) { 'spec/images/foo.png' }
10
+ let(:io) do
11
+ ShatteredMachine::Io.new(input_file, 'spec/images', 'exchange')
12
+ end
13
+ let(:in_img) { io.png_images.first.input }
14
+ let(:out_img) { io.png_images.first.output }
15
+ subject { ShatteredMachine::Exchange.new.call(in_img, out_img) }
16
+
17
+ after do
18
+ File.delete(output_file) if File.exist?(output_file)
19
+ end
20
+
21
+ it 'exchange image' do
22
+ subject
23
+ expect(File.exist?(output_file)).to be true
24
+ end
25
+ end
26
+ end