image_optim 0.26.3 → 0.28.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 +4 -4
- data/.appveyor.yml +9 -2
- data/.rubocop.yml +34 -12
- data/.travis.yml +18 -15
- data/CHANGELOG.markdown +23 -0
- data/CONTRIBUTING.markdown +4 -1
- data/Gemfile +3 -1
- data/LICENSE.txt +1 -1
- data/README.markdown +10 -3
- data/Vagrantfile +2 -0
- data/bin/image_optim +1 -0
- data/image_optim.gemspec +5 -7
- data/lib/image_optim.rb +2 -0
- data/lib/image_optim/bin_resolver.rb +2 -0
- data/lib/image_optim/bin_resolver/bin.rb +4 -0
- data/lib/image_optim/bin_resolver/comparable_condition.rb +3 -0
- data/lib/image_optim/bin_resolver/error.rb +2 -0
- data/lib/image_optim/bin_resolver/simple_version.rb +2 -0
- data/lib/image_optim/cache.rb +2 -0
- data/lib/image_optim/cache_path.rb +21 -2
- data/lib/image_optim/cmd.rb +2 -0
- data/lib/image_optim/config.rb +6 -4
- data/lib/image_optim/configuration_error.rb +2 -0
- data/lib/image_optim/handler.rb +2 -0
- data/lib/image_optim/hash_helpers.rb +2 -0
- data/lib/image_optim/image_meta.rb +2 -0
- data/lib/image_optim/non_negative_integer_range.rb +2 -0
- data/lib/image_optim/optimized_path.rb +3 -1
- data/lib/image_optim/option_definition.rb +2 -0
- data/lib/image_optim/option_helpers.rb +2 -0
- data/lib/image_optim/path.rb +30 -5
- data/lib/image_optim/runner.rb +2 -0
- data/lib/image_optim/runner/glob_helpers.rb +3 -1
- data/lib/image_optim/runner/option_parser.rb +5 -2
- data/lib/image_optim/space.rb +3 -1
- data/lib/image_optim/true_false_nil.rb +2 -0
- data/lib/image_optim/worker.rb +1 -0
- data/lib/image_optim/worker/advpng.rb +2 -0
- data/lib/image_optim/worker/class_methods.rb +4 -0
- data/lib/image_optim/worker/gifsicle.rb +2 -0
- data/lib/image_optim/worker/jhead.rb +3 -1
- data/lib/image_optim/worker/jpegoptim.rb +8 -4
- data/lib/image_optim/worker/jpegrecompress.rb +17 -0
- data/lib/image_optim/worker/jpegtran.rb +2 -0
- data/lib/image_optim/worker/optipng.rb +4 -2
- data/lib/image_optim/worker/pngcrush.rb +4 -2
- data/lib/image_optim/worker/pngout.rb +2 -0
- data/lib/image_optim/worker/pngquant.rb +3 -0
- data/lib/image_optim/worker/svgo.rb +2 -0
- data/script/update_worker_options_in_readme +4 -3
- data/script/worker_analysis +7 -3
- data/spec/image_optim/bin_resolver/comparable_condition_spec.rb +2 -0
- data/spec/image_optim/bin_resolver/simple_version_spec.rb +2 -0
- data/spec/image_optim/bin_resolver_spec.rb +2 -0
- data/spec/image_optim/cache_path_spec.rb +71 -26
- data/spec/image_optim/cache_spec.rb +5 -1
- data/spec/image_optim/cmd_spec.rb +2 -0
- data/spec/image_optim/config_spec.rb +2 -0
- data/spec/image_optim/handler_spec.rb +2 -0
- data/spec/image_optim/hash_helpers_spec.rb +2 -0
- data/spec/image_optim/image_meta_spec.rb +2 -0
- data/spec/image_optim/optimized_path_spec.rb +2 -0
- data/spec/image_optim/option_definition_spec.rb +2 -0
- data/spec/image_optim/option_helpers_spec.rb +2 -0
- data/spec/image_optim/path_spec.rb +65 -24
- data/spec/image_optim/runner/glob_helpers_spec.rb +2 -0
- data/spec/image_optim/runner/option_parser_spec.rb +2 -0
- data/spec/image_optim/space_spec.rb +13 -11
- data/spec/image_optim/worker/jpegrecompress_spec.rb +32 -0
- data/spec/image_optim/worker/optipng_spec.rb +2 -0
- data/spec/image_optim/worker/pngquant_spec.rb +2 -0
- data/spec/image_optim/worker_spec.rb +2 -0
- data/spec/image_optim_spec.rb +7 -4
- data/spec/images/invisiblepixels/generate +1 -0
- data/spec/images/quant/generate +3 -2
- data/spec/spec_helper.rb +2 -0
- metadata +14 -13
data/lib/image_optim/cmd.rb
CHANGED
data/lib/image_optim/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/option_helpers'
|
2
4
|
require 'image_optim/configuration_error'
|
3
5
|
require 'image_optim/hash_helpers'
|
@@ -18,7 +20,7 @@ class ImageOptim
|
|
18
20
|
end
|
19
21
|
|
20
22
|
# Local config path at `./.image_optim.yml`
|
21
|
-
LOCAL_PATH = './.image_optim.yml'
|
23
|
+
LOCAL_PATH = './.image_optim.yml'
|
22
24
|
|
23
25
|
class << self
|
24
26
|
# Read options at path: expand path (warn on failure), return {} if file
|
@@ -195,10 +197,10 @@ class ImageOptim
|
|
195
197
|
when /darwin9/
|
196
198
|
Cmd.capture 'hwprefs cpu_count'
|
197
199
|
when /darwin/
|
198
|
-
if (Cmd.capture 'which hwprefs')
|
199
|
-
Cmd.capture 'hwprefs thread_count'
|
200
|
-
else
|
200
|
+
if (Cmd.capture 'which hwprefs') == ''
|
201
201
|
Cmd.capture 'sysctl -n hw.ncpu'
|
202
|
+
else
|
203
|
+
Cmd.capture 'hwprefs thread_count'
|
202
204
|
end
|
203
205
|
when /linux/
|
204
206
|
Cmd.capture 'grep -c processor /proc/cpuinfo'
|
data/lib/image_optim/handler.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/path'
|
2
4
|
|
3
5
|
class ImageOptim
|
@@ -5,7 +7,7 @@ class ImageOptim
|
|
5
7
|
class OptimizedPath < DelegateClass(Path)
|
6
8
|
def initialize(path, original_or_size = nil)
|
7
9
|
path = Path.convert(path)
|
8
|
-
|
10
|
+
super(path)
|
9
11
|
if original_or_size.is_a?(Integer)
|
10
12
|
@original = path
|
11
13
|
@original_size = original_or_size
|
data/lib/image_optim/path.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'fspath'
|
2
4
|
require 'image_optim/image_meta'
|
3
5
|
|
@@ -48,11 +50,16 @@ class ImageOptim
|
|
48
50
|
|
49
51
|
# Atomic replace dst with self
|
50
52
|
def replace(dst)
|
51
|
-
dst = self.class.
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
dst = self.class.convert(dst)
|
54
|
+
if same_dev?(dst.dirname)
|
55
|
+
dst.copy_metadata(self)
|
56
|
+
begin
|
57
|
+
rename(dst.to_s)
|
58
|
+
rescue Errno::EXDEV
|
59
|
+
replace_using_tmp_file(dst)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
replace_using_tmp_file(dst)
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
@@ -66,5 +73,23 @@ class ImageOptim
|
|
66
73
|
def self.convert(path)
|
67
74
|
path.is_a?(self) ? path : new(path)
|
68
75
|
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def same_dev?(other)
|
80
|
+
stat.dev == other.stat.dev
|
81
|
+
end
|
82
|
+
|
83
|
+
def replace_using_tmp_file(dst)
|
84
|
+
dst.temp_path_with_tmp_ext(dst.dirname) do |temp|
|
85
|
+
move(temp)
|
86
|
+
dst.copy_metadata(temp)
|
87
|
+
temp.rename(dst.to_s)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def temp_path_with_tmp_ext(*args, &block)
|
92
|
+
self.class.temp_file_path([basename.to_s, '.tmp'], *args, &block)
|
93
|
+
end
|
69
94
|
end
|
70
95
|
end
|
data/lib/image_optim/runner.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ImageOptim
|
2
4
|
class Runner
|
3
5
|
# Helper methods for glob
|
@@ -21,7 +23,7 @@ class ImageOptim
|
|
21
23
|
.* # what is left
|
22
24
|
)
|
23
25
|
\z
|
24
|
-
/x
|
26
|
+
/x.freeze
|
25
27
|
|
26
28
|
# Expand curly braces in glob as fnmatch in ruby before 2.0 doesn't
|
27
29
|
# support them
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'image_optim'
|
4
5
|
require 'image_optim/true_false_nil'
|
@@ -42,7 +43,7 @@ class ImageOptim
|
|
42
43
|
# don't try to wrap if there is too little space for description
|
43
44
|
return text if wrapped_width < 20
|
44
45
|
|
45
|
-
wrapped = ''
|
46
|
+
wrapped = ''.dup
|
46
47
|
text.split("\n").each do |line|
|
47
48
|
if line.length <= columns
|
48
49
|
wrapped << line << "\n"
|
@@ -186,7 +187,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
186
187
|
ImageOptim::Worker.klasses.each_with_index do |klass, i|
|
187
188
|
next if klass.option_definitions.empty?
|
188
189
|
|
189
|
-
op.separator nil unless i
|
190
|
+
op.separator nil unless i == 0
|
190
191
|
|
191
192
|
bin = klass.bin_sym
|
192
193
|
klass.option_definitions.each do |option_definition|
|
@@ -201,6 +202,8 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
201
202
|
[Integer, 'N']
|
202
203
|
when Array >= type
|
203
204
|
[Array, 'a,b,c']
|
205
|
+
when String >= type
|
206
|
+
[String, 'S']
|
204
207
|
when ImageOptim::NonNegativeIntegerRange == type
|
205
208
|
[type, 'M-N']
|
206
209
|
else
|
data/lib/image_optim/space.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class ImageOptim
|
2
4
|
# Present size in readable form as fixed length string
|
3
5
|
module Space
|
@@ -15,7 +17,7 @@ class ImageOptim
|
|
15
17
|
else
|
16
18
|
log_denominator = Math.log(size.abs) / Math.log(BASE)
|
17
19
|
degree = [log_denominator.floor, SIZE_SYMBOLS.length - 1].min
|
18
|
-
number_string = if degree
|
20
|
+
number_string = if degree == 0
|
19
21
|
size.to_s
|
20
22
|
else
|
21
23
|
denominator = BASE**degree
|
data/lib/image_optim/worker.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/bin_resolver'
|
2
4
|
require 'image_optim/option_definition'
|
3
5
|
|
@@ -16,6 +18,7 @@ class ImageOptim
|
|
16
18
|
|
17
19
|
# Remember all classes inheriting from this one
|
18
20
|
def inherited(base)
|
21
|
+
super
|
19
22
|
@klasses << base
|
20
23
|
end
|
21
24
|
|
@@ -34,6 +37,7 @@ class ImageOptim
|
|
34
37
|
|
35
38
|
def option(name, default, type, description = nil, &proc)
|
36
39
|
attr_reader name
|
40
|
+
|
37
41
|
OptionDefinition.new(name, default, type, description, &proc).
|
38
42
|
tap{ |option_definition| option_definitions << option_definition }
|
39
43
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
require 'exifr/jpeg'
|
3
5
|
|
@@ -7,7 +9,7 @@ class ImageOptim
|
|
7
9
|
#
|
8
10
|
# Jhead internally uses jpegtran which should be on path
|
9
11
|
class Jhead < Worker
|
10
|
-
ORIENTED = 2..8 # not top-left
|
12
|
+
ORIENTED = (2..8).freeze # not top-left
|
11
13
|
|
12
14
|
# Works on jpegs
|
13
15
|
def image_formats
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
require 'image_optim/option_helpers'
|
3
5
|
|
@@ -9,14 +11,16 @@ class ImageOptim
|
|
9
11
|
option(:allow_lossy, false, 'Allow limiting maximum quality'){ |v| !!v }
|
10
12
|
|
11
13
|
STRIP_OPTION =
|
12
|
-
option(:strip, :all, Array, 'List of
|
13
|
-
'`:
|
14
|
+
option(:strip, :all, Array, 'List of markers to strip: '\
|
15
|
+
'`:com`, '\
|
14
16
|
'`:exif`, '\
|
15
17
|
'`:iptc`, '\
|
16
|
-
'`:icc
|
18
|
+
'`:icc`, '\
|
19
|
+
'`:xmp`, '\
|
20
|
+
'`:none` or '\
|
17
21
|
'`:all`') do |v|
|
18
22
|
values = Array(v).map(&:to_s)
|
19
|
-
known_values = %w[
|
23
|
+
known_values = %w[com exif iptc icc xmp none all]
|
20
24
|
unknown_values = values - known_values
|
21
25
|
unless unknown_values.empty?
|
22
26
|
warn "Unknown markers for jpegoptim: #{unknown_values.join(', ')}"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
require 'image_optim/option_helpers'
|
3
5
|
|
@@ -24,6 +26,20 @@ class ImageOptim
|
|
24
26
|
OptionHelpers.limit_with_range(v.to_i, 0...QUALITY_NAMES.length)
|
25
27
|
end
|
26
28
|
|
29
|
+
METHOD_OPTION =
|
30
|
+
option(:method, 'ssim', 'Comparison Metric: '\
|
31
|
+
'`mpe` - Mean pixel error, '\
|
32
|
+
'`ssim` - Structural similarity, '\
|
33
|
+
'`ms-ssim` - Multi-scale structural similarity (slow!), '\
|
34
|
+
'`smallfry` - Linear-weighted BBCQ-like (may be patented)') do |v, opt_def|
|
35
|
+
if %w[mpe ssim ms-ssim smallfry].include? v
|
36
|
+
v
|
37
|
+
else
|
38
|
+
warn "Unknown method for jpegrecompress: #{v}"
|
39
|
+
opt_def.default
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
27
43
|
def used_bins
|
28
44
|
[:'jpeg-recompress']
|
29
45
|
end
|
@@ -36,6 +52,7 @@ class ImageOptim
|
|
36
52
|
def optimize(src, dst)
|
37
53
|
args = %W[
|
38
54
|
--quality #{QUALITY_NAMES[quality]}
|
55
|
+
--method #{method}
|
39
56
|
--no-copy
|
40
57
|
#{src}
|
41
58
|
#{dst}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
require 'image_optim/option_helpers'
|
3
5
|
require 'image_optim/true_false_nil'
|
@@ -37,8 +39,8 @@ class ImageOptim
|
|
37
39
|
#{dst}
|
38
40
|
]
|
39
41
|
args.unshift "-i#{interlace ? 1 : 0}" unless interlace.nil?
|
40
|
-
if resolve_bin!(:optipng).version >= '0.7'
|
41
|
-
args.unshift '-strip', 'all'
|
42
|
+
if strip && resolve_bin!(:optipng).version >= '0.7'
|
43
|
+
args.unshift '-strip', 'all'
|
42
44
|
end
|
43
45
|
execute(:optipng, *args) && optimized?(src, dst)
|
44
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
|
3
5
|
class ImageOptim
|
@@ -37,8 +39,8 @@ class ImageOptim
|
|
37
39
|
end
|
38
40
|
flags.push '-fix' if fix
|
39
41
|
flags.push '-brute' if brute
|
40
|
-
if resolve_bin!(:pngcrush).version >= '1.7.38'
|
41
|
-
flags.push '-blacken'
|
42
|
+
if blacken && resolve_bin!(:pngcrush).version >= '1.7.38'
|
43
|
+
flags.push '-blacken'
|
42
44
|
end
|
43
45
|
|
44
46
|
args = flags + %W[
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'image_optim/worker'
|
2
4
|
require 'image_optim/option_helpers'
|
3
5
|
require 'image_optim/non_negative_integer_range'
|
@@ -53,6 +55,7 @@ class ImageOptim
|
|
53
55
|
--quality=#{quality.begin}-#{quality.end}
|
54
56
|
--speed=#{speed}
|
55
57
|
--output=#{dst}
|
58
|
+
--skip-if-larger
|
56
59
|
--force
|
57
60
|
#{max_colors}
|
58
61
|
--
|
@@ -1,13 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: UTF-8
|
3
|
+
# frozen_string_literal: true
|
3
4
|
|
4
5
|
require 'bundler/setup'
|
5
6
|
|
6
7
|
require 'image_optim'
|
7
8
|
|
8
9
|
README_FILE = File.expand_path('../../README.markdown', __FILE__)
|
9
|
-
BEGIN_MARKER = '<!---<worker-options>-->'
|
10
|
-
END_MARKER = '<!---</worker-options>-->'
|
10
|
+
BEGIN_MARKER = '<!---<worker-options>-->'
|
11
|
+
END_MARKER = '<!---</worker-options>-->'
|
11
12
|
|
12
13
|
def write_worker_options(io, klass)
|
13
14
|
io.puts "### #{klass.bin_sym}:"
|
@@ -17,7 +18,7 @@ def write_worker_options(io, klass)
|
|
17
18
|
klass.option_definitions.each do |option_definition|
|
18
19
|
line = "* `:#{option_definition.name}` — #{option_definition.description}"
|
19
20
|
unless line['(defaults']
|
20
|
-
line
|
21
|
+
line += " *(defaults to #{option_definition.default_description})*"
|
21
22
|
end
|
22
23
|
io.puts line
|
23
24
|
end
|
data/script/worker_analysis
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: UTF-8
|
3
|
+
# frozen_string_literal: true
|
3
4
|
|
4
5
|
require 'bundler/setup'
|
5
6
|
|
@@ -12,7 +13,7 @@ require 'digest'
|
|
12
13
|
require 'erb'
|
13
14
|
require 'ostruct'
|
14
15
|
|
15
|
-
DIR = 'tmp'
|
16
|
+
DIR = 'tmp'
|
16
17
|
Pathname(DIR).mkpath
|
17
18
|
|
18
19
|
Array.class_eval do
|
@@ -136,13 +137,14 @@ class Analyser
|
|
136
137
|
# Delegate to worker with short id
|
137
138
|
class WorkerVariant < DelegateClass(ImageOptim::Worker)
|
138
139
|
attr_reader :name, :id, :cons_id, :required
|
140
|
+
|
139
141
|
def initialize(klass, image_optim, options)
|
140
142
|
@required = options.delete(:required)
|
141
143
|
@run_order = options.delete(:run_order)
|
142
144
|
allow_consecutive_on = Array(options.delete(:allow_consecutive_on))
|
143
145
|
@image_optim = image_optim
|
144
146
|
@name = klass.bin_sym.to_s + options_string(options)
|
145
|
-
|
147
|
+
super(klass.new(image_optim, options))
|
146
148
|
@id = klass.bin_sym.to_s + options_string(self.options)
|
147
149
|
@cons_id = [klass, allow_consecutive_on.map{ |key| [key, send(key)] }]
|
148
150
|
end
|
@@ -451,6 +453,7 @@ class Analyser
|
|
451
453
|
attr_reader :name
|
452
454
|
attr_reader :success_count
|
453
455
|
attr_reader :time, :avg_time
|
456
|
+
|
454
457
|
def initialize(name, steps)
|
455
458
|
@name = name
|
456
459
|
@success_count = steps.count(&:success)
|
@@ -459,11 +462,12 @@ class Analyser
|
|
459
462
|
end
|
460
463
|
|
461
464
|
def unused?
|
462
|
-
success_count
|
465
|
+
success_count == 0
|
463
466
|
end
|
464
467
|
end
|
465
468
|
|
466
469
|
attr_reader :name, :results, :ids2names
|
470
|
+
|
467
471
|
def initialize(name, results, ids2names)
|
468
472
|
@name = name.to_s
|
469
473
|
@results = results
|