Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Oxipng #190

Merged
merged 1 commit into from Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/check.yml
Expand Up @@ -29,6 +29,7 @@ jobs:
bundler-cache: true
- run: sudo npm install -g svgo
- run: curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | sudo tar -xz -C /usr/local/bin --strip-components 2 --wildcards '*/amd64/pngout'
- run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
- run: bundle exec image_optim --info
- run: bundle exec rspec
coverage:
Expand All @@ -43,6 +44,7 @@ jobs:
bundler-cache: true
- run: sudo npm install -g svgo
- run: curl -L "https://www.jonof.id.au/files/kenutils/pngout-20200115-linux.tar.gz" | sudo tar -xz -C /usr/local/bin --strip-components 2 --wildcards '*/amd64/pngout'
- run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
- uses: paambaati/codeclimate-action@v2.7.5
with:
coverageCommand: bundle exec rspec
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.markdown
Expand Up @@ -2,6 +2,8 @@

## unreleased

* Add support for Oxipng [#167](https://github.com/toy/image_optim/issues/167) [#190](https://github.com/toy/image_optim/pull/190) [@oblakeerickson](https://github.com/oblakeerickson)

## v0.30.0 (2021-05-11)

* Add `timeout` option to restrict maximum time spent on every image [#21](https://github.com/toy/image_optim/issues/21) [#148](https://github.com/toy/image_optim/pull/148) [#149](https://github.com/toy/image_optim/pull/149) [#162](https://github.com/toy/image_optim/pull/162) [#184](https://github.com/toy/image_optim/pull/184) [#189](https://github.com/toy/image_optim/pull/189) [@tgxworld](https://github.com/tgxworld) [@oblakeerickson](https://github.com/oblakeerickson) [@toy](https://github.com/toy)
Expand Down
2 changes: 1 addition & 1 deletion lib/image_optim.rb
Expand Up @@ -14,7 +14,7 @@
require 'shellwords'

%w[
pngcrush pngout advpng optipng pngquant
pngcrush pngout advpng optipng pngquant oxipng
jhead jpegoptim jpegrecompress jpegtran
gifsicle
svgo
Expand Down
2 changes: 1 addition & 1 deletion lib/image_optim/bin_resolver/bin.rb
Expand Up @@ -109,7 +109,7 @@ def version_string
case name
when :advpng
capture("#{escaped_path} --version 2> #{Path::NULL}")[/\bv(\d+(\.\d+)+|none)/, 1]
when :gifsicle, :jpegoptim, :optipng
when :gifsicle, :jpegoptim, :optipng, :oxipng
capture("#{escaped_path} --version 2> #{Path::NULL}")[/\d+(\.\d+)+/]
when :svgo, :pngquant
capture("#{escaped_path} --version 2>&1")[/\A\d+(\.\d+)+/]
Expand Down
53 changes: 53 additions & 0 deletions lib/image_optim/worker/oxipng.rb
@@ -0,0 +1,53 @@
# frozen_string_literal: true

require 'image_optim/worker'
require 'image_optim/option_helpers'
require 'image_optim/true_false_nil'

class ImageOptim
class Worker
# https://github.com/shssoichiro/oxipng
class Oxipng < Worker
LEVEL_OPTION =
option(:level, 3, 'Optimization level preset: '\
'`0` is least, '\
'`6` is best') do |v|
OptionHelpers.limit_with_range(v.to_i, 0..6)
end

INTERLACE_OPTION =
option(:interlace, false, TrueFalseNil, 'Interlace: '\
'`true` - interlace on, '\
'`false` - interlace off, '\
'`nil` - as is in original image') do |v|
TrueFalseNil.convert(v)
end

STRIP_OPTION =
option(:strip, true, 'Remove all auxiliary chunks'){ |v| !!v }

def run_order
-4
end

def optimize(src, dst, options = {})
src.copy(dst)
args = %W[
-o #{level}
--quiet
--
#{dst}
]
args.unshift "-i#{interlace ? 1 : 0}" unless interlace.nil?
if strip
args.unshift '--strip', 'all'
end
execute(:oxipng, args, options) && optimized?(src, dst)
end

def optimized?(src, dst)
interlace ? dst.size? : super
end
end
end
end
89 changes: 89 additions & 0 deletions spec/image_optim/worker/oxipng_spec.rb
@@ -0,0 +1,89 @@
# frozen_string_literal: true

require 'spec_helper'
require 'image_optim/worker/oxipng'
require 'image_optim/path'

describe ImageOptim::Worker::Oxipng do
describe 'strip option' do
subject{ described_class.new(ImageOptim.new, options) }

let(:options){ {} }
let(:src){ instance_double(ImageOptim::Path, copy: nil) }
let(:dst){ instance_double(ImageOptim::Path) }

before do
oxipng_bin = instance_double(ImageOptim::BinResolver::Bin)
allow(subject).to receive(:resolve_bin!).
with(:oxipng).and_return(oxipng_bin)

allow(subject).to receive(:optimized?)
end

context 'by default' do
it 'should add --strip all to arguments' do
expect(subject).to receive(:execute) do |_bin, *args|
expect(args.join(' ')).to match(/(^| )--strip all($| )/)
end

subject.optimize(src, dst)
end
end

context 'when strip is disabled' do
let(:options){ {strip: false} }

it 'should not add --strip all to arguments' do
expect(subject).to receive(:execute) do |_bin, *args|
expect(args.join(' ')).not_to match(/(^| )--strip all($| )/)
end

subject.optimize(src, dst)
end
end
end

describe '#optimized?' do
let(:src){ instance_double(ImageOptim::Path, src_options) }
let(:dst){ instance_double(ImageOptim::Path, dst_options) }
let(:src_options){ {size: 10} }
let(:dst_options){ {size?: 9} }
let(:instance){ described_class.new(ImageOptim.new, instance_options) }
let(:instance_options){ {} }

subject{ instance.optimized?(src, dst) }

context 'when interlace option is enabled' do
let(:instance_options){ {interlace: true} }

context 'when dst is empty' do
let(:dst_options){ {size?: nil} }
it{ is_expected.to be_falsy }
end

context 'when dst is not empty' do
let(:dst_options){ {size?: 20} }
it{ is_expected.to be_truthy }
end
end

context 'when interlace option is disabled' do
let(:instance_options){ {interlace: false} }

context 'when dst is empty' do
let(:dst_options){ {size?: nil} }
it{ is_expected.to be_falsy }
end

context 'when dst is greater than or equal to src' do
let(:dst_options){ {size?: 10} }
it{ is_expected.to be_falsy }
end

context 'when dst is less than src' do
let(:dst_options){ {size?: 9} }
it{ is_expected.to be_truthy }
end
end
end
end