elrio 0.0.1 → 0.0.2
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/lib/elrio.rb +4 -1
- data/lib/elrio/cap_inset_detector.rb +18 -31
- data/lib/elrio/geometry.rb +27 -4
- data/lib/elrio/image_optimizer.rb +21 -60
- data/lib/elrio/n_gram_generator.rb +22 -0
- data/lib/elrio/pattern_detector.rb +63 -0
- data/lib/elrio/point_size.rb +7 -0
- data/lib/elrio/runner.rb +22 -24
- data/lib/elrio/version.rb +1 -1
- metadata +21 -31
- data/.gitignore +0 -16
- data/.rspec +0 -2
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -3
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -29
- data/LICENSE.txt +0 -22
- data/README.md +0 -51
- data/Rakefile +0 -6
- data/elrio.gemspec +0 -27
- data/spec/elrio/cap_inset_detector_spec.rb +0 -40
- data/spec/elrio/cli_spec.rb +0 -56
- data/spec/elrio/image_optimizer_spec.rb +0 -82
- data/spec/elrio/runner_spec.rb +0 -86
- data/spec/fixtures/optimized.png +0 -0
- data/spec/fixtures/optimized@2x.png +0 -0
- data/spec/fixtures/original.png +0 -0
- data/spec/spec_helper.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95d67d95fdf9b0ef83011a8ef477540f9ed20bc3
|
4
|
+
data.tar.gz: 8537667e7c1a1e99bc61ae6158a76ae054558dd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c65d31c63dc3e276204b5f07dbcb8daef918c6c8cb33e54dfed0400bbfc9756103ee73b891fd88fbdbe70de55b8f8fc9883cdc8397530d8bc97bd0a9df9b9790
|
7
|
+
data.tar.gz: b7bf1ed3afbdcc0dab75981bcde8991f27f308197f10fa2c04d51d411f7226214e0f02d5e077ad1f2bd1e637555f66277bf7287909c656c79431c03e5c967724
|
data/lib/elrio.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
require "elrio/cli"
|
2
1
|
require "elrio/cap_inset_detector"
|
2
|
+
require "elrio/cli"
|
3
3
|
require "elrio/geometry"
|
4
4
|
require "elrio/image_optimizer"
|
5
|
+
require "elrio/n_gram_generator"
|
6
|
+
require "elrio/pattern_detector"
|
7
|
+
require "elrio/point_size"
|
5
8
|
require "elrio/runner"
|
6
9
|
require "elrio/version"
|
@@ -1,37 +1,24 @@
|
|
1
1
|
module Elrio
|
2
2
|
class CapInsetDetector
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
(1...array.count).each do |i|
|
8
|
-
if array[i] == prev
|
9
|
-
runs[-1] += 1
|
10
|
-
else
|
11
|
-
runs << 1
|
12
|
-
end
|
13
|
-
|
14
|
-
prev = array[i]
|
15
|
-
end
|
16
|
-
|
17
|
-
longest_run_length = runs.max
|
18
|
-
|
19
|
-
return [array.count, 0] if longest_run_length == 1
|
20
|
-
|
21
|
-
longest_run_index = runs.index(longest_run_length)
|
22
|
-
|
23
|
-
if longest_run_index > 0
|
24
|
-
run_start = runs[0, longest_run_index].reduce(:+)
|
25
|
-
else
|
26
|
-
run_start = 0
|
27
|
-
end
|
28
|
-
|
29
|
-
run_end = run_start + longest_run_length
|
3
|
+
def initialize(pattern_detector = PatternDetector.new)
|
4
|
+
@pattern_detector = pattern_detector
|
5
|
+
end
|
30
6
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
7
|
+
def detect_cap_insets(image)
|
8
|
+
columns = (0...image.width).map { |x| image.column(x) }
|
9
|
+
rows = (0...image.height).map { |y| image.row(y) }
|
10
|
+
|
11
|
+
horizontal_pattern = @pattern_detector.detect_pattern(columns)
|
12
|
+
vertical_pattern = @pattern_detector.detect_pattern(rows)
|
13
|
+
|
14
|
+
Insets.new(
|
15
|
+
vertical_pattern.start,
|
16
|
+
horizontal_pattern.start,
|
17
|
+
vertical_pattern.end,
|
18
|
+
horizontal_pattern.end,
|
19
|
+
horizontal_pattern.size,
|
20
|
+
vertical_pattern.size
|
21
|
+
)
|
35
22
|
end
|
36
23
|
end
|
37
24
|
end
|
data/lib/elrio/geometry.rb
CHANGED
@@ -1,13 +1,36 @@
|
|
1
|
+
require "values"
|
2
|
+
|
1
3
|
module Elrio
|
2
|
-
Point =
|
4
|
+
Point = Value.new(:x, :y)
|
5
|
+
|
6
|
+
Size = Value.new(:width, :height)
|
3
7
|
|
4
|
-
|
8
|
+
Rect = Value.new(:x, :y, :width, :height)
|
5
9
|
|
6
|
-
|
10
|
+
Pattern = Value.new(:start, :end, :size)
|
7
11
|
|
8
|
-
Insets
|
12
|
+
class Insets < Value.new(:top, :left, :bottom, :right, :pattern_width, :pattern_height)
|
9
13
|
def to_s
|
10
14
|
[top, left, bottom, right].to_s
|
11
15
|
end
|
16
|
+
|
17
|
+
def *(factor)
|
18
|
+
Insets.new(
|
19
|
+
(factor * top).ceil,
|
20
|
+
(factor * left).ceil,
|
21
|
+
(factor * bottom).ceil,
|
22
|
+
(factor * right).ceil,
|
23
|
+
pattern_width,
|
24
|
+
pattern_height
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def /(factor)
|
29
|
+
self * (1.0 / factor)
|
30
|
+
end
|
31
|
+
|
32
|
+
def coerce(number)
|
33
|
+
[self, number]
|
34
|
+
end
|
12
35
|
end
|
13
36
|
end
|
@@ -1,73 +1,44 @@
|
|
1
1
|
module Elrio
|
2
2
|
class ImageOptimizer
|
3
|
-
def
|
4
|
-
|
5
|
-
end
|
6
|
-
|
7
|
-
def detect_cap_insets(image)
|
8
|
-
insets = detect_cap_insets_in_pixels(image)
|
9
|
-
|
10
|
-
if @retina
|
11
|
-
Insets.new(
|
12
|
-
(insets.top + 1) / 2,
|
13
|
-
(insets.left + 1) / 2,
|
14
|
-
(insets.bottom + 1) / 2,
|
15
|
-
(insets.right + 1) / 2
|
16
|
-
)
|
17
|
-
else
|
18
|
-
insets
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def optimize(image, insets)
|
23
|
-
insets = insets.dup
|
24
|
-
repeatable_size = 1
|
25
|
-
|
26
|
-
if @retina
|
27
|
-
repeatable_size *= 2
|
28
|
-
insets.top *= 2
|
29
|
-
insets.left *= 2
|
30
|
-
insets.bottom *= 2
|
31
|
-
insets.right *= 2
|
32
|
-
end
|
33
|
-
|
34
|
-
target_height = insets.top + insets.bottom + repeatable_size
|
35
|
-
target_width = insets.left + insets.right + repeatable_size
|
3
|
+
def optimize(image, pixel_insets)
|
4
|
+
target = target_size(pixel_insets)
|
36
5
|
|
37
|
-
return
|
6
|
+
return if target.height > image.height || target.width > image.width
|
38
7
|
|
39
|
-
source_x = image.width -
|
40
|
-
source_y = image.height -
|
41
|
-
target_x =
|
42
|
-
target_y =
|
8
|
+
source_x = image.width - pixel_insets.right
|
9
|
+
source_y = image.height - pixel_insets.bottom
|
10
|
+
target_x = target.width - pixel_insets.right
|
11
|
+
target_y = target.height - pixel_insets.bottom
|
12
|
+
source_width = pixel_insets.left + pixel_insets.pattern_width
|
13
|
+
source_height = pixel_insets.top + pixel_insets.pattern_height
|
43
14
|
|
44
|
-
optimized = ChunkyPNG::Image.new(
|
15
|
+
optimized = ChunkyPNG::Image.new(target.width, target.height)
|
45
16
|
|
46
17
|
copy_rect(
|
47
18
|
image,
|
48
19
|
optimized,
|
49
|
-
Rect.new(0, 0,
|
20
|
+
Rect.new(0, 0, source_width, source_height),
|
50
21
|
Point.new(0, 0)
|
51
22
|
)
|
52
23
|
|
53
24
|
copy_rect(
|
54
25
|
image,
|
55
26
|
optimized,
|
56
|
-
Rect.new(source_x, 0,
|
27
|
+
Rect.new(source_x, 0, pixel_insets.right, source_height),
|
57
28
|
Point.new(target_x, 0)
|
58
29
|
)
|
59
30
|
|
60
31
|
copy_rect(
|
61
32
|
image,
|
62
33
|
optimized,
|
63
|
-
Rect.new(0, source_y,
|
34
|
+
Rect.new(0, source_y, source_width, pixel_insets.bottom),
|
64
35
|
Point.new(0, target_y)
|
65
36
|
)
|
66
37
|
|
67
38
|
copy_rect(
|
68
39
|
image,
|
69
40
|
optimized,
|
70
|
-
Rect.new(source_x, source_y,
|
41
|
+
Rect.new(source_x, source_y, pixel_insets.right, pixel_insets.bottom),
|
71
42
|
Point.new(target_x, target_y)
|
72
43
|
)
|
73
44
|
|
@@ -76,23 +47,6 @@ module Elrio
|
|
76
47
|
|
77
48
|
private
|
78
49
|
|
79
|
-
def detect_cap_insets_in_pixels(image)
|
80
|
-
columns = (0...image.width).map {|x| image.column(x) }
|
81
|
-
rows = (0...image.height).map {|y| image.row(y) }
|
82
|
-
|
83
|
-
detector = CapInsetDetector.new
|
84
|
-
|
85
|
-
column_info = detector.detect_cap_insets(columns)
|
86
|
-
row_info = detector.detect_cap_insets(rows)
|
87
|
-
|
88
|
-
Insets.new(
|
89
|
-
row_info[0],
|
90
|
-
column_info[0],
|
91
|
-
row_info[1],
|
92
|
-
column_info[1]
|
93
|
-
)
|
94
|
-
end
|
95
|
-
|
96
50
|
def copy_rect(src, dest, src_rect, dest_origin)
|
97
51
|
(0...src_rect.width).each do |x|
|
98
52
|
(0...src_rect.height).each do |y|
|
@@ -106,5 +60,12 @@ module Elrio
|
|
106
60
|
end
|
107
61
|
end
|
108
62
|
end
|
63
|
+
|
64
|
+
def target_size(insets)
|
65
|
+
height = insets.top + insets.bottom + insets.pattern_height
|
66
|
+
width = insets.left + insets.right + insets.pattern_width
|
67
|
+
|
68
|
+
Size.new(width, height)
|
69
|
+
end
|
109
70
|
end
|
110
71
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Elrio
|
2
|
+
class NGramGenerator
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(list, n = 1, offset = 0)
|
6
|
+
@offset = offset
|
7
|
+
@list = list
|
8
|
+
@n = n
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
yield @list[0, @offset]
|
13
|
+
|
14
|
+
i = @offset
|
15
|
+
|
16
|
+
while i < @list.size
|
17
|
+
yield @list[i, @n]
|
18
|
+
i += @n
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Elrio
|
2
|
+
class PatternDetector
|
3
|
+
def detect_pattern(array)
|
4
|
+
max_n = array.size / 2
|
5
|
+
longest_run = (0..0)
|
6
|
+
longest_run_n = 1
|
7
|
+
|
8
|
+
(1..max_n).each do |n|
|
9
|
+
max_offset = [n-1, array.size - 2*n].min
|
10
|
+
|
11
|
+
(0..max_offset).each do |offset|
|
12
|
+
generator = NGramGenerator.new(array, n, offset)
|
13
|
+
run = find_longest_run(generator)
|
14
|
+
|
15
|
+
if run.size > [n, longest_run.size].max
|
16
|
+
longest_run = run
|
17
|
+
longest_run_n = n
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if longest_run.size == 1
|
23
|
+
start_cap = array.size
|
24
|
+
end_cap = 0
|
25
|
+
else
|
26
|
+
start_cap = longest_run.first
|
27
|
+
end_cap = array.count - longest_run.last - 1
|
28
|
+
end
|
29
|
+
|
30
|
+
Pattern.new(start_cap, end_cap, longest_run_n)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def find_longest_run(generator)
|
36
|
+
prev = nil
|
37
|
+
runs = []
|
38
|
+
|
39
|
+
generator.each do |ngram|
|
40
|
+
if ngram == prev
|
41
|
+
runs[-1] += ngram.size
|
42
|
+
else
|
43
|
+
runs << ngram.size
|
44
|
+
end
|
45
|
+
|
46
|
+
prev = ngram
|
47
|
+
end
|
48
|
+
|
49
|
+
longest_run_length = runs.max
|
50
|
+
longest_run_index = runs.index(longest_run_length)
|
51
|
+
|
52
|
+
if longest_run_index > 0
|
53
|
+
run_start = runs[0, longest_run_index].reduce(:+)
|
54
|
+
else
|
55
|
+
run_start = 0
|
56
|
+
end
|
57
|
+
|
58
|
+
run_end = run_start + longest_run_length - 1
|
59
|
+
|
60
|
+
(run_start..run_end)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/elrio/runner.rb
CHANGED
@@ -2,43 +2,41 @@ require "chunky_png"
|
|
2
2
|
|
3
3
|
module Elrio
|
4
4
|
class Runner
|
5
|
+
def initialize(cap_inset_detector = CapInsetDetector.new, image_optimizer = ImageOptimizer.new)
|
6
|
+
@cap_inset_detector = cap_inset_detector
|
7
|
+
@image_optimizer = image_optimizer
|
8
|
+
end
|
9
|
+
|
5
10
|
def analyze(path)
|
6
11
|
image = ChunkyPNG::Image.from_file(path)
|
7
|
-
|
8
|
-
|
12
|
+
scale = PointSize.from_filename(path)
|
13
|
+
insets = @cap_inset_detector.detect_cap_insets(image)
|
14
|
+
insets / scale
|
9
15
|
end
|
10
16
|
|
11
17
|
def optimize(path)
|
12
|
-
retina = is_retina?(path)
|
13
|
-
|
14
|
-
if retina
|
15
|
-
opt_suffix = "-optimized@2x.png"
|
16
|
-
opt_base = path.sub(/@2x/, '')
|
17
|
-
else
|
18
|
-
opt_suffix = "-optimized.png"
|
19
|
-
opt_base = path
|
20
|
-
end
|
21
|
-
|
22
|
-
optimized_path = File.join(
|
23
|
-
File.dirname(opt_base),
|
24
|
-
File.basename(opt_base, ".*") + opt_suffix
|
25
|
-
)
|
26
|
-
|
27
18
|
image = ChunkyPNG::Image.from_file(path)
|
19
|
+
scale = PointSize.from_filename(path)
|
20
|
+
insets = @cap_inset_detector.detect_cap_insets(image)
|
21
|
+
point_insets = insets / scale
|
28
22
|
|
29
|
-
|
30
|
-
insets = image_optimizer.detect_cap_insets(image)
|
23
|
+
optimized_image = @image_optimizer.optimize(image, insets)
|
31
24
|
|
32
|
-
|
33
|
-
|
25
|
+
if optimized_image
|
26
|
+
optimized_path = optimized_path_for(path)
|
27
|
+
optimized_image.save(optimized_path)
|
28
|
+
end
|
34
29
|
|
35
|
-
|
30
|
+
point_insets
|
36
31
|
end
|
37
32
|
|
38
33
|
private
|
39
34
|
|
40
|
-
def
|
41
|
-
|
35
|
+
def optimized_path_for(path)
|
36
|
+
File.join(
|
37
|
+
File.dirname(path),
|
38
|
+
File.basename(path, ".*") + "-optimized.png"
|
39
|
+
)
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
data/lib/elrio/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elrio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Can Berk Güder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: values
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: Cap inset detector & optimizer for resizable UIKit assets.
|
70
84
|
email:
|
71
85
|
- cbguder@gmail.com
|
@@ -74,33 +88,17 @@ executables:
|
|
74
88
|
extensions: []
|
75
89
|
extra_rdoc_files: []
|
76
90
|
files:
|
77
|
-
- .gitignore
|
78
|
-
- .rspec
|
79
|
-
- .ruby-gemset
|
80
|
-
- .ruby-version
|
81
|
-
- .travis.yml
|
82
|
-
- Gemfile
|
83
|
-
- Gemfile.lock
|
84
|
-
- LICENSE.txt
|
85
|
-
- README.md
|
86
|
-
- Rakefile
|
87
91
|
- bin/elrio
|
88
|
-
- elrio.gemspec
|
89
92
|
- lib/elrio.rb
|
90
93
|
- lib/elrio/cap_inset_detector.rb
|
91
94
|
- lib/elrio/cli.rb
|
92
95
|
- lib/elrio/geometry.rb
|
93
96
|
- lib/elrio/image_optimizer.rb
|
97
|
+
- lib/elrio/n_gram_generator.rb
|
98
|
+
- lib/elrio/pattern_detector.rb
|
99
|
+
- lib/elrio/point_size.rb
|
94
100
|
- lib/elrio/runner.rb
|
95
101
|
- lib/elrio/version.rb
|
96
|
-
- spec/elrio/cap_inset_detector_spec.rb
|
97
|
-
- spec/elrio/cli_spec.rb
|
98
|
-
- spec/elrio/image_optimizer_spec.rb
|
99
|
-
- spec/elrio/runner_spec.rb
|
100
|
-
- spec/fixtures/optimized.png
|
101
|
-
- spec/fixtures/optimized@2x.png
|
102
|
-
- spec/fixtures/original.png
|
103
|
-
- spec/spec_helper.rb
|
104
102
|
homepage: https://github.com/cbguder/elrio
|
105
103
|
licenses:
|
106
104
|
- MIT
|
@@ -121,16 +119,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
119
|
version: '0'
|
122
120
|
requirements: []
|
123
121
|
rubyforge_project:
|
124
|
-
rubygems_version: 2.
|
122
|
+
rubygems_version: 2.2.2
|
125
123
|
signing_key:
|
126
124
|
specification_version: 4
|
127
125
|
summary: Cap inset detector & optimizer for resizable UIKit assets.
|
128
|
-
test_files:
|
129
|
-
- spec/elrio/cap_inset_detector_spec.rb
|
130
|
-
- spec/elrio/cli_spec.rb
|
131
|
-
- spec/elrio/image_optimizer_spec.rb
|
132
|
-
- spec/elrio/runner_spec.rb
|
133
|
-
- spec/fixtures/optimized.png
|
134
|
-
- spec/fixtures/optimized@2x.png
|
135
|
-
- spec/fixtures/original.png
|
136
|
-
- spec/spec_helper.rb
|
126
|
+
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
elrio
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.0.0-p247
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
elrio (0.0.1)
|
5
|
-
chunky_png
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
chunky_png (1.2.8)
|
11
|
-
diff-lcs (1.2.4)
|
12
|
-
rake (0.9.6)
|
13
|
-
rspec (2.14.1)
|
14
|
-
rspec-core (~> 2.14.0)
|
15
|
-
rspec-expectations (~> 2.14.0)
|
16
|
-
rspec-mocks (~> 2.14.0)
|
17
|
-
rspec-core (2.14.5)
|
18
|
-
rspec-expectations (2.14.2)
|
19
|
-
diff-lcs (>= 1.1.3, < 2.0)
|
20
|
-
rspec-mocks (2.14.3)
|
21
|
-
|
22
|
-
PLATFORMS
|
23
|
-
ruby
|
24
|
-
|
25
|
-
DEPENDENCIES
|
26
|
-
bundler (~> 1.3)
|
27
|
-
elrio!
|
28
|
-
rake
|
29
|
-
rspec
|
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2013 Can Berk Güder
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
# Elrio
|
2
|
-
|
3
|
-
Cap inset detector & optimizer for resizable UIKit assets.
|
4
|
-
|
5
|
-
[](https://codeclimate.com/github/cbguder/elrio)
|
6
|
-
[](https://travis-ci.org/cbguder/elrio)
|
7
|
-
|
8
|
-
## Installation
|
9
|
-
|
10
|
-
Add this line to your application's Gemfile:
|
11
|
-
|
12
|
-
gem 'elrio'
|
13
|
-
|
14
|
-
And then execute:
|
15
|
-
|
16
|
-
$ bundle
|
17
|
-
|
18
|
-
Or install it yourself as:
|
19
|
-
|
20
|
-
$ gem install elrio
|
21
|
-
|
22
|
-
## Usage
|
23
|
-
|
24
|
-
To get the resizable cap insets for an asset:
|
25
|
-
|
26
|
-
$ elrio analyze image.png
|
27
|
-
|
28
|
-
To create an optimized version of the asset:
|
29
|
-
|
30
|
-
$ elrio optimize image.png
|
31
|
-
|
32
|
-
## Example
|
33
|
-
|
34
|
-
When run with this image:
|
35
|
-
|
36
|
-

|
37
|
-
|
38
|
-
$ elrio optimize image.png
|
39
|
-
image.png: [48, 48, 48, 48] # Top, Left, Bottom, Right
|
40
|
-
|
41
|
-
This optimized version is written to `image-optimized.png`:
|
42
|
-
|
43
|
-

|
44
|
-
|
45
|
-
## Contributing
|
46
|
-
|
47
|
-
1. Fork it
|
48
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
49
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
50
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
51
|
-
5. Create new Pull Request
|
data/Rakefile
DELETED
data/elrio.gemspec
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
lib = File.expand_path("../lib", __FILE__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require "elrio/version"
|
6
|
-
|
7
|
-
Gem::Specification.new do |spec|
|
8
|
-
spec.name = "elrio"
|
9
|
-
spec.version = Elrio::VERSION
|
10
|
-
spec.authors = ["Can Berk Güder"]
|
11
|
-
spec.email = ["cbguder@gmail.com"]
|
12
|
-
spec.description = "Cap inset detector & optimizer for resizable UIKit assets."
|
13
|
-
spec.summary = "Cap inset detector & optimizer for resizable UIKit assets."
|
14
|
-
spec.homepage = "https://github.com/cbguder/elrio"
|
15
|
-
spec.license = "MIT"
|
16
|
-
|
17
|
-
spec.files = `git ls-files`.split($/)
|
18
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
-
spec.require_paths = ["lib"]
|
21
|
-
|
22
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
-
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "rspec"
|
25
|
-
|
26
|
-
spec.add_runtime_dependency "chunky_png"
|
27
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Elrio::CapInsetDetector do
|
4
|
-
describe "#detect_cap_insets" do
|
5
|
-
it "returns the non-repeating prefix and suffix lengths of a collection" do
|
6
|
-
data = %w(A - - - B C)
|
7
|
-
subject.detect_cap_insets(data).should == [1, 2]
|
8
|
-
end
|
9
|
-
|
10
|
-
it "returns zero when all items are the same" do
|
11
|
-
data = %w(- - -)
|
12
|
-
subject.detect_cap_insets(data).should == [0, 0]
|
13
|
-
end
|
14
|
-
|
15
|
-
it "returns the length of the collection when all items are unique" do
|
16
|
-
data = %w(A B C D E)
|
17
|
-
subject.detect_cap_insets(data).should == [5, 0]
|
18
|
-
end
|
19
|
-
|
20
|
-
it "does not fail with a single-item collection" do
|
21
|
-
data = %w(A)
|
22
|
-
subject.detect_cap_insets(data).should == [1, 0]
|
23
|
-
end
|
24
|
-
|
25
|
-
it "can handle multiple runs" do
|
26
|
-
data = %w(A A A B C - - - - - D E F F F)
|
27
|
-
subject.detect_cap_insets(data).should == [5, 5]
|
28
|
-
end
|
29
|
-
|
30
|
-
it "can handle longest runs at the beginning" do
|
31
|
-
data = %w(- - - A)
|
32
|
-
subject.detect_cap_insets(data).should == [0, 1]
|
33
|
-
end
|
34
|
-
|
35
|
-
it "can handle longest runs at the end" do
|
36
|
-
data = %w(A - - -)
|
37
|
-
subject.detect_cap_insets(data).should == [1, 0]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
data/spec/elrio/cli_spec.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Elrio::CLI do
|
4
|
-
let(:output) { double(:output) }
|
5
|
-
|
6
|
-
subject { Elrio::CLI.new(output) }
|
7
|
-
|
8
|
-
describe "#run" do
|
9
|
-
let(:runner) { double(Elrio::Runner) }
|
10
|
-
|
11
|
-
before do
|
12
|
-
Elrio::Runner.stub(:new).and_return(runner)
|
13
|
-
end
|
14
|
-
|
15
|
-
context "when run without a command" do
|
16
|
-
it "prints usage instructions" do
|
17
|
-
output.should_receive(:puts).with(/Usage: elrio <command> <images>/)
|
18
|
-
subject.run([])
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
context "when run with the analyze command" do
|
23
|
-
before do
|
24
|
-
runner.stub(:analyze).and_return(Elrio::Insets.new(1, 2, 3, 4))
|
25
|
-
end
|
26
|
-
|
27
|
-
it "prints the cap insets for each file" do
|
28
|
-
args = %w(analyze foo bar)
|
29
|
-
|
30
|
-
args[1..-1].each do |arg|
|
31
|
-
runner.should_receive(:analyze).with(arg)
|
32
|
-
output.should_receive(:puts).with("#{arg}: [1, 2, 3, 4]")
|
33
|
-
end
|
34
|
-
|
35
|
-
subject.run(args)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context "when run with the optimize command" do
|
40
|
-
before do
|
41
|
-
runner.stub(:optimize).and_return(Elrio::Insets.new(1, 2, 3, 4))
|
42
|
-
end
|
43
|
-
|
44
|
-
it "prints the cap insets for each file" do
|
45
|
-
args = %w(optimize foo bar)
|
46
|
-
|
47
|
-
args[1..-1].each do |arg|
|
48
|
-
runner.should_receive(:optimize).with(arg)
|
49
|
-
output.should_receive(:puts).with("#{arg}: [1, 2, 3, 4]")
|
50
|
-
end
|
51
|
-
|
52
|
-
subject.run(args)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Elrio::ImageOptimizer do
|
4
|
-
describe "#detect_cap_insets" do
|
5
|
-
let(:cap_inset_detector) { double(Elrio::CapInsetDetector) }
|
6
|
-
let(:image) { double(ChunkyPNG::Image) }
|
7
|
-
let(:columns) { [double] * 5 }
|
8
|
-
let(:rows) { [double] * 3 }
|
9
|
-
|
10
|
-
before do
|
11
|
-
image.stub(:width).and_return(columns.count)
|
12
|
-
image.stub(:height).and_return(rows.count)
|
13
|
-
image.stub(:column) {|x| columns[x] }
|
14
|
-
image.stub(:row) {|x| rows[x] }
|
15
|
-
|
16
|
-
cap_inset_detector.stub(:detect_cap_insets).with(rows).and_return([1, 3])
|
17
|
-
cap_inset_detector.stub(:detect_cap_insets).with(columns).and_return([2, 4])
|
18
|
-
|
19
|
-
Elrio::CapInsetDetector.stub(:new).and_return(cap_inset_detector)
|
20
|
-
end
|
21
|
-
|
22
|
-
context "in non-retina mode" do
|
23
|
-
it "passes the rows and columns to the detector" do
|
24
|
-
subject.detect_cap_insets(image).should == Elrio::Insets.new(1, 2, 3, 4)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context "in retina mode" do
|
29
|
-
subject { Elrio::ImageOptimizer.new(true) }
|
30
|
-
|
31
|
-
it "halves the cap insets" do
|
32
|
-
subject.detect_cap_insets(image).should == Elrio::Insets.new(1, 1, 2, 2)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "#optimize" do
|
38
|
-
let(:image) { ChunkyPNG::Image.from_file("spec/fixtures/original.png") }
|
39
|
-
|
40
|
-
context "in non-retina mode" do
|
41
|
-
let(:expected) { ChunkyPNG::Image.from_file("spec/fixtures/optimized.png") }
|
42
|
-
|
43
|
-
it "produces the expected image" do
|
44
|
-
optimized = subject.optimize(image, Elrio::Insets.new(48, 48, 48, 48))
|
45
|
-
optimized.should == expected
|
46
|
-
end
|
47
|
-
|
48
|
-
it "does not modify the insets" do
|
49
|
-
insets = Elrio::Insets.new(48, 48, 48, 48)
|
50
|
-
subject.optimize(image, insets)
|
51
|
-
insets.should == Elrio::Insets.new(48, 48, 48, 48)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "returns nil if insets are too big" do
|
55
|
-
optimized = subject.optimize(image, Elrio::Insets.new(image.height, image.width, 0, 0))
|
56
|
-
optimized.should be_nil
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
context "in retina mode" do
|
61
|
-
let(:expected) { ChunkyPNG::Image.from_file("spec/fixtures/optimized@2x.png") }
|
62
|
-
|
63
|
-
subject { Elrio::ImageOptimizer.new(true) }
|
64
|
-
|
65
|
-
it "produces the expected image" do
|
66
|
-
optimized = subject.optimize(image, Elrio::Insets.new(24, 24, 24, 24))
|
67
|
-
optimized.should == expected
|
68
|
-
end
|
69
|
-
|
70
|
-
it "does not modify the insets" do
|
71
|
-
insets = Elrio::Insets.new(24, 24, 24, 24)
|
72
|
-
subject.optimize(image, insets)
|
73
|
-
insets.should == Elrio::Insets.new(24, 24, 24, 24)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "returns nil if insets are too big" do
|
77
|
-
optimized = subject.optimize(image, Elrio::Insets.new(image.height / 2, image.width / 2, 0, 0))
|
78
|
-
optimized.should be_nil
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/spec/elrio/runner_spec.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Elrio::Runner do
|
4
|
-
let(:image) { double(ChunkyPNG::Image) }
|
5
|
-
let(:optimized) { double(ChunkyPNG::Image) }
|
6
|
-
let(:image_optimizer) { double(Elrio::ImageOptimizer) }
|
7
|
-
let(:insets) { double(Elrio::Insets) }
|
8
|
-
|
9
|
-
before do
|
10
|
-
ChunkyPNG::Image.stub(:from_file).with(path).and_return(image)
|
11
|
-
image_optimizer.stub(:detect_cap_insets).and_return(insets)
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "#analyze" do
|
15
|
-
context "with a non-retina image" do
|
16
|
-
let(:path) { "file/path.png".freeze }
|
17
|
-
|
18
|
-
before do
|
19
|
-
Elrio::ImageOptimizer.should_receive(:new).with(false).and_return(image_optimizer)
|
20
|
-
end
|
21
|
-
|
22
|
-
it "returns the cap insets detected by the optimizer" do
|
23
|
-
subject.analyze(path).should == insets
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
context "with a retina image" do
|
28
|
-
let(:path) { "file/path@2x.png".freeze }
|
29
|
-
|
30
|
-
before do
|
31
|
-
Elrio::ImageOptimizer.should_receive(:new).with(true).and_return(image_optimizer)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "returns the cap insets detected by the optimizer" do
|
35
|
-
subject.analyze(path).should == insets
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "#optimize" do
|
41
|
-
context "when the image is optimizable" do
|
42
|
-
before do
|
43
|
-
image_optimizer.stub(:optimize).with(image, insets).and_return(optimized)
|
44
|
-
end
|
45
|
-
|
46
|
-
context "with a non-retina image" do
|
47
|
-
let(:path) { "file/path.png".freeze }
|
48
|
-
|
49
|
-
before do
|
50
|
-
Elrio::ImageOptimizer.should_receive(:new).with(false).and_return(image_optimizer)
|
51
|
-
optimized.should_receive(:save).with("file/path-optimized.png")
|
52
|
-
end
|
53
|
-
|
54
|
-
it "returns the cap insets detected by the optimizer" do
|
55
|
-
subject.optimize(path).should == insets
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "with a retina image" do
|
60
|
-
let(:path) { "file/path@2x.png".freeze }
|
61
|
-
|
62
|
-
before do
|
63
|
-
Elrio::ImageOptimizer.should_receive(:new).with(true).and_return(image_optimizer)
|
64
|
-
optimized.should_receive(:save).with("file/path-optimized@2x.png")
|
65
|
-
end
|
66
|
-
|
67
|
-
it "returns the cap insets detected by the optimizer" do
|
68
|
-
subject.optimize(path).should == insets
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context "when the image is not optimizable" do
|
74
|
-
let(:path) { "file/path.png".freeze }
|
75
|
-
|
76
|
-
before do
|
77
|
-
Elrio::ImageOptimizer.should_receive(:new).and_return(image_optimizer)
|
78
|
-
image_optimizer.stub(:optimize).with(image, insets).and_return(nil)
|
79
|
-
end
|
80
|
-
|
81
|
-
it "does not blow up" do
|
82
|
-
-> { subject.optimize(path) }.should_not raise_exception
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
data/spec/fixtures/optimized.png
DELETED
Binary file
|
Binary file
|
data/spec/fixtures/original.png
DELETED
Binary file
|