dimension 0.1.4 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 144828daa18cf773b80abb57606d047917fea1f7
4
- data.tar.gz: a4310e48fc02af5b1899c2bdb6822e51c3ff35e3
3
+ metadata.gz: 193039197b19c73705300522aaf23f6ae3b07aee
4
+ data.tar.gz: c741743c6dfd4ab233832a46cc97e2b77f8b3901
5
5
  SHA512:
6
- metadata.gz: 6eddc9393524c095d2a7a9a6f9af0a05b741bff3147d2b346738364109f985716359a23b5436b6fe7b42ac19abc60f1c34269f376afaa5675d7deadfc68e036f
7
- data.tar.gz: af4ac2e3807547a92113983ceada7d5f51c0e8b18d09b7b0650cb32844a33bff8a31fbe792a759043646052b6019fd8f1d197e27eddd28b74eff8e6eaabeee96
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#') do
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
- "<a href='/images/#{name}'>#{name}</a>"
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
- begin
31
- require 'imlib2'
32
- Dimension.processor = 'imlib2'
33
- rescue LoadError
34
- out = `which convert`
35
- if $?.success?
36
- Dimension.processor = 'image_magick'
37
- else
38
- puts "No available processors found. Please install ruby-imlib2 or ImageMagick."
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
@@ -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+)[:|#](\w{1,2})?$/ # e.g. '20x50:ne'
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), '-' + new_geometry.join('x') + 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
- IO.read(@temp_file)
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
- @temp_file = new_file_path # otherwise we'd get a ENOENT error when reading its data
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
@@ -1,7 +1,7 @@
1
1
  module Dimension
2
2
  MAJOR = 0
3
- MINOR = 1
4
- PATCH = 4
3
+ MINOR = 2
4
+ PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
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.1.4
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-04 00:00:00.000000000 Z
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: []