image_optim_pack 0.1.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.
Files changed (81) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +51 -0
  4. data/.travis.yml +22 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +27 -0
  7. data/Makefile +312 -0
  8. data/README.markdown +77 -0
  9. data/Vagrantfile +13 -0
  10. data/acknowledgements/7z.txt +56 -0
  11. data/acknowledgements/advancecomp.txt +674 -0
  12. data/acknowledgements/bmp2png.txt +8 -0
  13. data/acknowledgements/cexcept.txt +8 -0
  14. data/acknowledgements/gifread.txt +14 -0
  15. data/acknowledgements/gifsicle.txt +343 -0
  16. data/acknowledgements/jhead.txt +12 -0
  17. data/acknowledgements/jpegoptim.txt +339 -0
  18. data/acknowledgements/libjpeg.txt +64 -0
  19. data/acknowledgements/libpng.txt +90 -0
  20. data/acknowledgements/optipng-authors.txt +26 -0
  21. data/acknowledgements/optipng.txt +21 -0
  22. data/acknowledgements/pngcrush.txt +35 -0
  23. data/acknowledgements/pngquant.txt +56 -0
  24. data/acknowledgements/zlib.txt +34 -0
  25. data/acknowledgements/zopfli-contributors.txt +6 -0
  26. data/acknowledgements/zopfli.txt +201 -0
  27. data/image_optim_pack.gemspec +26 -0
  28. data/lib/image_optim/pack.rb +110 -0
  29. data/lib/image_optim_pack.rb +1 -0
  30. data/script/livecheck +72 -0
  31. data/script/run +26 -0
  32. data/script/update_versions +17 -0
  33. data/script/vm_init +30 -0
  34. data/spec/image_optim/pack_spec.rb +114 -0
  35. data/spec/image_optim_spec.rb +109 -0
  36. data/spec/spec_helper.rb +0 -0
  37. data/vendor/darwin/i386/advpng +0 -0
  38. data/vendor/darwin/i386/gifsicle +0 -0
  39. data/vendor/darwin/i386/jhead +0 -0
  40. data/vendor/darwin/i386/jpegoptim +0 -0
  41. data/vendor/darwin/i386/jpegtran +0 -0
  42. data/vendor/darwin/i386/libjpeg.dylib +0 -0
  43. data/vendor/darwin/i386/libpng.dylib +0 -0
  44. data/vendor/darwin/i386/libz.dylib +0 -0
  45. data/vendor/darwin/i386/optipng +0 -0
  46. data/vendor/darwin/i386/pngcrush +0 -0
  47. data/vendor/darwin/i386/pngquant +0 -0
  48. data/vendor/darwin/x86_64/advpng +0 -0
  49. data/vendor/darwin/x86_64/gifsicle +0 -0
  50. data/vendor/darwin/x86_64/jhead +0 -0
  51. data/vendor/darwin/x86_64/jpegoptim +0 -0
  52. data/vendor/darwin/x86_64/jpegtran +0 -0
  53. data/vendor/darwin/x86_64/libjpeg.dylib +0 -0
  54. data/vendor/darwin/x86_64/libpng.dylib +0 -0
  55. data/vendor/darwin/x86_64/libz.dylib +0 -0
  56. data/vendor/darwin/x86_64/optipng +0 -0
  57. data/vendor/darwin/x86_64/pngcrush +0 -0
  58. data/vendor/darwin/x86_64/pngquant +0 -0
  59. data/vendor/linux/i686/advpng +0 -0
  60. data/vendor/linux/i686/gifsicle +0 -0
  61. data/vendor/linux/i686/jhead +0 -0
  62. data/vendor/linux/i686/jpegoptim +0 -0
  63. data/vendor/linux/i686/jpegtran +0 -0
  64. data/vendor/linux/i686/libjpeg.so +0 -0
  65. data/vendor/linux/i686/libpng.so +0 -0
  66. data/vendor/linux/i686/libz.so +0 -0
  67. data/vendor/linux/i686/optipng +0 -0
  68. data/vendor/linux/i686/pngcrush +0 -0
  69. data/vendor/linux/i686/pngquant +0 -0
  70. data/vendor/linux/x86_64/advpng +0 -0
  71. data/vendor/linux/x86_64/gifsicle +0 -0
  72. data/vendor/linux/x86_64/jhead +0 -0
  73. data/vendor/linux/x86_64/jpegoptim +0 -0
  74. data/vendor/linux/x86_64/jpegtran +0 -0
  75. data/vendor/linux/x86_64/libjpeg.so +0 -0
  76. data/vendor/linux/x86_64/libpng.so +0 -0
  77. data/vendor/linux/x86_64/libz.so +0 -0
  78. data/vendor/linux/x86_64/optipng +0 -0
  79. data/vendor/linux/x86_64/pngcrush +0 -0
  80. data/vendor/linux/x86_64/pngquant +0 -0
  81. metadata +196 -0
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'image_optim_pack'
5
+ s.version = '0.1.0'
6
+ s.summary = %q{Precompiled binaries for image_optim: advpng, gifsicle, jhead, jpegoptim, jpegtran, optipng, pngcrush, pngquant}
7
+ s.homepage = "http://github.com/toy/#{s.name}"
8
+ s.authors = ['Ivan Kuchin']
9
+ s.license = 'MIT'
10
+
11
+ s.rubyforge_project = s.name
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = %w[lib]
17
+
18
+ s.add_dependency 'image_optim', '~> 0.18'
19
+ s.add_dependency 'fspath', '~> 2.1'
20
+
21
+ s.add_development_dependency 'versionomy', '~> 0.4.4'
22
+ s.add_development_dependency 'rspec', '~> 3.0'
23
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9.3')
24
+ s.add_development_dependency 'rubocop', '~> 0.26.0'
25
+ end
26
+ end
@@ -0,0 +1,110 @@
1
+ require 'fspath'
2
+ require 'image_optim/bin_resolver/bin'
3
+
4
+ class ImageOptim
5
+ # Handle selection of directory with binaries most suitable for current
6
+ # operating system and architecture
7
+ module Pack
8
+ # Path to binary, last two parts are expect to be os/arch
9
+ class Path
10
+ # Path provided to initialize as FSPath
11
+ attr_reader :path
12
+
13
+ # Intended os
14
+ attr_reader :os
15
+
16
+ # Inteded architecture
17
+ attr_reader :arch
18
+
19
+ # Receive path, use last part for arch and last but one part for os
20
+ def initialize(path)
21
+ @path = FSPath(path)
22
+ @os = @path.dirname.basename.to_s
23
+ @arch = @path.basename.to_s
24
+ end
25
+
26
+ # Return path converted to string
27
+ def to_s
28
+ path.to_s
29
+ end
30
+
31
+ # Cached array of BinResolver::Bin instances for each bin
32
+ def bins
33
+ @bins ||= bin_paths.map do |bin_path|
34
+ BinResolver::Bin.new(bin_path.basename.to_s, bin_path.to_s)
35
+ end
36
+ end
37
+
38
+ # Return true if all bins can execute and return version
39
+ def all_bins_working?
40
+ bins.all?(&:version)
41
+ end
42
+
43
+ # Return true if all bins can't execute and return version
44
+ def all_bins_failing?
45
+ bins.none?(&:version)
46
+ end
47
+
48
+ # List of bins which can execute and return version
49
+ def working_bins
50
+ bins.select(&:version)
51
+ end
52
+
53
+ # List of bins which can't execute and return version
54
+ def failing_bins
55
+ bins.reject(&:version)
56
+ end
57
+
58
+ private
59
+
60
+ # All children except those starting with 'lib'
61
+ def bin_paths
62
+ path.children.reject{ |child| child.basename.to_s =~ /^lib/ }
63
+ end
64
+ end
65
+
66
+ # downcased `uname -s`
67
+ OS = `uname -s`.strip.downcase
68
+
69
+ # downcased `uname -m`
70
+ ARCH = `uname -m`.strip.downcase
71
+
72
+ # Path to vendor at root of image_optim_pack
73
+ VENDOR_PATH = FSPath('../../../vendor').expand_path(__FILE__)
74
+
75
+ # List of paths
76
+ PATHS = VENDOR_PATH.glob('*/*').map{ |path| Path.new(path) }
77
+
78
+ class << self
79
+ # Return path to directory with binaries
80
+ # Yields debug messages if block given
81
+ def path
82
+ ordered_by_os_arch_match.find do |path|
83
+ yield "image_optim_pack: #{debug_message(path)}" if block_given?
84
+ path.all_bins_working?
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ # Order by match of os and architecture
91
+ def ordered_by_os_arch_match
92
+ PATHS.sort_by do |path|
93
+ [path.os == OS ? 0 : 1, path.arch == ARCH ? 0 : 1]
94
+ end
95
+ end
96
+
97
+ # Messages based on success of getting versions of bins
98
+ def debug_message(path)
99
+ case
100
+ when path.all_bins_working?
101
+ "all bins from #{path} worked"
102
+ when path.all_bins_failing?
103
+ "all bins from #{path} failed"
104
+ else
105
+ "#{path.failing_bins.map(&:name).join(', ')} from #{path} failed"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1 @@
1
+ require 'image_optim/pack'
data/script/livecheck ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'uri'
5
+ require 'net/http'
6
+ require 'versionomy'
7
+
8
+ bare = ARGV.delete('--bare')
9
+
10
+ unless [1, 2].include?(ARGV.length)
11
+ abort 'expected project name and optional current version'
12
+ end
13
+
14
+ def version(string)
15
+ Versionomy.parse(string)
16
+ end
17
+
18
+ name = ARGV[0].downcase
19
+ config = YAML.load(DATA)[name]
20
+ abort "Livecheck for #{name} was not defined" unless config
21
+
22
+ uri = URI(config['url'])
23
+ version_regex = Regexp.new(config['regexp'])
24
+ current_version = ARGV[1] && version(ARGV[1])
25
+
26
+ data = Net::HTTP.get(uri)
27
+ versions = data.scan(version_regex).uniq.map{ |match| version(match[0]) }
28
+ abort 'No versions found' if versions.empty?
29
+
30
+ latest_version = versions.max
31
+ case
32
+ when bare
33
+ print latest_version
34
+ when current_version
35
+ if latest_version > current_version
36
+ puts "#{name} #{latest_version} > #{current_version}"
37
+ end
38
+ else
39
+ puts "#{name} #{latest_version}"
40
+ end
41
+
42
+ __END__
43
+ advancecomp:
44
+ url: http://sourceforge.net/projects/advancemame/rss?path=/advancecomp
45
+ regexp: /advancecomp/(\d+(?:\.\d+)*)/
46
+ gifsicle:
47
+ url: http://www.lcdf.org/gifsicle/
48
+ regexp: gifsicle-(\d+(?:\.\d+)*)\.tar\.gz
49
+ jhead:
50
+ url: http://www.sentex.net/~mwandel/jhead/
51
+ regexp: jhead-(\d+(?:\.\d+)*)
52
+ jpegoptim:
53
+ url: http://www.kokkonen.net/tjko/src/
54
+ regexp: jpegoptim-(\d+(?:\.\d+)*)
55
+ libjpeg:
56
+ url: http://www.ijg.org/files/
57
+ regexp: jpegsrc.v(.*?).tar.gz
58
+ libpng:
59
+ url: http://sourceforge.net/projects/libpng/rss
60
+ regexp: /libpng\d*/(\d+(?:\.\d+)*)/
61
+ libz:
62
+ url: http://sourceforge.net/projects/libpng/rss?path=/zlib
63
+ regexp: /zlib/(\d+(?:\.\d+)*)/
64
+ optipng:
65
+ url: http://sourceforge.net/projects/optipng/rss
66
+ regexp: /OptiPNG/optipng-(\d+(?:\.\d+)*)/
67
+ pngcrush:
68
+ url: http://sourceforge.net/projects/pmt/rss?path=/pngcrush
69
+ regexp: /pngcrush/(\d+(?:\.\d+)*)/
70
+ pngquant:
71
+ url: http://pngquant.org/releases.html
72
+ regexp: pngquant-([0-9.]+)-src.tar.bz2
data/script/run ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -xe
4
+
5
+ MAKE_COMMAND='make all -j'
6
+
7
+ for arg in "$@"; do
8
+ if [[ "$arg" =~ ^([A-Z_]+)=(.*)$ ]]; then
9
+ eval "$(printf %q=%q "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")"
10
+ fi
11
+ done
12
+
13
+ if [ "$(uname -s | tr A-Z a-z)" != 'darwin' ]; then
14
+ echo 'expected to run on Mac OS X'
15
+ exit 1
16
+ fi
17
+
18
+ for arch in i386 x86_64; do
19
+ $MAKE_COMMAND ARCH=$arch
20
+ done
21
+
22
+ for vm in linux_{i686,x86_64}; do
23
+ vagrant up $vm
24
+ vagrant ssh $vm -c "cd /vagrant; $MAKE_COMMAND"
25
+ [ -n "$NO_HALT" ] || vagrant halt
26
+ done
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'English'
4
+
5
+ tty = $stdout.tty?
6
+
7
+ ARGF.each do |line|
8
+ if (name = line[/^([A-Z]+)_VER *:= *.*/, 1])
9
+ $stderr << name << ' := ' unless tty
10
+ latest_version = `script/livecheck --bare #{name}`.strip
11
+ abort unless $CHILD_STATUS.success?
12
+ $stderr << latest_version << "\n" unless tty
13
+ $stdout << "#{name}_VER := #{latest_version}\n"
14
+ else
15
+ $stdout << line
16
+ end
17
+ end
data/script/vm_init ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+
3
+ present(){
4
+ local missing=0
5
+ for name in "$@"; do
6
+ if command -v "$name" >/dev/null 2>&1; then
7
+ echo "got $name"
8
+ else
9
+ echo "no $name"
10
+ missing=1
11
+ fi
12
+ done
13
+ [ $missing -eq 0 ] || return 127
14
+ }
15
+
16
+ if ! present make wget gcc g++ chrpath perl pkg-config automake; then
17
+ if present apt-get; then
18
+ apt-get -y install make wget gcc g++ chrpath perl pkg-config automake
19
+ elif present yum; then
20
+ yum -y install make wget gcc gcc-c++ chrpath perl pkg-config automake
21
+ else
22
+ echo 'unknown package manager'
23
+ exit 1
24
+ fi
25
+ fi
26
+
27
+ if ! make --version 2>&1 | grep -q 'GNU Make'; then
28
+ echo 'non gnu make'
29
+ exit 1
30
+ fi
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+ require 'image_optim/pack'
3
+
4
+ describe ImageOptim::Pack do
5
+ before do
6
+ stub_const('Pack', ImageOptim::Pack)
7
+ stub_const('Path', Pack::Path)
8
+ end
9
+
10
+ describe :path do
11
+ it 'returns path' do
12
+ expect(Pack.path).to be
13
+ end
14
+
15
+ it 'returns instance of Path' do
16
+ expect(Pack.path).to be_an(Path)
17
+ end
18
+
19
+ it 'yields debug messages' do
20
+ expect{ |block| Pack.path(&block) }.
21
+ to yield_with_args(/^image_optim_pack: all bins .* worked$/)
22
+ end
23
+ end
24
+
25
+ describe ImageOptim::Pack::Path do
26
+ describe :path do
27
+ it 'returns FSPath with path' do
28
+ expect(Path.new('path').path).to eq(FSPath('path'))
29
+ end
30
+ end
31
+
32
+ describe :to_s do
33
+ it 'returns stringified path' do
34
+ expect(Path.new(Pathname('path')).to_s).to eq('path')
35
+ end
36
+ end
37
+
38
+ describe :os do
39
+ it 'returns last but one part of path' do
40
+ expect(Path.new('path/futureos/Kqbit').os).to eq('futureos')
41
+ end
42
+ end
43
+
44
+ describe :arch do
45
+ it 'returns last but one part of path' do
46
+ expect(Path.new('path/futureos/Kqbit').arch).to eq('Kqbit')
47
+ end
48
+ end
49
+
50
+ describe :bins do
51
+ subject{ Pack.path.bins }
52
+
53
+ define :be_non_libraries do
54
+ match do |bin|
55
+ bin.name !~ /^lib/
56
+ end
57
+ end
58
+
59
+ it{ should be_an(Array) }
60
+
61
+ it{ should_not be_empty }
62
+
63
+ it{ should all(be_an(ImageOptim::BinResolver::Bin)) }
64
+
65
+ it{ should all(have_attributes(:version => be)) }
66
+
67
+ it{ should all(be_non_libraries) }
68
+ end
69
+
70
+ describe 'bin helpers' do
71
+ def bins_with_versions(*versions)
72
+ versions.map do |version|
73
+ double(:version => version, :inspect => version ? 'good' : 'bad')
74
+ end
75
+ end
76
+
77
+ def path_with_bins(*bins)
78
+ path = Path.new('path')
79
+ allow(path).to receive(:bins).and_return(bins)
80
+ path
81
+ end
82
+
83
+ describe 'all good bins' do
84
+ let(:bins){ bins_with_versions('1.3', '6.1.6') }
85
+ subject{ path_with_bins(*bins) }
86
+
87
+ it{ should be_all_bins_working }
88
+ it{ should_not be_all_bins_failing }
89
+ it{ should have_attributes(:working_bins => eq(bins)) }
90
+ it{ should have_attributes(:failing_bins => be_empty) }
91
+ end
92
+
93
+ describe 'mixed good/bad bins' do
94
+ let(:bins){ bins_with_versions('1.3', nil, '6.1.6', nil) }
95
+ subject{ path_with_bins(*bins) }
96
+
97
+ it{ should_not be_all_bins_working }
98
+ it{ should_not be_all_bins_failing }
99
+ it{ should have_attributes(:working_bins => bins.select(&:version)) }
100
+ it{ should have_attributes(:failing_bins => bins.reject(&:version)) }
101
+ end
102
+
103
+ describe 'all bad bins' do
104
+ let(:bins){ bins_with_versions(nil, nil) }
105
+ subject{ path_with_bins(*bins) }
106
+
107
+ it{ should_not be_all_bins_working }
108
+ it{ should be_all_bins_failing }
109
+ it{ should have_attributes(:working_bins => be_empty) }
110
+ it{ should have_attributes(:failing_bins => eq(bins)) }
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'image_optim'
3
+ require 'image_optim/cmd'
4
+ require 'tempfile'
5
+ require 'English'
6
+
7
+ describe ImageOptim do
8
+ def self.temp_copy(image)
9
+ image.temp_path.tap{ |path| image.copy(path) }
10
+ end
11
+
12
+ before do
13
+ stub_const('Cmd', ImageOptim::Cmd)
14
+ end
15
+
16
+ def flatten_animation(image)
17
+ if image.format == :gif
18
+ flattened = image.temp_path
19
+ flatten_command = %W[
20
+ convert
21
+ #{image.to_s.shellescape}
22
+ -coalesce
23
+ -append
24
+ #{flattened.to_s.shellescape}
25
+ ].join(' ')
26
+ expect(Cmd.run(flatten_command)).to be_truthy
27
+ flattened
28
+ else
29
+ image
30
+ end
31
+ end
32
+
33
+ def nrmse(image_a, image_b)
34
+ coalesce_a = flatten_animation(image_a)
35
+ coalesce_b = flatten_animation(image_b)
36
+ nrmse_command = %W[
37
+ compare
38
+ -metric RMSE
39
+ #{coalesce_a.to_s.shellescape}
40
+ #{coalesce_b.to_s.shellescape}
41
+ /dev/null
42
+ 2>&1
43
+ ].join(' ')
44
+ output = Cmd.capture(nrmse_command)
45
+ if [0, 1].include?($CHILD_STATUS.exitstatus)
46
+ output[/\((\d+(\.\d+)?)\)/, 1].to_f
47
+ else
48
+ fail "compare #{image_a} with #{image_b} failed with `#{output}`"
49
+ end
50
+ end
51
+
52
+ define :have_same_data_as do |expected|
53
+ match{ |actual| actual.binread == expected.binread }
54
+ end
55
+
56
+ define :have_size do
57
+ match(&:size?)
58
+ end
59
+
60
+ define :be_smaller_than do |expected|
61
+ match{ |actual| actual.size < expected.size }
62
+ end
63
+
64
+ define :be_pixel_identical_to do |expected|
65
+ match do |actual|
66
+ @diff = nrmse(actual, expected)
67
+ @diff == 0
68
+ end
69
+ failure_message do |actual|
70
+ "expected #{actual} to be pixel identical to #{expected}, got "\
71
+ "normalized root-mean-square error of #{@diff}"
72
+ end
73
+ end
74
+
75
+ image_optim = ImageOptim.new
76
+
77
+ # grab images from image_optim gem
78
+ image_optim_root = Gem.loaded_specs['image_optim'].gem_dir
79
+ images_dir = ImageOptim::ImagePath.new(image_optim_root) / 'spec/images'
80
+ test_images = images_dir.glob('**/*.*')
81
+
82
+ # select images which for which there are workers
83
+ test_images = test_images.select do |image|
84
+ image_optim.workers_for_image(image)
85
+ end
86
+
87
+ rotated = images_dir / 'orient/original.jpg'
88
+ rotate_images = images_dir.glob('orient/?.jpg')
89
+
90
+ copies = test_images.map{ |image| temp_copy(image) }
91
+ original_by_copy = Hash[copies.zip(test_images)]
92
+
93
+ image_optim.optimize_images(copies) do |copy, optimized|
94
+ fail 'expected copy to not be nil' if copy.nil?
95
+ original = original_by_copy[copy]
96
+
97
+ it "optimizes #{original}" do
98
+ expect(copy).to have_same_data_as(original)
99
+
100
+ expect(optimized).not_to be_nil
101
+ expect(optimized).to have_size
102
+ expect(optimized).to be_smaller_than(original)
103
+ expect(optimized).not_to have_same_data_as(original)
104
+
105
+ compare_to = rotate_images.include?(original) ? rotated : original
106
+ expect(optimized).to be_pixel_identical_to(compare_to)
107
+ end
108
+ end
109
+ end