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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.appveyor.yml +2 -0
  3. data/.github/workflows/check.yml +59 -0
  4. data/.pre-commit-hooks.yaml +9 -0
  5. data/.rubocop.yml +6 -3
  6. data/CHANGELOG.markdown +22 -0
  7. data/CONTRIBUTING.markdown +5 -2
  8. data/Gemfile +1 -7
  9. data/LICENSE.txt +1 -1
  10. data/README.markdown +21 -10
  11. data/Vagrantfile +1 -1
  12. data/image_optim.gemspec +7 -4
  13. data/lib/image_optim/bin_resolver/bin.rb +10 -9
  14. data/lib/image_optim/cache.rb +6 -0
  15. data/lib/image_optim/cmd.rb +45 -6
  16. data/lib/image_optim/config.rb +14 -8
  17. data/lib/image_optim/elapsed_time.rb +26 -0
  18. data/lib/image_optim/errors.rb +9 -0
  19. data/lib/image_optim/path.rb +1 -1
  20. data/lib/image_optim/runner/option_parser.rb +24 -18
  21. data/lib/image_optim/runner.rb +1 -1
  22. data/lib/image_optim/timer.rb +25 -0
  23. data/lib/image_optim/worker/advpng.rb +7 -7
  24. data/lib/image_optim/worker/gifsicle.rb +11 -11
  25. data/lib/image_optim/worker/jhead.rb +2 -2
  26. data/lib/image_optim/worker/jpegoptim.rb +13 -11
  27. data/lib/image_optim/worker/jpegrecompress.rb +17 -2
  28. data/lib/image_optim/worker/jpegtran.rb +4 -4
  29. data/lib/image_optim/worker/optipng.rb +7 -7
  30. data/lib/image_optim/worker/oxipng.rb +53 -0
  31. data/lib/image_optim/worker/pngcrush.rb +6 -6
  32. data/lib/image_optim/worker/pngout.rb +7 -7
  33. data/lib/image_optim/worker/pngquant.rb +10 -9
  34. data/lib/image_optim/worker/svgo.rb +2 -2
  35. data/lib/image_optim/worker.rb +32 -29
  36. data/lib/image_optim.rb +16 -10
  37. data/script/update_worker_options_in_readme +2 -2
  38. data/script/worker_analysis +16 -18
  39. data/spec/image_optim/bin_resolver_spec.rb +5 -5
  40. data/spec/image_optim/cache_path_spec.rb +7 -10
  41. data/spec/image_optim/cache_spec.rb +8 -8
  42. data/spec/image_optim/cmd_spec.rb +64 -6
  43. data/spec/image_optim/config_spec.rb +36 -20
  44. data/spec/image_optim/elapsed_time_spec.rb +14 -0
  45. data/spec/image_optim/handler_spec.rb +1 -1
  46. data/spec/image_optim/hash_helpers_spec.rb +18 -18
  47. data/spec/image_optim/option_definition_spec.rb +6 -6
  48. data/spec/image_optim/path_spec.rb +8 -11
  49. data/spec/image_optim/runner/option_parser_spec.rb +4 -4
  50. data/spec/image_optim/timer_spec.rb +32 -0
  51. data/spec/image_optim/worker/jpegrecompress_spec.rb +32 -0
  52. data/spec/image_optim/worker/optipng_spec.rb +11 -11
  53. data/spec/image_optim/worker/oxipng_spec.rb +89 -0
  54. data/spec/image_optim/worker/pngquant_spec.rb +5 -5
  55. data/spec/image_optim/worker_spec.rb +17 -17
  56. data/spec/image_optim_spec.rb +47 -10
  57. data/spec/images/invisiblepixels/generate +1 -1
  58. data/spec/spec_helper.rb +18 -17
  59. metadata +36 -15
  60. 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
- 'default ones') do |paths|
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
- 'for images') do |recursive|
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
- '(defaults to .*)') do |glob|
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
- '(defaults to .*)') do |glob|
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
- 'files') do |glob|
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
- '(defaults to number of processors)') do |threads|
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
- '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|
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
- 'by default image_optim_pack will be used if available, '\
145
- 'will turn on skip-missing-workers unless explicitly disabled') do |pack|
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
- 'into the specified directory') do |cache_dir|
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
- '(updating workers invalidates cache)') do |cache_worker_digests|
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
- 'problematic binaries') do |skip|
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
- 'optimizations') do |allow_lossy|
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
- 'config, binary resolution log, information about each tool invocation, '\
228
- 'backtrace of exception)') do
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
 
@@ -38,7 +38,7 @@ class ImageOptim
38
38
  if size_a == size_b
39
39
  "------ #{Space::EMPTY_SPACE}"
40
40
  else
41
- percent = 100 - 100.0 * size_b / size_a
41
+ percent = 100 - (100.0 * size_b / size_a)
42
42
  space = Space.space(size_a - size_b)
43
43
  format('%5.2f%% %s', percent, space)
44
44
  end
@@ -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
- '`0` - don\'t compress, '\
13
- '`1` - fast, '\
14
- '`2` - normal, '\
15
- '`3` - extra, '\
16
- '`4` - extreme') do |v|
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, *args) && optimized?(src, dst)
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(:interlace => interlace))
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
- '`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|
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
- '`1` - light and fast, '\
32
- '`2` - normal, '\
33
- '`3` - heavy (slower)') do |v|
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, *args) && optimized?(src, dst)
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, *args) && dst.size?
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 extra markers to strip: '\
15
- '`:comments`, '\
16
- '`:exif`, '\
17
- '`:iptc`, '\
18
- '`:icc` or '\
19
- '`:all`') do |v|
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[all comments exif iptc icc]
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
- '`0`..`100`, ignored in default/lossless mode') do |v, opt_def|
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
- 'in lossless mode'
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, *args) && optimized?(src, dst)
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', *args) && optimized?(src, dst)
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
- 'ignore progressive option'){ |v| !!v }
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, *args) && optimized?(src, dst)
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, *args) && optimized?(src, dst)
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
- '`0` is least, '\
14
- '`7` is best') do |v|
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
- '`true` - interlace on, '\
21
- '`false` - interlace off, '\
22
- '`nil` - as is in original image') do |v|
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, *args) && optimized?(src, dst)
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
- '`:alla` - all except tRNS/transparency or '\
12
- '`:allb` - all except tRNS and gAMA/gamma') do |v|
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
- 'such as bad CRCs'){ |v| !!v }
18
+ 'such as bad CRCs'){ |v| !!v }
19
19
 
20
20
  BRUTE_OPTION =
21
21
  option(:brute, false, 'Brute force try all methods, '\
22
- 'very time-consuming and generally not worthwhile'){ |v| !!v }
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, *args) && optimized?(src, dst)
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
- '`0` - xtreme, '\
16
- '`1` - intense, '\
17
- '`2` - longest Match, '\
18
- '`3` - huffman Only, '\
19
- '`4` - uncompressed') do |v|
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, *args) && optimized?(src, dst)
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
- '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|
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
- 'in lossless mode'
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
- '`1` - slow, '\
44
- '`3` - default, '\
45
- '`11` - fast & rough') do |v|
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, *args) && optimized?(src, dst)
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, *args) && optimized?(src, dst)
30
+ execute(:svgo, args, options) && optimized?(src, dst)
31
31
  end
32
32
  end
33
33
  end