image_optim 0.24.3 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 76bd58bfc7147d344de3180b31eaf30572fdb940
4
- data.tar.gz: 477c8fb36bab5dcd8abc0fa9714404453e19fc7a
3
+ metadata.gz: 0274d7bf579938c5c208f39fb85d60781d727566
4
+ data.tar.gz: e27be28692c75bc6456071311a3bed6fa3a8a216
5
5
  SHA512:
6
- metadata.gz: c4dbea61152a4b7ae6fb1dd97f2019dd474073863009b7af55000f9e7657e691c43dbc7e11917f477f674536aba80d4e7d9dff588c0026f5e516a215941a5f6c
7
- data.tar.gz: f120068d0b452500630247676dedbe475b8299127d3aa08e479308fea2731b7f3b06b63291858482d4f90ded3f9137975e11b9e66aa5abb0b067dd53e167e348
6
+ metadata.gz: e8b6ec09460fa0f07b11ab3930e4b55d52c6ab313caf1a04b55ad5244b20430686f1833a0e66e3501f3cbeb64893b7e35cc9fa75edcb3810b93fb8942d853a20
7
+ data.tar.gz: 7550e19b5a4ec5821e3b4603bab89d48f09fe6c24e65beb79130c5eda8fa2aa5f7e3e7fe08630bef0021ddcf3affc50964662db1e339d8e5b4c96c10ca08fea7
@@ -6,6 +6,33 @@ AllCops:
6
6
  Bundler/OrderedGems:
7
7
  Enabled: false
8
8
 
9
+ Layout/AccessModifierIndentation:
10
+ EnforcedStyle: outdent
11
+
12
+ Layout/CaseIndentation:
13
+ EnforcedStyle: end
14
+
15
+ Layout/DotPosition:
16
+ EnforcedStyle: trailing
17
+
18
+ Layout/IndentArray:
19
+ EnforcedStyle: consistent
20
+
21
+ Layout/IndentAssignment:
22
+ Enabled: false
23
+
24
+ Layout/IndentHash:
25
+ EnforcedStyle: consistent
26
+
27
+ Layout/IndentHeredoc:
28
+ Enabled: false
29
+
30
+ Layout/SpaceBeforeBlockBraces:
31
+ EnforcedStyle: no_space
32
+
33
+ Layout/SpaceInsideHashLiteralBraces:
34
+ EnforcedStyle: no_space
35
+
9
36
  Lint/AmbiguousBlockAssociation:
10
37
  Exclude:
11
38
  - spec/**/*_spec.rb
@@ -41,18 +68,9 @@ Security/MarshalLoad:
41
68
  Exclude:
42
69
  - 'script/worker_analysis'
43
70
 
44
- Style/AccessModifierIndentation:
45
- EnforcedStyle: outdent
46
-
47
71
  Style/Alias:
48
72
  EnforcedStyle: prefer_alias_method
49
73
 
50
- Style/CaseIndentation:
51
- EnforcedStyle: end
52
-
53
- Style/DotPosition:
54
- EnforcedStyle: trailing
55
-
56
74
  Style/DoubleNegation:
57
75
  Enabled: false
58
76
 
@@ -68,43 +86,20 @@ Style/HashSyntax:
68
86
  Style/IfUnlessModifier:
69
87
  MaxLineLength: 40
70
88
 
71
- Style/IndentArray:
72
- EnforcedStyle: consistent
73
-
74
- Style/IndentAssignment:
75
- Enabled: false
76
-
77
- Style/IndentHash:
78
- EnforcedStyle: consistent
79
-
80
- Style/IndentHeredoc:
81
- Enabled: false
82
-
83
89
  Style/ParallelAssignment:
84
90
  Enabled: false
85
91
 
86
- Style/PercentLiteralDelimiters:
87
- PreferredDelimiters:
88
- '%w': '[]'
89
- '%W': '[]'
90
-
91
92
  Style/Semicolon:
92
93
  AllowAsExpressionSeparator: true
93
94
 
94
95
  Style/SignalException:
95
96
  EnforcedStyle: semantic
96
97
 
97
- Style/SpaceBeforeBlockBraces:
98
- EnforcedStyle: no_space
99
-
100
- Style/SpaceInsideHashLiteralBraces:
101
- EnforcedStyle: no_space
98
+ Style/SymbolArray:
99
+ Enabled: false
102
100
 
103
101
  Style/TrailingCommaInArguments:
104
102
  EnforcedStyleForMultiline: no_comma
105
103
 
106
104
  Style/TrailingCommaInLiteral:
107
105
  EnforcedStyleForMultiline: comma
108
-
109
- Style/SymbolArray:
110
- Enabled: false
@@ -15,11 +15,14 @@ rvm:
15
15
  - '2.4.1'
16
16
  - 'jruby-1.7.26'
