image_optim 0.27.1 → 0.31.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 +2 -0
- data/.github/workflows/check.yml +59 -0
- data/.pre-commit-hooks.yaml +9 -0
- data/.rubocop.yml +6 -3
- data/CHANGELOG.markdown +22 -0
- data/CONTRIBUTING.markdown +5 -2
- data/Gemfile +1 -7
- data/LICENSE.txt +1 -1
- data/README.markdown +21 -10
- data/Vagrantfile +1 -1
- data/image_optim.gemspec +7 -4
- data/lib/image_optim/bin_resolver/bin.rb +10 -9
- data/lib/image_optim/cache.rb +6 -0
- data/lib/image_optim/cmd.rb +45 -6
- data/lib/image_optim/config.rb +14 -8
- data/lib/image_optim/elapsed_time.rb +26 -0
- data/lib/image_optim/errors.rb +9 -0
- data/lib/image_optim/path.rb +1 -1
- data/lib/image_optim/runner/option_parser.rb +24 -18
- data/lib/image_optim/runner.rb +1 -1
- data/lib/image_optim/timer.rb +25 -0
- data/lib/image_optim/worker/advpng.rb +7 -7
- data/lib/image_optim/worker/gifsicle.rb +11 -11
- data/lib/image_optim/worker/jhead.rb +2 -2
- data/lib/image_optim/worker/jpegoptim.rb +13 -11
- data/lib/image_optim/worker/jpegrecompress.rb +17 -2
- data/lib/image_optim/worker/jpegtran.rb +4 -4
- data/lib/image_optim/worker/optipng.rb +7 -7
- data/lib/image_optim/worker/oxipng.rb +53 -0
- data/lib/image_optim/worker/pngcrush.rb +6 -6
- data/lib/image_optim/worker/pngout.rb +7 -7
- data/lib/image_optim/worker/pngquant.rb +10 -9
- data/lib/image_optim/worker/svgo.rb +2 -2
- data/lib/image_optim/worker.rb +32 -29
- data/lib/image_optim.rb +16 -10
- data/script/update_worker_options_in_readme +2 -2
- data/script/worker_analysis +16 -18
- data/spec/image_optim/bin_resolver_spec.rb +5 -5
- data/spec/image_optim/cache_path_spec.rb +7 -10
- data/spec/image_optim/cache_spec.rb +8 -8
- data/spec/image_optim/cmd_spec.rb +64 -6
- data/spec/image_optim/config_spec.rb +36 -20
- data/spec/image_optim/elapsed_time_spec.rb +14 -0
- data/spec/image_optim/handler_spec.rb +1 -1
- data/spec/image_optim/hash_helpers_spec.rb +18 -18
- data/spec/image_optim/option_definition_spec.rb +6 -6
- data/spec/image_optim/path_spec.rb +8 -11
- data/spec/image_optim/runner/option_parser_spec.rb +4 -4
- data/spec/image_optim/timer_spec.rb +32 -0
- data/spec/image_optim/worker/jpegrecompress_spec.rb +32 -0
- data/spec/image_optim/worker/optipng_spec.rb +11 -11
- data/spec/image_optim/worker/oxipng_spec.rb +89 -0
- data/spec/image_optim/worker/pngquant_spec.rb +5 -5
- data/spec/image_optim/worker_spec.rb +17 -17
- data/spec/image_optim_spec.rb +47 -10
- data/spec/images/invisiblepixels/generate +1 -1
- data/spec/spec_helper.rb +18 -17
- metadata +36 -15
- data/.travis.yml +0 -49
@@ -38,7 +38,7 @@ class ImageOptim
|
|
38
38
|
columns = terminal_columns - 1
|
39
39
|
# 1 for distance between summary and description
|
40
40
|
# 2 for additional indent
|
41
|
-
wrapped_indent = summary_indent + ' ' * (summary_width + 1 + 2)
|
41
|
+
wrapped_indent = summary_indent + (' ' * (summary_width + 1 + 2))
|
42
42
|
wrapped_width = columns - wrapped_indent.length
|
43
43
|
# don't try to wrap if there is too little space for description
|
44
44
|
return text if wrapped_width < 20
|
@@ -84,7 +84,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
84
84
|
ImageOptim::TrueFalseNil.add_to_option_parser(op)
|
85
85
|
ImageOptim::NonNegativeIntegerRange.add_to_option_parser(op)
|
86
86
|
|
87
|
-
op.banner = <<-TEXT.gsub(/^\s*\|/, '')
|
87
|
+
op.banner = <<-TEXT.gsub(/^\s*\|/, '') # rubocop:disable Layout/DotPosition
|
88
88
|
|#{ImageOptim.full_version}
|
89
89
|
|
|
90
90
|
|Usage:
|
@@ -97,29 +97,29 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
97
97
|
TEXT
|
98
98
|
|
99
99
|
op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of '\
|
100
|
-
|
100
|
+
'default ones') do |paths|
|
101
101
|
options[:config_paths] = paths
|
102
102
|
end
|
103
103
|
|
104
104
|
op.separator nil
|
105
105
|
|
106
106
|
op.on('-r', '-R', '--recursive', 'Recursively scan directories '\
|
107
|
-
|
107
|
+
'for images') do |recursive|
|
108
108
|
options[:recursive] = recursive
|
109
109
|
end
|
110
110
|
|
111
111
|
op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories '\
|
112
|
-
|
112
|
+
'(defaults to .*)') do |glob|
|
113
113
|
options[:exclude_dir_glob] = glob
|
114
114
|
end
|
115
115
|
|
116
116
|
op.on("--exclude-file 'GLOB'", 'Glob for excluding files '\
|
117
|
-
|
117
|
+
'(defaults to .*)') do |glob|
|
118
118
|
options[:exclude_file_glob] = glob
|
119
119
|
end
|
120
120
|
|
121
121
|
op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and '\
|
122
|
-
|
122
|
+
'files') do |glob|
|
123
123
|
options[:exclude_file_glob] = options[:exclude_dir_glob] = glob
|
124
124
|
end
|
125
125
|
|
@@ -130,19 +130,19 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
130
130
|
end
|
131
131
|
|
132
132
|
op.on('--[no-]threads N', Integer, 'Number of threads or disable '\
|
133
|
-
|
133
|
+
'(defaults to number of processors)') do |threads|
|
134
134
|
options[:threads] = threads
|
135
135
|
end
|
136
136
|
|
137
137
|
op.on('--[no-]nice N', Integer, 'Nice level, priority of all used tools '\
|
138
|
-
|
139
|
-
|
138
|
+
'with higher value meaning lower priority, in range -20..19, negative '\
|
139
|
+
'values can be set only if run by root user (defaults to 10)') do |nice|
|
140
140
|
options[:nice] = nice
|
141
141
|
end
|
142
142
|
|
143
143
|
op.on('--[no-]pack', 'Require image_optim_pack or disable it, '\
|
144
|
-
|
145
|
-
|
144
|
+
'by default image_optim_pack will be used if available, '\
|
145
|
+
'will turn on skip-missing-workers unless explicitly disabled') do |pack|
|
146
146
|
options[:pack] = pack
|
147
147
|
end
|
148
148
|
|
@@ -150,12 +150,12 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
150
150
|
op.separator ' Caching:'
|
151
151
|
|
152
152
|
op.on('--cache-dir DIR', 'Cache optimized images '\
|
153
|
-
|
153
|
+
'into the specified directory') do |cache_dir|
|
154
154
|
options[:cache_dir] = cache_dir
|
155
155
|
end
|
156
156
|
|
157
157
|
op.on('--cache-worker-digests', 'Cache worker digests '\
|
158
|
-
|
158
|
+
'(updating workers invalidates cache)') do |cache_worker_digests|
|
159
159
|
options[:cache_worker_digests] = cache_worker_digests
|
160
160
|
end
|
161
161
|
|
@@ -163,7 +163,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
163
163
|
op.separator ' Disabling workers:'
|
164
164
|
|
165
165
|
op.on('--[no-]skip-missing-workers', 'Skip workers with missing or '\
|
166
|
-
|
166
|
+
'problematic binaries') do |skip|
|
167
167
|
options[:skip_missing_workers] = skip
|
168
168
|
end
|
169
169
|
|
@@ -178,10 +178,14 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
178
178
|
op.separator ' Worker options:'
|
179
179
|
|
180
180
|
op.on('--allow-lossy', 'Allow lossy workers and '\
|
181
|
-
|
181
|
+
'optimizations') do |allow_lossy|
|
182
182
|
options[:allow_lossy] = allow_lossy
|
183
183
|
end
|
184
184
|
|
185
|
+
op.on('--timeout N', Float, 'Maximum time in seconds to spend on one image') do |timeout|
|
186
|
+
options[:timeout] = timeout
|
187
|
+
end
|
188
|
+
|
185
189
|
op.separator nil
|
186
190
|
|
187
191
|
ImageOptim::Worker.klasses.each_with_index do |klass, i|
|
@@ -202,6 +206,8 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
202
206
|
[Integer, 'N']
|
203
207
|
when Array >= type
|
204
208
|
[Array, 'a,b,c']
|
209
|
+
when String >= type
|
210
|
+
[String, 'S']
|
205
211
|
when ImageOptim::NonNegativeIntegerRange == type
|
206
212
|
[type, 'M-N']
|
207
213
|
else
|
@@ -224,8 +230,8 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
224
230
|
op.separator ' Common options:'
|
225
231
|
|
226
232
|
op.on_tail('-v', '--verbose', 'Verbose output (show global and worker '\
|
227
|
-
|
228
|
-
|
233
|
+
'config, binary resolution log, information about each tool invocation, '\
|
234
|
+
'backtrace of exception)') do
|
229
235
|
options[:verbose] = true
|
230
236
|
end
|
231
237
|
|
data/lib/image_optim/runner.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_optim/elapsed_time'
|
4
|
+
|
5
|
+
class ImageOptim
|
6
|
+
# Hold start time and timeout
|
7
|
+
class Timer
|
8
|
+
include ElapsedTime
|
9
|
+
|
10
|
+
def initialize(seconds)
|
11
|
+
@start = now
|
12
|
+
@seconds = seconds
|
13
|
+
end
|
14
|
+
|
15
|
+
def elapsed
|
16
|
+
now - @start
|
17
|
+
end
|
18
|
+
|
19
|
+
def left
|
20
|
+
@seconds - elapsed
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :to_f, :left
|
24
|
+
end
|
25
|
+
end
|
@@ -9,11 +9,11 @@ class ImageOptim
|
|
9
9
|
class Advpng < Worker
|
10
10
|
LEVEL_OPTION =
|
11
11
|
option(:level, 4, 'Compression level: '\
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
'`0` - don\'t compress, '\
|
13
|
+
'`1` - fast, '\
|
14
|
+
'`2` - normal, '\
|
15
|
+
'`3` - extra, '\
|
16
|
+
'`4` - extreme') do |v|
|
17
17
|
OptionHelpers.limit_with_range(v.to_i, 0..4)
|
18
18
|
end
|
19
19
|
|
@@ -21,7 +21,7 @@ class ImageOptim
|
|
21
21
|
4
|
22
22
|
end
|
23
23
|
|
24
|
-
def optimize(src, dst)
|
24
|
+
def optimize(src, dst, options = {})
|
25
25
|
src.copy(dst)
|
26
26
|
args = %W[
|
27
27
|
--recompress
|
@@ -30,7 +30,7 @@ class ImageOptim
|
|
30
30
|
--
|
31
31
|
#{dst}
|
32
32
|
]
|
33
|
-
execute(:advpng,
|
33
|
+
execute(:advpng, args, options) && optimized?(src, dst)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -12,32 +12,32 @@ class ImageOptim
|
|
12
12
|
return super if options.key?(:interlace)
|
13
13
|
|
14
14
|
[false, true].map do |interlace|
|
15
|
-
new(image_optim, options.merge(:
|
15
|
+
new(image_optim, options.merge(interlace: interlace))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
INTERLACE_OPTION =
|
20
20
|
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
'`true` - interlace on, '\
|
22
|
+
'`false` - interlace off, '\
|
23
|
+
'`nil` - as is in original image '\
|
24
|
+
'(defaults to running two instances, one with interlace off and '\
|
25
|
+
'one with on)') do |v|
|
26
26
|
TrueFalseNil.convert(v)
|
27
27
|
end
|
28
28
|
|
29
29
|
LEVEL_OPTION =
|
30
30
|
option(:level, 3, 'Compression level: '\
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
'`1` - light and fast, '\
|
32
|
+
'`2` - normal, '\
|
33
|
+
'`3` - heavy (slower)') do |v|
|
34
34
|
OptionHelpers.limit_with_range(v.to_i, 1..3)
|
35
35
|
end
|
36
36
|
|
37
37
|
CAREFUL_OPTION =
|
38
38
|
option(:careful, false, 'Avoid bugs with some software'){ |v| !!v }
|
39
39
|
|
40
|
-
def optimize(src, dst)
|
40
|
+
def optimize(src, dst, options = {})
|
41
41
|
args = %W[
|
42
42
|
--output=#{dst}
|
43
43
|
--no-comments
|
@@ -58,7 +58,7 @@ class ImageOptim
|
|
58
58
|
end
|
59
59
|
args.unshift '--careful' if careful
|
60
60
|
args.unshift "--optimize=#{level}" if level
|
61
|
-
execute(:gifsicle,
|
61
|
+
execute(:gifsicle, args, options) && optimized?(src, dst)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -25,7 +25,7 @@ class ImageOptim
|
|
25
25
|
[:jhead, :jpegtran]
|
26
26
|
end
|
27
27
|
|
28
|
-
def optimize(src, dst)
|
28
|
+
def optimize(src, dst, options = {})
|
29
29
|
return false unless oriented?(src)
|
30
30
|
|
31
31
|
src.copy(dst)
|
@@ -34,7 +34,7 @@ class ImageOptim
|
|
34
34
|
#{dst}
|
35
35
|
]
|
36
36
|
resolve_bin!(:jpegtran)
|
37
|
-
execute(:jhead,
|
37
|
+
execute(:jhead, args, options) && dst.size?
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
@@ -11,14 +11,16 @@ class ImageOptim
|
|
11
11
|
option(:allow_lossy, false, 'Allow limiting maximum quality'){ |v| !!v }
|
12
12
|
|
13
13
|
STRIP_OPTION =
|
14
|
-
option(:strip, :all, Array, 'List of
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
option(:strip, :all, Array, 'List of markers to strip: '\
|
15
|
+
'`:com`, '\
|
16
|
+
'`:exif`, '\
|
17
|
+
'`:iptc`, '\
|
18
|
+
'`:icc`, '\
|
19
|
+
'`:xmp`, '\
|
20
|
+
'`:none` or '\
|
21
|
+
'`:all`') do |v|
|
20
22
|
values = Array(v).map(&:to_s)
|
21
|
-
known_values = %w[
|
23
|
+
known_values = %w[com exif iptc icc xmp none all]
|
22
24
|
unknown_values = values - known_values
|
23
25
|
unless unknown_values.empty?
|
24
26
|
warn "Unknown markers for jpegoptim: #{unknown_values.join(', ')}"
|
@@ -28,13 +30,13 @@ class ImageOptim
|
|
28
30
|
|
29
31
|
MAX_QUALITY_OPTION =
|
30
32
|
option(:max_quality, 100, 'Maximum image quality factor '\
|
31
|
-
|
33
|
+
'`0`..`100`, ignored in default/lossless mode') do |v, opt_def|
|
32
34
|
if allow_lossy
|
33
35
|
OptionHelpers.limit_with_range(v.to_i, 0..100)
|
34
36
|
else
|
35
37
|
if v != opt_def.default
|
36
38
|
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
37
|
-
|
39
|
+
'in lossless mode'
|
38
40
|
end
|
39
41
|
opt_def.default
|
40
42
|
end
|
@@ -45,7 +47,7 @@ class ImageOptim
|
|
45
47
|
max_quality < 100 ? -1 : 0
|
46
48
|
end
|
47
49
|
|
48
|
-
def optimize(src, dst)
|
50
|
+
def optimize(src, dst, options = {})
|
49
51
|
src.copy(dst)
|
50
52
|
args = %W[
|
51
53
|
--quiet
|
@@ -56,7 +58,7 @@ class ImageOptim
|
|
56
58
|
args.unshift "--strip-#{strip_marker}"
|
57
59
|
end
|
58
60
|
args.unshift "--max=#{max_quality}" if max_quality < 100
|
59
|
-
execute(:jpegoptim,
|
61
|
+
execute(:jpegoptim, args, options) && optimized?(src, dst)
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
@@ -26,6 +26,20 @@ class ImageOptim
|
|
26
26
|
OptionHelpers.limit_with_range(v.to_i, 0...QUALITY_NAMES.length)
|
27
27
|
end
|
28
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
|
+
|
29
43
|
def used_bins
|
30
44
|
[:'jpeg-recompress']
|
31
45
|
end
|
@@ -35,14 +49,15 @@ class ImageOptim
|
|
35
49
|
-5
|
36
50
|
end
|
37
51
|
|
38
|
-
def optimize(src, dst)
|
52
|
+
def optimize(src, dst, options = {})
|
39
53
|
args = %W[
|
40
54
|
--quality #{QUALITY_NAMES[quality]}
|
55
|
+
--method #{method}
|
41
56
|
--no-copy
|
42
57
|
#{src}
|
43
58
|
#{dst}
|
44
59
|
]
|
45
|
-
execute(:'jpeg-recompress',
|
60
|
+
execute(:'jpeg-recompress', args, options) && optimized?(src, dst)
|
46
61
|
end
|
47
62
|
end
|
48
63
|
end
|
@@ -17,13 +17,13 @@ class ImageOptim
|
|
17
17
|
|
18
18
|
JPEGRESCAN_OPTION =
|
19
19
|
option(:jpegrescan, true, 'Use jpegtran through jpegrescan, '\
|
20
|
-
|
20
|
+
'ignore progressive option'){ |v| !!v }
|
21
21
|
|
22
22
|
def used_bins
|
23
23
|
jpegrescan ? [:jpegtran, :jpegrescan] : [:jpegtran]
|
24
24
|
end
|
25
25
|
|
26
|
-
def optimize(src, dst)
|
26
|
+
def optimize(src, dst, options = {})
|
27
27
|
if jpegrescan
|
28
28
|
args = %W[
|
29
29
|
#{src}
|
@@ -31,7 +31,7 @@ class ImageOptim
|
|
31
31
|
]
|
32
32
|
args.unshift '-s' unless copy_chunks
|
33
33
|
resolve_bin!(:jpegtran)
|
34
|
-
execute(:jpegrescan,
|
34
|
+
execute(:jpegrescan, args, options) && optimized?(src, dst)
|
35
35
|
else
|
36
36
|
args = %W[
|
37
37
|
-optimize
|
@@ -40,7 +40,7 @@ class ImageOptim
|
|
40
40
|
]
|
41
41
|
args.unshift '-copy', (copy_chunks ? 'all' : 'none')
|
42
42
|
args.unshift '-progressive' if progressive
|
43
|
-
execute(:jpegtran,
|
43
|
+
execute(:jpegtran, args, options) && optimized?(src, dst)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -10,16 +10,16 @@ class ImageOptim
|
|
10
10
|
class Optipng < Worker
|
11
11
|
LEVEL_OPTION =
|
12
12
|
option(:level, 6, 'Optimization level preset: '\
|
13
|
-
|
14
|
-
|
13
|
+
'`0` is least, '\
|
14
|
+
'`7` is best') do |v|
|
15
15
|
OptionHelpers.limit_with_range(v.to_i, 0..7)
|
16
16
|
end
|
17
17
|
|
18
18
|
INTERLACE_OPTION =
|
19
19
|
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
'`true` - interlace on, '\
|
21
|
+
'`false` - interlace off, '\
|
22
|
+
'`nil` - as is in original image') do |v|
|
23
23
|
TrueFalseNil.convert(v)
|
24
24
|
end
|
25
25
|
|
@@ -30,7 +30,7 @@ class ImageOptim
|
|
30
30
|
-4
|
31
31
|
end
|
32
32
|
|
33
|
-
def optimize(src, dst)
|
33
|
+
def optimize(src, dst, options = {})
|
34
34
|
src.copy(dst)
|
35
35
|
args = %W[
|
36
36
|
-o #{level}
|
@@ -42,7 +42,7 @@ class ImageOptim
|
|
42
42
|
if strip && resolve_bin!(:optipng).version >= '0.7'
|
43
43
|
args.unshift '-strip', 'all'
|
44
44
|
end
|
45
|
-
execute(:optipng,
|
45
|
+
execute(:optipng, args, options) && optimized?(src, dst)
|
46
46
|
end
|
47
47
|
|
48
48
|
def optimized?(src, dst)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'image_optim/worker'
|
4
|
+
require 'image_optim/option_helpers'
|
5
|
+
require 'image_optim/true_false_nil'
|
6
|
+
|
7
|
+
class ImageOptim
|
8
|
+
class Worker
|
9
|
+
# https://github.com/shssoichiro/oxipng
|
10
|
+
class Oxipng < Worker
|
11
|
+
LEVEL_OPTION =
|
12
|
+
option(:level, 3, 'Optimization level preset: '\
|
13
|
+
'`0` is least, '\
|
14
|
+
'`6` is best') do |v|
|
15
|
+
OptionHelpers.limit_with_range(v.to_i, 0..6)
|
16
|
+
end
|
17
|
+
|
18
|
+
INTERLACE_OPTION =
|
19
|
+
option(:interlace, false, TrueFalseNil, 'Interlace: '\
|
20
|
+
'`true` - interlace on, '\
|
21
|
+
'`false` - interlace off, '\
|
22
|
+
'`nil` - as is in original image') do |v|
|
23
|
+
TrueFalseNil.convert(v)
|
24
|
+
end
|
25
|
+
|
26
|
+
STRIP_OPTION =
|
27
|
+
option(:strip, true, 'Remove all auxiliary chunks'){ |v| !!v }
|
28
|
+
|
29
|
+
def run_order
|
30
|
+
-4
|
31
|
+
end
|
32
|
+
|
33
|
+
def optimize(src, dst, options = {})
|
34
|
+
src.copy(dst)
|
35
|
+
args = %W[
|
36
|
+
-o #{level}
|
37
|
+
--quiet
|
38
|
+
--
|
39
|
+
#{dst}
|
40
|
+
]
|
41
|
+
args.unshift "-i#{interlace ? 1 : 0}" unless interlace.nil?
|
42
|
+
if strip
|
43
|
+
args.unshift '--strip', 'all'
|
44
|
+
end
|
45
|
+
execute(:oxipng, args, options) && optimized?(src, dst)
|
46
|
+
end
|
47
|
+
|
48
|
+
def optimized?(src, dst)
|
49
|
+
interlace ? dst.size? : super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -8,18 +8,18 @@ class ImageOptim
|
|
8
8
|
class Pngcrush < Worker
|
9
9
|
CHUNKS_OPTION =
|
10
10
|
option(:chunks, :alla, Array, 'List of chunks to remove or '\
|
11
|
-
|
12
|
-
|
11
|
+
'`:alla` - all except tRNS/transparency or '\
|
12
|
+
'`:allb` - all except tRNS and gAMA/gamma') do |v|
|
13
13
|
Array(v).map(&:to_s)
|
14
14
|
end
|
15
15
|
|
16
16
|
FIX_OPTION =
|
17
17
|
option(:fix, false, 'Fix otherwise fatal conditions '\
|
18
|
-
|
18
|
+
'such as bad CRCs'){ |v| !!v }
|
19
19
|
|
20
20
|
BRUTE_OPTION =
|
21
21
|
option(:brute, false, 'Brute force try all methods, '\
|
22
|
-
|
22
|
+
'very time-consuming and generally not worthwhile'){ |v| !!v }
|
23
23
|
|
24
24
|
BLACKEN_OPTION =
|
25
25
|
option(:blacken, true, 'Blacken fully transparent pixels'){ |v| !!v }
|
@@ -28,7 +28,7 @@ class ImageOptim
|
|
28
28
|
-6
|
29
29
|
end
|
30
30
|
|
31
|
-
def optimize(src, dst)
|
31
|
+
def optimize(src, dst, options = {})
|
32
32
|
flags = %w[
|
33
33
|
-reduce
|
34
34
|
-cc
|
@@ -49,7 +49,7 @@ class ImageOptim
|
|
49
49
|
#{dst}
|
50
50
|
]
|
51
51
|
|
52
|
-
execute(:pngcrush,
|
52
|
+
execute(:pngcrush, args, options) && optimized?(src, dst)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
@@ -12,11 +12,11 @@ class ImageOptim
|
|
12
12
|
|
13
13
|
STRATEGY_OPTION =
|
14
14
|
option(:strategy, 0, 'Strategy: '\
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
'`0` - xtreme, '\
|
16
|
+
'`1` - intense, '\
|
17
|
+
'`2` - longest Match, '\
|
18
|
+
'`3` - huffman Only, '\
|
19
|
+
'`4` - uncompressed') do |v|
|
20
20
|
OptionHelpers.limit_with_range(v.to_i, 0..4)
|
21
21
|
end
|
22
22
|
|
@@ -24,7 +24,7 @@ class ImageOptim
|
|
24
24
|
2
|
25
25
|
end
|
26
26
|
|
27
|
-
def optimize(src, dst)
|
27
|
+
def optimize(src, dst, options = {})
|
28
28
|
args = %W[
|
29
29
|
-k#{copy_chunks ? 1 : 0}
|
30
30
|
-s#{strategy}
|
@@ -33,7 +33,7 @@ class ImageOptim
|
|
33
33
|
#{src}
|
34
34
|
#{dst}
|
35
35
|
]
|
36
|
-
execute(:pngout,
|
36
|
+
execute(:pngout, args, options) && optimized?(src, dst)
|
37
37
|
rescue SignalException => e
|
38
38
|
raise unless Signal.list.key(e.signo) == 'SEGV'
|
39
39
|
raise unless resolve_bin!(:pngout).version <= '20150920'
|
@@ -19,9 +19,9 @@ class ImageOptim
|
|
19
19
|
QUALITY_OPTION =
|
20
20
|
option(:quality, '`100..100`, `0..100` in lossy mode',
|
21
21
|
NonNegativeIntegerRange, 'min..max - don\'t '\
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
'save below min, use less colors below max (both in range `0..100`; '\
|
23
|
+
'in yaml - `!ruby/range 0..100`), ignored in default/lossless '\
|
24
|
+
'mode') do |v, opt_def|
|
25
25
|
if allow_lossy
|
26
26
|
if v == opt_def.default
|
27
27
|
0..100
|
@@ -32,7 +32,7 @@ class ImageOptim
|
|
32
32
|
else
|
33
33
|
if v != opt_def.default
|
34
34
|
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
35
|
-
|
35
|
+
'in lossless mode'
|
36
36
|
end
|
37
37
|
100..100
|
38
38
|
end
|
@@ -40,9 +40,9 @@ class ImageOptim
|
|
40
40
|
|
41
41
|
SPEED_OPTION =
|
42
42
|
option(:speed, 3, 'speed/quality trade-off: '\
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
'`1` - slow, '\
|
44
|
+
'`3` - default, '\
|
45
|
+
'`11` - fast & rough') do |v|
|
46
46
|
OptionHelpers.limit_with_range(v.to_i, 1..11)
|
47
47
|
end
|
48
48
|
|
@@ -50,17 +50,18 @@ class ImageOptim
|
|
50
50
|
-2
|
51
51
|
end
|
52
52
|
|
53
|
-
def optimize(src, dst)
|
53
|
+
def optimize(src, dst, options = {})
|
54
54
|
args = %W[
|
55
55
|
--quality=#{quality.begin}-#{quality.end}
|
56
56
|
--speed=#{speed}
|
57
57
|
--output=#{dst}
|
58
|
+
--skip-if-larger
|
58
59
|
--force
|
59
60
|
#{max_colors}
|
60
61
|
--
|
61
62
|
#{src}
|
62
63
|
]
|
63
|
-
execute(:pngquant,
|
64
|
+
execute(:pngquant, args, options) && optimized?(src, dst)
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -16,7 +16,7 @@ class ImageOptim
|
|
16
16
|
Array(v).map(&:to_s)
|
17
17
|
end
|
18
18
|
|
19
|
-
def optimize(src, dst)
|
19
|
+
def optimize(src, dst, options = {})
|
20
20
|
args = %W[
|
21
21
|
--input #{src}
|
22
22
|
--output #{dst}
|
@@ -27,7 +27,7 @@ class ImageOptim
|
|
27
27
|
enable_plugins.each do |plugin_name|
|
28
28
|
args.unshift "--enable=#{plugin_name}"
|
29
29
|
end
|
30
|
-
execute(:svgo,
|
30
|
+
execute(:svgo, args, options) && optimized?(src, dst)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|