dimension 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1be13d491cf7820b7fdd352dac29074884ee584
4
+ data.tar.gz: 1cf3a7360986cebb5d83089ad33b7fe85aa171b7
5
+ SHA512:
6
+ metadata.gz: 1541bb58760ef65bb60df5342b7fe1f85ae9a5fc4b36d4ec896be9d204749eb38cd631e40748c61af7f341865fe2c5068b49cb6453307092f7e31f7d97f025a6
7
+ data.tar.gz: 7d992bb47aeaeb109d92cd3d0944912012bf4f29fb278c7375122bc5b37cbc8076246e0672f891dd9383696196f1f1d31c8d4f008c1718ae8059f6a9579111df
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ Resizer
2
+ =======
3
+
4
+ Fast, simplified image resizing for Ruby. No ImageMagick.
5
+
6
+ ``` rb
7
+ require 'resizer'
8
+
9
+ thumb = Resizer.open('tux.png')
10
+ thumb.generate('100x100') # => { :width => 100, :height => 100 }
11
+ thumb.save('resized.png')
12
+ ```
13
+
14
+ Or generate and write file automatically.
15
+
16
+ ``` rb
17
+ thumb = Resizer.new('tux.png')
18
+ thumb.generate!('100x300!') # will write file as 'tux-100x300.png'
19
+ ```
20
+
21
+ # In memory processing
22
+
23
+ Yes sir, we have it.
24
+
25
+ ``` rb
26
+ thumb = Resizer.open(params[:file])
27
+ thumb.generate('200x300#')
28
+ thumb.image_data
29
+ ```
30
+
31
+ You can also pass a block, which will ensure the original image is closed after processing.
32
+
33
+ ``` rb
34
+ get '/resize/:file' do
35
+ thumb = Resizer.open(params[:file])
36
+ thumb.generate('200x300#') do |res|
37
+ @out = thumb.to_response
38
+ end
39
+ @out.to_response
40
+ end
41
+ ```
42
+
43
+ # Resizing geometries
44
+
45
+ This is taken directly from the excellent Dragonfly gem.
46
+
47
+ Copyright
48
+ =========
49
+
50
+ (c) Fork, Ltd. MIT Licensed.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/benchmark.rb ADDED
@@ -0,0 +1,28 @@
1
+ require './lib/resizer'
2
+ require 'benchmark'
3
+
4
+ file = ARGV[0] or abort('File needed')
5
+ geometry = ARGV[1] or abort('Geometry required: 100x100#ne')
6
+
7
+ out = "#{File.basename(file)}.out#{File.extname(file)}"
8
+ puts "Processing #{file}"
9
+
10
+ Benchmark.bm do |x|
11
+
12
+ x.report do
13
+ Resizer.processor = 'imlib2'
14
+ a = Resizer.open(file)
15
+ res = a.generate!(geometry)
16
+ puts res.inspect
17
+ end
18
+
19
+ sleep 1
20
+
21
+ x.report do
22
+ Resizer.processor = 'image_magick'
23
+ b = Resizer.open(file)
24
+ res = b.generate!(geometry)
25
+ puts res.inspect
26
+ end
27
+
28
+ end
data/bin/dimension ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require './lib/dimension'
4
+ require 'benchmark'
5
+
6
+ file = ARGV[0] or abort('File needed. Try with examples/assets/chair.jpg')
7
+ geometry = ARGV[1] or abort('Geometry required. Example: 100x100')
8
+
9
+ thumb = Dimension.open(file)
10
+ res = thumb.generate!(geometry)
11
+ # puts res.inspect
data/dimension.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/dimension/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "dimension"
6
+ s.version = Dimension::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Tomás Pollak']
9
+ s.email = ['tomas@forkhq.com']
10
+ s.homepage = "https://github.com/tomas/dimension"
11
+ s.summary = "Native, in-process image resizing in Ruby."
12
+ s.description = "Yes, there are other graphic libraries besides ImageMagick."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "dimension"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
19
+ s.require_path = 'lib'
20
+ # s.bindir = 'bin'
21
+ end
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,32 @@
1
+ ROOT = File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'sinatra'
4
+ require ROOT + '/../lib/resizer'
5
+
6
+ get '/' do
7
+ images = Dir.glob(File.join(ROOT, 'assets') + '/*')
8
+ links = images.map do |i|
9
+ name = File.basename(i)
10
+ "<a href='/images/#{name}'>#{name}</a>"
11
+ end
12
+ '<ul><li>' + links.join('</li><li>') + '</li></ul>'
13
+ end
14
+
15
+ get '/images/:file' do
16
+ file = File.join(ROOT, 'assets', params[:file])
17
+
18
+ if params[:geometry].nil?
19
+ puts "Returning original file: #{file}"
20
+ return send_file file
21
+ end
22
+
23
+ begin
24
+ thumb = Resizer.open(file)
25
+ rescue => e
26
+ return e.message
27
+ end
28
+
29
+ thumb.generate(params[:geometry]) do
30
+ thumb.to_response
31
+ end
32
+ end
@@ -0,0 +1,106 @@
1
+ require 'fileutils'
2
+
3
+ module Dimension
4
+
5
+ class Image
6
+
7
+ GRAVITIES = {
8
+ 'nw' => 'NorthWest',
9
+ 'n' => 'North',
10
+ 'ne' => 'NorthEast',
11
+ 'w' => 'West',
12
+ 'c' => 'Center',
13
+ 'e' => 'East',
14
+ 'sw' => 'SouthWest',
15
+ 's' => 'South',
16
+ 'se' => 'SouthEast'
17
+ }
18
+
19
+ # Geometry string patterns
20
+ RESIZE_GEOMETRY = /^(\d+)?x(\d+)?[><%^!]?$|^\d+@$/ # e.g. '300x200!'
21
+ CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)#(\w{1,2})?$/ # e.g. '20x50#ne'
22
+ CROP_GEOMETRY = /^(\d+)x(\d+)([+-]\d+)?([+-]\d+)?(\w{1,2})?$/ # e.g. '30x30+10+10'
23
+
24
+ attr_reader :file
25
+
26
+ def initialize(file)
27
+ unless Dimension.processor
28
+ raise "No processor set! Please set Dimension.processor = 'imlib2' or 'image_magick'"
29
+ end
30
+
31
+ @file = File.expand_path(file)
32
+ raise ArgumentError, "File not found: #{@file}" unless File.exist?(@file)
33
+ log "File loaded: #{file}. Geometry: #{geometry.join('x')}"
34
+ end
35
+
36
+ def generate(geometry, &block)
37
+ new_geometry = resize_to(geometry)
38
+ if block_given?
39
+ out = yield(new_geometry)
40
+ close
41
+ end
42
+ out || self
43
+ end
44
+
45
+ def generate!(geometry, output_file = nil)
46
+ resize_to(geometry)
47
+ save(output_file) && self
48
+ end
49
+
50
+ def resize_to(geometry)
51
+ case geometry
52
+ when RESIZE_GEOMETRY
53
+ log "Resize -- #{$1}x#{$2}"
54
+ resize($1, $2)
55
+ when CROPPED_RESIZE_GEOMETRY
56
+ log "Resize and crop -- width: #{$1}, height: #{$2}, gravity: #{$3}"
57
+ resize_and_crop($1, $2, $3)
58
+ when CROP_GEOMETRY
59
+ log "Crop -- width: #{$1}, height: #{$2}, x: #{$3}, y: #{$4}, gravity: #{$5}"
60
+ crop($1, $2, $3, $4, $5)
61
+ else
62
+ raise ArgumentError, "Didn't recognise the geometry string #{geometry}"
63
+ end
64
+
65
+ new_geometry = get_new_geometry
66
+ {:width => new_geometry[0], :height => new_geometry[1]}
67
+ end
68
+
69
+ def save(out_file)
70
+ new_geometry = get_new_geometry
71
+
72
+ if out_file.nil?
73
+ out_file = file.sub(File.extname(file), '-' + new_geometry.join('x') + File.extname(file))
74
+ end
75
+
76
+ log "Writing file: #{out_file}"
77
+ save_as(out_file) or return false
78
+ close
79
+
80
+ {:file => out_file, :width => new_geometry[0], :height => new_geometry[1]}
81
+ end
82
+
83
+ def to_s
84
+ image_data
85
+ end
86
+
87
+ def to_a
88
+ to_response
89
+ end
90
+
91
+ def inspect
92
+ geometry = get_new_geometry
93
+ "#<Dimension::Image:#{object_id} @width=#{geometry[0]}, @height=#{geometry[1]}>"
94
+ end
95
+
96
+ def to_response(env = nil)
97
+ [200, {'Content-Type' => "image/#{format}"}, image_data]
98
+ end
99
+
100
+ def log(msg)
101
+ puts "[Dimension::#{Dimension.processor}] #{msg}"
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,75 @@
1
+ module ImageMagickProcessor
2
+
3
+ def geometry
4
+ [image_details[:width], image_details[:height]]
5
+ end
6
+
7
+ def format
8
+ image_details[:format]
9
+ end
10
+
11
+ def image_data
12
+ IO.read(@temp_file)
13
+ end
14
+
15
+ def save_as(new_file_path)
16
+ return if new_file_path == @temp_file
17
+ FileUtils.mv(@temp_file, new_file_path)
18
+ end
19
+
20
+ def get_new_geometry
21
+ self.class.new(@temp_file).geometry
22
+ end
23
+
24
+ def save!
25
+ FileUtils.mv(@temp_file, file)
26
+ end
27
+
28
+ def close
29
+ log "Removing temp file: #{@temp_file}"
30
+ FileUtils.rm_f(@temp_file)
31
+ end
32
+
33
+ private
34
+
35
+ def resize(w, h)
36
+ convert "-resize #{w}x#{h}"
37
+ end
38
+
39
+ def resize_and_crop(w, h, gravity)
40
+ gravity = self.class::GRAVITIES[gravity || 'c']
41
+ convert "-resize #{w}x#{h}^^ -gravity #{gravity} -crop #{w}x#{h}+0+0 +repage"
42
+ end
43
+
44
+ def crop(width, height, x = 0, y = 0, gravity)
45
+ raise ArgumentError, "you can't give a crop offset and gravity at the same time" if x != 0 and gravity
46
+
47
+ x = '+' + x unless x[/^[+-]/]
48
+ y = '+' + y unless y[/^[+-]/]
49
+ args = "-crop #{w}x#{h}#{x}#{y} +repage"
50
+
51
+ if gravity and g = self.class::GRAVITIES[gravity]
52
+ args = "-gravity #{gravity} #{args}"
53
+ end
54
+
55
+ convert args
56
+ end
57
+
58
+ def convert(args)
59
+ @temp_file = "/tmp/#{$$}.#{File.basename(file)}"
60
+ out = `convert "#{file}" #{args} "#{@temp_file}"`
61
+ end
62
+
63
+ def image_details
64
+ @image_details ||= identify(file)
65
+ end
66
+
67
+ def identify(path)
68
+ out = `identify -ping -format '%m %w %h' "#{path}"`
69
+ format, width, height = out.split
70
+ { :format => format.downcase,
71
+ :width => width.to_i,
72
+ :height => height.to_i }
73
+ end
74
+
75
+ end
@@ -0,0 +1,141 @@
1
+ require 'imlib2'
2
+
3
+ module Imlib2Processor
4
+
5
+ def image
6
+ @image ||= Imlib2::Image.load(@file)
7
+ end
8
+
9
+ def geometry
10
+ [image.w, image.h]
11
+ end
12
+
13
+ def format
14
+ image.format # .gsub('jpeg', 'jpg')
15
+ rescue ArgumentError
16
+ File.extname(file).sub('.', '')
17
+ end
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
+ def image_data
31
+ unless @temp_file
32
+ @temp_file = "/tmp/#{$$}.#{File.basename(file)}"
33
+ save_as(@temp_file)
34
+ end
35
+ IO.read(@temp_file)
36
+ end
37
+
38
+ def save_as(new_file_path)
39
+ image.save(new_file_path)
40
+ end
41
+
42
+ def save!
43
+ image.save(file)
44
+ end
45
+
46
+ def close
47
+ log "Closing image."
48
+ FileUtils.rm(@temp_file) if @temp_file
49
+ image.delete!(true) # free image, and de-cache it too
50
+ end
51
+
52
+ def get_new_geometry
53
+ geometry
54
+ end
55
+
56
+ # http://pablotron.org/software/imlib2-ruby/doc/
57
+ #
58
+ # iw, ih = old_image.width, old_image.height
59
+ # new_w, new_h = iw - 20, ih - 20
60
+ # values = [10, 10, iw - 10, iw - 10, new_w, new_h]
61
+ # new_image = old_image.crop_scaled values
62
+
63
+ def resize(w, h)
64
+ new_w, new_h = get_resize_geometry(w, h, false)
65
+ crop_scaled(0, 0, new_w, new_h)
66
+ end
67
+
68
+ # def resize_and_fill(w, h)
69
+ # new_w, new_h = get_resize_geometry(w, h, false)
70
+ # x = (w.to_i - new_w)
71
+ # y = (h.to_i - new_h)
72
+ # image.crop_scaled!(x*-1, y*-1, image.w + x*2, image.h + y*2, w.to_i, h.to_i)
73
+ # end
74
+
75
+ def resize_and_crop(w, h, gravity)
76
+ new_w, new_h = get_resize_geometry(w, h, true)
77
+ crop_scaled(0, 0, new_w, new_h)
78
+ # crop_scaled(0, 0, w, h)
79
+
80
+ offset = get_offset(w, h, gravity)
81
+ # original_height = image.h
82
+ # original_width = image.w
83
+
84
+ image.crop!(offset[0], offset[1], w.to_i, h.to_i)
85
+ # crop_scaled(offset[0], offset[1], new_w, new_h)
86
+ end
87
+
88
+ def crop(width, height, x, y, gravity)
89
+ rect = [x || 0, y || 0, width.to_i, height.to_i]
90
+ image.crop!(rect)
91
+ end
92
+
93
+ def crop_scaled(x, y, new_w, new_h)
94
+ log "Resizing #{image.w}x#{image.h} to #{new_w}x#{new_h}. Offset at #{x},#{y}"
95
+ image.crop_scaled!(x, y, image.w, image.h, new_w.to_i, new_h.to_i)
96
+ end
97
+
98
+ def get_offset(w, h, gravity = 'c')
99
+ if gravity.nil? or gravity == '' or gravity == 'c'
100
+ return get_center_offset(w, h)
101
+ else
102
+ raise 'Not implemented'
103
+ end
104
+ end
105
+
106
+ def get_center_offset(w, h)
107
+ w_diff = image.w - w.to_i
108
+ h_diff = image.h - h.to_i
109
+ log "Width diff: #{w_diff}"
110
+ log "Height diff: #{h_diff}"
111
+
112
+ if w_diff == h_diff
113
+ return [0, 0]
114
+ elsif w_diff > h_diff
115
+ return [w_diff.to_f/2, 0]
116
+ else
117
+ return [0, h_diff.to_f/2]
118
+ end
119
+ end
120
+
121
+ def get_resize_geometry(w, h, to_longer = true)
122
+ if to_longer
123
+ if image.w < image.h
124
+ new_h = ((w.to_f / image.w) * image.h).round
125
+ return w.to_i, new_h
126
+ else
127
+ new_w = ((h.to_f / image.h) * image.w).round
128
+ return new_w, h.to_i
129
+ end
130
+ else
131
+ if w && image.w >= image.h
132
+ new_h = ((w.to_f / image.w) * image.h).round
133
+ return w.to_i, new_h
134
+ else
135
+ new_w = ((h.to_f / image.h) * image.w).round
136
+ return new_w, h.to_i
137
+ end
138
+ end
139
+ end
140
+
141
+ end
@@ -0,0 +1,7 @@
1
+ module Dimension
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 1
5
+
6
+ VERSION = [MAJOR, MINOR, PATCH].join('.')
7
+ end
data/lib/dimension.rb ADDED
@@ -0,0 +1,39 @@
1
+ # require File.expand_path(File.dirname(__FILE__)) + '/image'
2
+ require 'dimension/image'
3
+
4
+ module Dimension
5
+
6
+ ROOT = File.expand_path(File.dirname(__FILE__))
7
+
8
+ PROCESSORS = {
9
+ 'imlib2' => 'Imlib2Processor',
10
+ 'image_magick' => 'ImageMagickProcessor'
11
+ }
12
+
13
+ def self.processor
14
+ @processor
15
+ end
16
+
17
+ def self.processor=(name)
18
+ @processor = PROCESSORS[name] or raise "Processor not found: #{name}"
19
+ # require File.join(ROOT, 'dimension', 'processors', name)
20
+ require_relative "dimension/processors/#{name}"
21
+ Image.include(Kernel.const_get(@processor))
22
+ end
23
+
24
+ def self.open(file)
25
+ Image.new(file)
26
+ end
27
+
28
+ end
29
+
30
+ begin
31
+ require 'imlib2'
32
+ Dimension.processor = 'imlib2'
33
+ rescue LoadError
34
+ if system("convert -h")
35
+ Dimension.processor = 'image_magick'
36
+ else
37
+ puts "No available processors found. Please install ruby-imlib2 or ImageMagick."
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dimension
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomás Pollak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Yes, there are other graphic libraries besides ImageMagick.
14
+ email:
15
+ - tomas@forkhq.com
16
+ executables:
17
+ - dimension
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - README.md
23
+ - Rakefile
24
+ - benchmark.rb
25
+ - bin/dimension
26
+ - dimension.gemspec
27
+ - examples/assets/black.gif
28
+ - examples/assets/chair.jpg
29
+ - examples/assets/the-two-towers.png
30
+ - examples/assets/window.jpg
31
+ - examples/sinatra.rb
32
+ - lib/dimension.rb
33
+ - lib/dimension/image.rb
34
+ - lib/dimension/processors/image_magick.rb
35
+ - lib/dimension/processors/imlib2.rb
36
+ - lib/dimension/version.rb
37
+ homepage: https://github.com/tomas/dimension
38
+ licenses: []
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.6
54
+ requirements: []
55
+ rubyforge_project: dimension
56
+ rubygems_version: 2.2.0
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Native, in-process image resizing in Ruby.
60
+ test_files: []