17
17
  - 'jruby-9.0.5.0'
18
- - 'jruby-9.1.5.0'
18
+ - 'jruby-9.1.9.0'
19
19
  script:
20
20
  - bundle exec image_optim --info
21
21
  - bundle exec rspec
22
22
  before_install:
23
+ - gem update --system
24
+ - gem update bundler
25
+ - nvm install stable
23
26
  - mkdir -p ~/bin
24
27
  - command -v svgo || npm install svgo
25
28
  - command -v pngout || curl -L "http://static.jonof.id.au/dl/kenutils/pngout-20130221-linux.tar.gz" | tar -xz -C ~/bin --strip-components 2 --wildcards '*/x86_64/pngout'
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## unreleased
4
4
 
5
+ ## v0.25.0 (2017-07-06)
6
+
7
+ * Fix error `uninitialized constant EXIFR::JPEG` (breaking change in [exifr v1.3.0](https://github.com/remvee/exifr/commit/e073a22d06c39f2c1c0e77f5b5fe71545b25e967)) [#150](https://github.com/toy/image_optim/pull/150) [@abemedia](https://github.com/abemedia)
8
+ * Add option to `pngquant` worker to limit maximum number of colors to use [#144](https://github.com/toy/image_optim/issues/144) [@toy](https://github.com/toy)
9
+
5
10
  ## v0.24.3 (2017-05-04)
6
11
 
7
12
  * Set mode of cache files to `0666 & ~umask`, related to [#147](https://github.com/toy/image_optim/issues/147) [@toy](https://github.com/toy)
@@ -58,7 +58,7 @@ With version:
58
58
 
59
59
  <!---<update-version>-->
60
60
  ```ruby
61
- gem 'image_optim', '~> 0.24'
61
+ gem 'image_optim', '~> 0.25'
62
62
  ```
63
63
  <!---</update-version>-->
64
64
 
@@ -319,6 +319,7 @@ Worker has no options
319
319
 
320
320
  ### pngquant:
321
321
  * `:allow_lossy` — Allow quality option *(defaults to `false`)*
322
+ * `:max_colors` — Maximum number of colors to use *(defaults to `256`)*
322
323
  * `:quality` — min..max - don't save below min, use less colors below max (both in range `0..100`; in yaml - `!ruby/range 0..100`), ignored in default/lossless mode *(defaults to `100..100`, `0..100` in lossy mode)*
323
324
  * `:speed` — speed/quality trade-off: `1` - slow, `3` - default, `11` - fast & rough *(defaults to `3`)*
324
325
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'image_optim'
5
- s.version = '0.24.3'
5
+ s.version = '0.25.0'
6
6
  s.summary = %q{Optimize (lossless compress, optionally lossy) images (jpeg, png, gif, svg) using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, pngcrush, pngout, pngquant, svgo)}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
@@ -29,6 +29,6 @@ EOF
29
29
  s.add_development_dependency 'image_optim_pack', '~> 0.2', '>= 0.2.2'
30
30
  s.add_development_dependency 'rspec', '~> 3.0'
31
31
  if RUBY_VERSION >= '2.0'
32
- s.add_development_dependency 'rubocop', '~> 0.47'
32
+ s.add_development_dependency 'rubocop', '~> 0.49'
33
33
  end
34
34
  end
@@ -1,5 +1,5 @@
1
1
  require 'image_optim/worker'
2
- require 'exifr'
2
+ require 'exifr/jpeg'
3
3
 
4
4
  class ImageOptim
5
5
  class Worker
@@ -9,6 +9,11 @@ class ImageOptim
9
9
  ALLOW_LOSSY_OPTION =
10
10
  option(:allow_lossy, false, 'Allow quality option'){ |v| !!v }
11
11
 
12
+ MAX_COLORS_OPTION =
13
+ option(:max_colors, 256, 'Maximum number of colors to use') do |v|
14
+ OptionHelpers.limit_with_range(v.to_i, 2..256)
15
+ end
16
+
12
17
  QUALITY_OPTION =
13
18
  option(:quality, '`100..100`, `0..100` in lossy mode',
14
19
  NonNegativeIntegerRange, 'min..max - don\'t '\
@@ -49,6 +54,7 @@ class ImageOptim
49
54
  --speed=#{speed}
50
55
  --output=#{dst}
51
56
  --force
57
+ #{max_colors}
52
58
  --
53
59
  #{src}
54
60
  ]
@@ -133,8 +133,10 @@ class Analyser
133
133
 
134
134
  # Delegate to worker with short id
135
135
  class WorkerVariant < DelegateClass(ImageOptim::Worker)
136
- attr_reader :name, :id, :cons_id
136
+ attr_reader :name, :id, :cons_id, :required
137
137
  def initialize(klass, image_optim, options)
138
+ @required = options.delete(:required)
139
+ @run_order = options.delete(:run_order)
138
140
  allow_consecutive_on = Array(options.delete(:allow_consecutive_on))
139
141
  @image_optim = image_optim
140
142
  @name = klass.bin_sym.to_s + options_string(options)
@@ -143,6 +145,10 @@ class Analyser
143
145
  @cons_id = [klass, allow_consecutive_on.map{ |key| [key, send(key)] }]
144
146
  end
145
147
 
148
+ def run_order
149
+ @run_order || super
150
+ end
151
+
146
152
  def etag
147
153
  [
148
154
  id,
@@ -155,7 +161,8 @@ class Analyser
155
161
 
156
162
  def bin_versions
157
163
  @bin_versions ||= used_bins.map do |name|
158
- @image_optim.resolve_bin!(name).to_s
164
+ bin = @image_optim.resolve_bin!(name)
165
+ "#{bin.name} #{bin.version}"
159
166
  end
160
167
  end
161
168
 
@@ -189,7 +196,7 @@ class Analyser
189
196
 
190
197
  dst_size = success ? dst.size : nil
191
198
  digest = (success ? dst : src).digest
192
- cache = digest.sub(/../, '\0/') + ".#{src.image_format}"
199
+ cache = digest.sub(/../, '\0/')
193
200
  result = new(worker.id, success, time, src.size, dst_size, cache)
194
201
  if success
195
202
  path = result.path
@@ -274,7 +281,9 @@ class Analyser
274
281
  end
275
282
 
276
283
  def run_workers(src, workers, last_result = nil, &block)
284
+ required_workers = workers.select(&:required)
277
285
  with_progress(workers, last_result) do |worker|
286
+ next if required_workers.any?{ |w| w.run_order < worker.run_order }
278
287
  worker_result, result_image = run_worker(src, worker)
279
288
 
280
289
  steps = (last_result ? last_result.steps : []) + [worker_result]
@@ -306,19 +315,12 @@ class Analyser
306
315
  def difference_with(other)
307
316
  run_cache[:difference][other.digest] ||=
308
317
  Cache.get(:difference, [@path.digest, other.digest].sort, nil) do
309
- images = [flatten_animation(@path), flatten_animation(other)]
310
-
311
- alpha_presence = images.map do |image|
312
- Cmd.capture("identify -format '%A' #{image.to_s.shellescape}")
313
- end
314
- if alpha_presence.uniq.length == 2
315
- images.map!{ |image| underlay_noise(image) }
316
- end
318
+ images = for_compare(@path, other)
317
319
 
318
320
  nrmse = Cmd.capture(%W[
319
321
  convert
320
- #{images[0]} -auto-orient
321
- #{images[1]} -auto-orient
322
+ #{images[0].image_format}:#{images[0]} -auto-orient
323
+ #{images[1].image_format}:#{images[1]} -auto-orient
322
324
  -metric RMSE
323
325
  -compare
324
326
  -format %[distortion]
@@ -331,16 +333,33 @@ class Analyser
331
333
  end
332
334
  end
333
335
 
336
+ def for_compare(*images)
337
+ images.map!{ |image| flatten_animation(image) }
338
+
339
+ alpha_presence = images.map do |image|
340
+ !!Cmd.capture(%W[
341
+ identify
342
+ -format %A
343
+ #{image.image_format}:#{image.to_s.shellescape}
344
+ ].shelljoin)[/true/i]
345
+ end
346
+ if alpha_presence.uniq.length != 1
347
+ images.map!{ |image| underlay_noise(image) }
348
+ end
349
+
350
+ images
351
+ end
352
+
334
353
  def flatten_animation(image)
335
354
  run_cache[:flatten][image.digest] ||= begin
336
355
  if image.image_format == :gif
337
356
  flattened = image.temp_path
338
357
  Cmd.run(*%W[
339
358
  convert
340
- #{image}
359
+ #{image.image_format}:#{image}
341
360
  -coalesce
342
361
  -append
343
- #{flattened}
362
+ #{image.image_format}:#{flattened}
344
363
  ]) || fail("failed flattening of #{image}")
345
364
  flattened
346
365
  else
@@ -354,12 +373,12 @@ class Analyser
354
373
  with_noise = image.temp_path
355
374
  Cmd.run(*%W[
356
375
  convert
357
- #{image}
376
+ #{image.image_format}:#{image}
358
377
  +noise Random
359
- #{image}
378
+ #{image.image_format}:#{image}
360
379
  -flatten
361
380
  -alpha off
362
- #{with_noise}
381
+ #{image.image_format}:#{with_noise}
363
382
  ]) || fail("failed underlaying noise to #{image}")
364
383
  with_noise
365
384
  end
@@ -455,25 +474,24 @@ class Analyser
455
474
  end
456
475
  end
457
476
 
458
- def initialize(option_variants)
459
- option_variants = HashHelpers.deep_symbolise_keys(option_variants)
477
+ def initialize(config)
478
+ config = HashHelpers.deep_symbolise_keys(config)
460
479
  image_optim = ImageOptim.new
461
480
 
462
481
  @workers_by_format = Hash.new{ |h, k| h[k] = [] }
463
482
  ImageOptim::Worker.klasses.each do |klass|
464
- worker_options_config = option_variants.delete(klass.bin_sym) || {}
465
- allow_consecutive_on = worker_options_config.delete(:allow_consecutive_on)
466
- worker_option_variants = case worker_options_config
467
- when Array
468
- worker_options_config
469
- when Hash
470
- worker_options_config.variants
471
- else
472
- fail "Array or Hash expected, got #{worker_options_config}"
483
+ worker_config = config.delete(klass.bin_sym)
484
+ next if worker_config == false
485
+ worker_config ||= {}
486
+
487
+ option_variants = worker_config.delete(:variants) || [{}]
488
+ option_variants = case option_variants
489
+ when Array then option_variants
490
+ when Hash then option_variants.variants
491
+ else fail "Array or Hash expected, got #{option_variants}"
473
492
  end
474
- worker_option_variants.each do |options|
475
- options = HashHelpers.deep_symbolise_keys(options)
476
- options[:allow_consecutive_on] = allow_consecutive_on
493
+ option_variants.each do |options|
494
+ options = HashHelpers.deep_symbolise_keys(options).merge(worker_config)
477
495
  worker = WorkerVariant.new(klass, image_optim, options)
478
496
  worker.image_formats.each do |format|
479
497
  @workers_by_format[format] << worker
@@ -483,7 +501,7 @@ class Analyser
483
501
 
484
502
  log_workers_by_format
485
503
 
486
- fail "unknown variants: #{option_variants}" unless option_variants.empty?
504
+ fail "unknown variants: #{config}" unless config.empty?
487
505
  end
488
506
 
489
507
  def analyse(paths)
@@ -553,7 +571,30 @@ private
553
571
  end
554
572
  end
555
573
 
556
- def option_variants
574
+ abort <<-HELP if ARGV.empty?
575
+ Specify paths for analysis.
576
+
577
+ Example of `.analysis_variants.yml`:
578
+ jhead:
579
+ required: true # don't skip this worker
580
+ jpegtran: # 3 worker variants
581
+ variants:
582
+ - jpegrescan: true
583
+ - progressive: true
584
+ - progressive: false
585
+ optipng: # 6 worker variants by combining options
586
+ variants:
587
+ level: [6, 7]
588
+ interlace: [true, false, nil]
589
+ gifsicle: # allow variants with different interlace to run consecutively
590
+ variants:
591
+ allow_consecutive_on: interlace
592
+ interlace: [true, false]
593
+ careful: [true, false]
594
+ # other workers will be used with default options
595
+ HELP
596
+
597
+ Analyser.new(begin
557
598
  path = '.analysis_variants.yml'
558
599
  case h = YAML.load_file(path)
559
600
  when Hash then h
@@ -563,27 +604,4 @@ def option_variants
563
604
  rescue Errno::ENOENT => e
564
605
  warn e
565
606
  {}
566
- end
567
-
568
- analyser = Analyser.new(option_variants)
569
-
570
- if ARGV.empty?
571
- abort <<-HELP
572
- Specify paths for analysis.
573
-
574
- Example of `.analysis_variants.yml`:
575
- jpegtran: # 3 worker variants
576
- - jpegrescan: true
577
- - progressive: true
578
- - progressive: false
579
- optipng: # 6 worker variants by combining options
580
- level: [6, 7]
581
- interlace: [true, false, nil]
582
- gifsicle: # allow variants with different interlace to run consecutively
583
- allow_consecutive_on: interlace
584
- interlace: [true, false]
585
- careful: [true, false]
586
- # other workers will be used with default options
587
- HELP
588
- end
589
- analyser.analyse(ARGV)
607
+ end).analyse(ARGV)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_optim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.3
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Kuchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-04 00:00:00.000000000 Z
11
+ date: 2017-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fspath
@@ -132,14 +132,14 @@ dependencies:
132
132
  requirements:
133
133
  - - "~>"
134
134
  - !ruby/object:Gem::Version
135
- version: '0.47'
135
+ version: '0.49'
136
136
  type: :development
137
137
  prerelease: false
138
138
  version_requirements: !ruby/object:Gem::Requirement
139
139
  requirements:
140
140
  - - "~>"
141
141
  - !ruby/object:Gem::Version
142
- version: '0.47'
142
+ version: '0.49'
143
143
  description:
144
144
  email:
145
145
  executables: