dimension 0.0.1

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 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: []