dimension 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/examples/sinatra.rb +17 -1
- data/lib/dimension.rb +19 -9
- data/lib/dimension/image.rb +15 -4
- data/lib/dimension/processors/image_magick.rb +3 -2
- data/lib/dimension/processors/imlib2.rb +0 -11
- data/lib/dimension/processors/vips.rb +142 -0
- data/lib/dimension/version.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 193039197b19c73705300522aaf23f6ae3b07aee
|
4
|
+
data.tar.gz: c741743c6dfd4ab233832a46cc97e2b77f8b3901
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6660d273e74e9772aac18cf4d29c585b517ec4e117f933015c5d0b81c82f9986af371c1c8d10c9c25cced1624db8ea2eeae519d5694ea6b50d0d47ababab371b
|
7
|
+
data.tar.gz: a3b939d1c3d5d10b022d71cbfc1ab04549e278ba396b9424448e058e77a5a69f814d5e1920b7e77634f6a5cb524a8a62c40615a80727e4a00943cbf5489e8228
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@ Fast, simplified image resizing for Ruby. No ImageMagick.
|
|
5
5
|
|
6
6
|
``` rb
|
7
7
|
require 'dimension'
|
8
|
-
|
8
|
+
|
9
9
|
thumb = Dimension.open('tux.png')
|
10
10
|
thumb.generate('100x100') # => { :width => 100, :height => 100 }
|
11
11
|
thumb.save('resized.png')
|
@@ -33,7 +33,7 @@ You can also pass a block, which will ensure the original image is closed after
|
|
33
33
|
``` rb
|
34
34
|
get '/resize/:file' do
|
35
35
|
thumb = Dimension.open(params[:file])
|
36
|
-
thumb.generate('200x300
|
36
|
+
thumb.generate('200x300@') do
|
37
37
|
thumb.to_response
|
38
38
|
end
|
39
39
|
end
|
data/examples/sinatra.rb
CHANGED
@@ -3,11 +3,27 @@ ROOT = File.expand_path(File.dirname(__FILE__))
|
|
3
3
|
require 'sinatra'
|
4
4
|
require ROOT + '/../lib/dimension'
|
5
5
|
|
6
|
+
if processor = ARGV[0]
|
7
|
+
puts "Using #{processor} as processor"
|
8
|
+
Dimension.processor = processor
|
9
|
+
end
|
10
|
+
|
11
|
+
geometries = [
|
12
|
+
'200x200',
|
13
|
+
'300x200!',
|
14
|
+
'200x300#',
|
15
|
+
'20x50:ne'
|
16
|
+
]
|
17
|
+
|
6
18
|
get '/' do
|
7
19
|
images = Dir.glob(File.join(ROOT, 'assets') + '/*')
|
20
|
+
index = 0
|
8
21
|
links = images.map do |i|
|
9
22
|
name = File.basename(i)
|
10
|
-
|
23
|
+
geom = geometries[index]
|
24
|
+
index += 1
|
25
|
+
index = 0 unless geometries[index]
|
26
|
+
"<a href='/images/#{name}?geometry=#{geom}'>#{name}</a>"
|
11
27
|
end
|
12
28
|
'<ul><li>' + links.join('</li><li>') + '</li></ul>'
|
13
29
|
end
|
data/lib/dimension.rb
CHANGED
@@ -6,6 +6,7 @@ module Dimension
|
|
6
6
|
ROOT = File.expand_path(File.dirname(__FILE__))
|
7
7
|
|
8
8
|
PROCESSORS = {
|
9
|
+
'vips' => 'VipsProcessor',
|
9
10
|
'imlib2' => 'Imlib2Processor',
|
10
11
|
'image_magick' => 'ImageMagickProcessor'
|
11
12
|
}
|
@@ -27,14 +28,23 @@ module Dimension
|
|
27
28
|
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
def load_processor(name)
|
32
|
+
# puts "Loading #{name}"
|
33
|
+
require name
|
34
|
+
Dimension.processor = name
|
35
|
+
true
|
36
|
+
rescue LoadError => e
|
37
|
+
# puts "#{name} not found."
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
unless load_processor 'vips'
|
42
|
+
unless load_processor 'imlib2'
|
43
|
+
out = `which convert`
|
44
|
+
if $?.success?
|
45
|
+
Dimension.processor = 'image_magick'
|
46
|
+
else
|
47
|
+
puts "No available processors found. Please install ruby-vips, ruby-imlib2 or ImageMagick."
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
data/lib/dimension/image.rb
CHANGED
@@ -18,7 +18,7 @@ class Image
|
|
18
18
|
|
19
19
|
# Geometry string patterns
|
20
20
|
RESIZE_GEOMETRY = /^(\d+)?x(\d+)?[><%^!]?$|^\d+@$/ # e.g. '300x200!'
|
21
|
-
CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)[
|
21
|
+
CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)[:|@](\w{1,2})?$/ # e.g. '20x50:ne'
|
22
22
|
CROP_GEOMETRY = /^(\d+)x(\d+)([+-]\d+)?([+-]\d+)?(\w{1,2})?$/ # e.g. '30x30+10+10'
|
23
23
|
|
24
24
|
attr_reader :file
|
@@ -46,13 +46,14 @@ class Image
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def generate!(geometry, output_file = nil)
|
49
|
+
@geometry_string = geometry # for saving as [file]-[geometry].[ext]
|
49
50
|
resize_to(geometry)
|
50
51
|
save(output_file) && self
|
51
52
|
end
|
52
53
|
|
53
54
|
def resize_to(geometry)
|
54
55
|
case geometry
|
55
|
-
when RESIZE_GEOMETRY
|
56
|
+
when RESIZE_GEOMETRY
|
56
57
|
log "Resize -- #{$1}x#{$2}"
|
57
58
|
resize($1, $2)
|
58
59
|
when CROPPED_RESIZE_GEOMETRY
|
@@ -71,7 +72,6 @@ class Image
|
|
71
72
|
|
72
73
|
def save(out_file)
|
73
74
|
new_geometry = get_new_geometry
|
74
|
-
|
75
75
|
path = @path
|
76
76
|
|
77
77
|
if out_file and File.directory?(out_file)
|
@@ -80,7 +80,7 @@ class Image
|
|
80
80
|
end
|
81
81
|
|
82
82
|
if out_file.nil?
|
83
|
-
out_file = File.join(path, @name.sub(File.extname(file), '-' +
|
83
|
+
out_file = File.join(path, @name.sub(File.extname(file), '-' + @geometry_string + File.extname(file)))
|
84
84
|
end
|
85
85
|
|
86
86
|
log "Writing file: #{out_file}"
|
@@ -98,6 +98,17 @@ class Image
|
|
98
98
|
to_response
|
99
99
|
end
|
100
100
|
|
101
|
+
def to_rgba
|
102
|
+
bytes = image_data.bytes
|
103
|
+
(1..bytes.length).step(4).map { |i| bytes[i..i+2] << bytes[i-1] }.flatten
|
104
|
+
end
|
105
|
+
|
106
|
+
# transforms data (RGBA buffer) into a array of RGB values
|
107
|
+
def to_rgb
|
108
|
+
bytes = image_data.bytes
|
109
|
+
(1..bytes.length).step(4).map { |i| [bytes[i-1],bytes[i],bytes[i+1]] }.flatten
|
110
|
+
end
|
111
|
+
|
101
112
|
def inspect
|
102
113
|
geometry = get_new_geometry
|
103
114
|
"#<Dimension::Image:#{object_id} @width=#{geometry[0]}, @height=#{geometry[1]}>"
|
@@ -9,13 +9,14 @@ module ImageMagickProcessor
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def image_data
|
12
|
-
|
12
|
+
@current_path = path if @current_path.nil?
|
13
|
+
IO.read(@current_path)
|
13
14
|
end
|
14
15
|
|
15
16
|
def save_as(new_file_path)
|
16
17
|
return if new_file_path == @temp_file
|
17
18
|
FileUtils.mv(@temp_file, new_file_path)
|
18
|
-
@
|
19
|
+
@current_path = new_file_path
|
19
20
|
end
|
20
21
|
|
21
22
|
def get_new_geometry
|
@@ -16,17 +16,6 @@ module Imlib2Processor
|
|
16
16
|
File.extname(file).sub('.', '')
|
17
17
|
end
|
18
18
|
|
19
|
-
def to_rgba
|
20
|
-
bytes = data.bytes
|
21
|
-
(1..bytes.length).step(4).map { |i| bytes[i..i+2] << bytes[i-1] }.flatten
|
22
|
-
end
|
23
|
-
|
24
|
-
# transforms data (RGBA buffer) into a array of RGB values
|
25
|
-
def to_rgb
|
26
|
-
bytes = data.bytes
|
27
|
-
(1..bytes.length).step(4).map { |i| [bytes[i-1],bytes[i],bytes[i+1]] }.flatten
|
28
|
-
end
|
29
|
-
|
30
19
|
def image_data
|
31
20
|
unless @temp_file
|
32
21
|
@temp_file = "/tmp/#{$$}.#{File.basename(file)}"
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# from examples at
|
2
|
+
# - https://github.com/eltiare/carrierwave-vips/blob/master/lib/carrierwave/vips.rb
|
3
|
+
# - https://github.com/jcupitt/ruby-vips/tree/master/examples
|
4
|
+
|
5
|
+
require 'vips'
|
6
|
+
|
7
|
+
module VipsProcessor
|
8
|
+
|
9
|
+
FORMAT_OPTS = {
|
10
|
+
'jpeg' => { :quality => 0.9 },
|
11
|
+
'png' => { :compression => 6, :interlace => false }
|
12
|
+
}
|
13
|
+
|
14
|
+
SHARPEN_MASK = begin
|
15
|
+
conv_mask = [
|
16
|
+
[ -1, -1, -1 ],
|
17
|
+
[ -1, 24, -1 ],
|
18
|
+
[ -1, -1, -1 ]
|
19
|
+
]
|
20
|
+
::VIPS::Mask.new conv_mask, 16
|
21
|
+
end
|
22
|
+
|
23
|
+
def image
|
24
|
+
@image ||= if format == 'jpeg'
|
25
|
+
VIPS::Image.jpeg(@file, :sequential => true)
|
26
|
+
elsif format == 'png'
|
27
|
+
VIPS::Image.png(@file, :sequential => true)
|
28
|
+
else
|
29
|
+
VIPS::Image.new(@file)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def geometry
|
34
|
+
[image.x_size, image.y_size]
|
35
|
+
end
|
36
|
+
|
37
|
+
def format
|
38
|
+
file[/.png$/] ? 'png' : file[/jpe?g$/] ? 'jpeg' : File.extname(file).sub('.', '')
|
39
|
+
end
|
40
|
+
|
41
|
+
def image_data
|
42
|
+
writer.to_memory
|
43
|
+
end
|
44
|
+
|
45
|
+
def save_as(new_file_path)
|
46
|
+
save(new_file_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def save!
|
50
|
+
save(file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def close
|
54
|
+
log "Closing image and cutting thread..."
|
55
|
+
@image = nil
|
56
|
+
VIPS::thread_shutdown
|
57
|
+
# image.delete!(true) # free image, and de-cache it too
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_new_geometry
|
61
|
+
geometry
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def resize(width, height, min_or_max = :min)
|
67
|
+
ratio = get_ratio(width, height, min_or_max)
|
68
|
+
if ratio == 1
|
69
|
+
return log "Same ratio as original."
|
70
|
+
end
|
71
|
+
if ratio > 1
|
72
|
+
@image = image.affinei_resize :nearest, ratio
|
73
|
+
else
|
74
|
+
if ratio <= 0.5
|
75
|
+
factor = (1.0 / ratio).floor
|
76
|
+
@image = image.shrink(factor)
|
77
|
+
@image = image.tile_cache(image.x_size, 1, 30)
|
78
|
+
ratio = get_ratio(width, height, min_or_max)
|
79
|
+
end
|
80
|
+
@image = image.affinei_resize :bicubic, ratio
|
81
|
+
@image = image.conv SHARPEN_MASK
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def resize_and_crop(new_width, new_height, gravity)
|
86
|
+
resize(new_width, new_height, :max)
|
87
|
+
|
88
|
+
if image.x_size > new_width.to_i
|
89
|
+
top = 0
|
90
|
+
left = (image.x_size - new_width.to_i) / 2
|
91
|
+
elsif image.y_size > new_height.to_i
|
92
|
+
left = 0
|
93
|
+
top = (image.y_size - new_height.to_i) / 2
|
94
|
+
else
|
95
|
+
left = 0
|
96
|
+
top = 0
|
97
|
+
end
|
98
|
+
|
99
|
+
@image = image.extract_area(left, top, new_width.to_i, new_height.to_i)
|
100
|
+
end
|
101
|
+
|
102
|
+
def crop(width, height, x, y, gravity)
|
103
|
+
raise "TODO"
|
104
|
+
end
|
105
|
+
|
106
|
+
def crop_scaled(x, y, new_w, new_h)
|
107
|
+
raise "TODO"
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_ratio(width, height, min_or_max = :min)
|
111
|
+
width_ratio = width.to_f / image.x_size
|
112
|
+
height_ratio = height.to_f / image.y_size
|
113
|
+
if height.nil?
|
114
|
+
width_ratio
|
115
|
+
elsif width.nil?
|
116
|
+
height_ratio
|
117
|
+
else
|
118
|
+
[width_ratio, height_ratio].send(min_or_max)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def write(filename, strip = false)
|
123
|
+
if strip
|
124
|
+
writer.remove_exif
|
125
|
+
writer.remove_icc
|
126
|
+
end
|
127
|
+
writer.write(filename)
|
128
|
+
end
|
129
|
+
|
130
|
+
def writer
|
131
|
+
@writer ||= writer_class.send(:new, @image, FORMAT_OPTS[format] || {})
|
132
|
+
end
|
133
|
+
|
134
|
+
def writer_class
|
135
|
+
case format
|
136
|
+
when 'jpeg' then VIPS::JPEGWriter
|
137
|
+
when 'png' then VIPS::PNGWriter
|
138
|
+
else VIPS::Writer
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
data/lib/dimension/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dimension
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomás Pollak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack-throttle
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/dimension/middleware.rb
|
50
50
|
- lib/dimension/processors/image_magick.rb
|
51
51
|
- lib/dimension/processors/imlib2.rb
|
52
|
+
- lib/dimension/processors/vips.rb
|
52
53
|
- lib/dimension/version.rb
|
53
54
|
homepage: https://github.com/tomas/dimension
|
54
55
|
licenses: []
|