imgry 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Imgry
2
+
3
+ A Ruby gem for fast image resizing/cropping designed for JRuby with MRI support.
4
+
5
+ Imgry provides an elegant interface and optimizations to third party libraries: Imgscalr, ImageVoodoo and MiniMagick. The library was designed to work as fast as possible under JRuby, and it does, from what we've seen it outperforms any other JRuby image resizing library, if it doesn't, please show us and let's improve it!
6
+
7
+ It's also convienient for teams that work between MRI and JRuby, working seamlessly across development/production app environments.
8
+
9
+ We've also implemented ImageMagick/GraphicsMagick's geometry for defining sizes such as "500x", "300x200!", "256x100>", etc. This works consistently across all image processors.
10
+
11
+ # Authors
12
+
13
+ * Peter Kieltyka
14
+ * Haifeng Cao
15
+
16
+ # License
17
+
18
+ MIT License - Copyright (c) 2007-2012 Nulayer Inc.
data/lib/imgry.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Imgry
2
+ extend self
3
+
4
+ def with_bytes(img_blob, format=nil)
5
+ end
6
+
7
+ def from_file(img_blob, format=nil)
8
+ end
9
+
10
+ def default_processor
11
+ :mini_magick
12
+ end
13
+
14
+ def default_processor=(default_processor)
15
+ @default_processor = default_processor
16
+ end
17
+
18
+ end
19
+
20
+ Imagery = Imgry
21
+
22
+ require 'imgry/version'
23
+ require 'imgry/geometry'
24
+ require 'imgry/processor'
@@ -0,0 +1,48 @@
1
+ module Imgry
2
+ module Geometry
3
+ extend self
4
+
5
+ def scale(orig_width, orig_height, geometry)
6
+ op = geometry[-1] # Expecting !, >, <, or nothing
7
+ new_width, new_height = nil, nil
8
+ ask_width, ask_height = geometry.split('x').map {|x| x.to_i }
9
+ ask_height ||= 0
10
+ aspect_ratio = orig_width.to_f / orig_height.to_f
11
+
12
+ scale = Proc.new do
13
+ if ask_width == 0 || ask_width < ask_height
14
+ new_width, new_height = scale_by_height(ask_height, aspect_ratio)
15
+ else
16
+ new_width, new_height = scale_by_width(ask_width, aspect_ratio)
17
+ end
18
+ end
19
+
20
+ case op
21
+ when '!'
22
+ new_width, new_height = ask_width.to_i, ask_height.to_i
23
+ when '>'
24
+ scale.call if orig_width > ask_width || orig_height > ask_height
25
+ when '<'
26
+ scale.call if orig_width < ask_width || orig_height < ask_height
27
+ else
28
+ scale.call
29
+ end
30
+
31
+ # In the end, we've determined we don't want to resizie (perhaps due to > or < operations)
32
+ # return [nil,nil] if new_width.nil? || new_width.nil?
33
+
34
+ # binding.pry
35
+
36
+ [new_width, new_height]
37
+ end
38
+
39
+ def scale_by_width(new_width, aspect_ratio)
40
+ [new_width.to_i, (new_width / aspect_ratio).to_i]
41
+ end
42
+
43
+ def scale_by_height(new_height, aspect_ratio)
44
+ [(new_height * aspect_ratio).to_i, new_height.to_i]
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ module Imgry
2
+ module Processor
3
+
4
+ class Adapter
5
+
6
+ def self.with_bytes(img_blob, format=nil)
7
+ new(img_blob, format)
8
+ end
9
+
10
+ def self.from_file(path, format=nil)
11
+ # TODO: .. get the format from the filepath ... unless specified..
12
+ end
13
+
14
+ def self.lib_loaded?
15
+ !!@lib_loaded
16
+ end
17
+
18
+ def initialize(img_blob, format=nil)
19
+ self.class.load_lib! if !self.class.lib_loaded?
20
+
21
+ @img_blob = img_blob
22
+ @format = format
23
+ load_image_blob!
24
+ end
25
+
26
+ def aspect_ratio
27
+ width.to_f / height.to_f
28
+ end
29
+
30
+ # TODO .. add abstract methods.. at least comments..
31
+
32
+ end
33
+
34
+ class InvalidImageError < StandardError; end
35
+
36
+ end
37
+ end
38
+
39
+ %w(
40
+ image_voodoo
41
+ img_scalr
42
+ mini_magick
43
+ ).each {|lib| require "imgry/processor/#{lib}" }
@@ -0,0 +1,90 @@
1
+ module Imgry
2
+ module Processor
3
+
4
+ class ImageVoodoo < Adapter
5
+
6
+ def self.load_lib!
7
+ return if @lib_loaded
8
+
9
+ if RUBY_ENGINE != 'jruby'
10
+ raise 'The ImageVoodoo processor is only available on JRuby.'
11
+ end
12
+
13
+ begin
14
+ require 'image_voodoo'
15
+ rescue LoadError
16
+ raise 'Cannot load image_voodoo gem.'
17
+ end
18
+
19
+ # TODO...
20
+ # turn on opengl........
21
+
22
+ # Add-ons..
23
+ ::ImageVoodoo.class_eval do
24
+ def src
25
+ @src
26
+ end
27
+ end
28
+
29
+ @lib_loaded = true
30
+ end
31
+
32
+ #-----
33
+
34
+ def load_image_blob!
35
+ begin
36
+ @image_ref = ::ImageVoodoo.with_bytes(@img_blob)
37
+ if @image_ref.src.nil?
38
+ raise InvalidImageError, "Image is either corrupt or unsupported."
39
+ end
40
+ rescue => ex
41
+ raise InvalidImageError, ex.message
42
+ end
43
+ end
44
+
45
+ def resize!(geometry)
46
+ return if geometry.nil?
47
+ @image_ref = @image_ref.resize(*Geometry.scale(width, height, geometry))
48
+ end
49
+
50
+ def width
51
+ @image_ref.width
52
+ end
53
+
54
+ def height
55
+ @image_ref.height
56
+ end
57
+
58
+ def to_blob
59
+ # TODO: output format is hardcoded, we should get the format
60
+ # that it is while coming in, and use that on the way out..
61
+ # we can use jpg by default..
62
+ @image_ref.bytes('jpg')
63
+ end
64
+
65
+ def raw
66
+ @image_ref
67
+ end
68
+
69
+ def supported_write_formats
70
+ # Typical formats:
71
+ # [BMP, bmp, jpg, JPG, wbmp, jpeg, png, JPEG, PNG, WBMP, GIF, gif]
72
+ ::ImageVoodoo::ImageIO.getReaderFormatNames.to_a
73
+ end
74
+
75
+ def supported_read_formats
76
+ # Typical formats:
77
+ # [BMP, bmp, jpg, JPG, wbmp, jpeg, png, JPEG, PNG, WBMP, GIF, gif]
78
+ ::ImageVoodoo::ImageIO.getWriterFormatNames.to_a
79
+ end
80
+
81
+ def clean!
82
+ @image_ref = nil
83
+ @src_blob = nil
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+ end
90
+
@@ -0,0 +1,182 @@
1
+ module Imgry
2
+ module Processor
3
+
4
+ class ImgScalr < Adapter
5
+
6
+ def self.load_lib!
7
+ return if @lib_loaded
8
+
9
+ if RUBY_ENGINE != 'jruby'
10
+ raise 'The ImgScalr processor is only available on JRuby.'
11
+ end
12
+
13
+ self.class_eval do
14
+ include Java
15
+
16
+ require 'java/imgscalr-lib-4.2.jar'
17
+
18
+ java_import javax.imageio.ImageIO
19
+ java_import org.imgscalr.Scalr
20
+ java_import java.awt.image.BufferedImage
21
+ java_import java.io.ByteArrayInputStream
22
+ java_import java.io.ByteArrayOutputStream
23
+ end
24
+
25
+ @lib_loaded = true
26
+ end
27
+
28
+ #-----
29
+
30
+ def load_image_blob!
31
+ @img_blob = @img_blob.to_java_bytes if String === @img_blob
32
+
33
+ @image_ref = ImageIO.read(ByteArrayInputStream.new(@img_blob))
34
+ # TODO .. raise if bullshit... InvalidImageError...
35
+
36
+ end
37
+
38
+ def resize!(width, height)
39
+ options = {}
40
+ method = options[:method] ? options[:method] : Scalr::Method::QUALITY
41
+ mode = options[:mode] ? options[:mode] : Scalr::Mode::FIT_EXACT
42
+ ops = options[:ops] ? options[:ops] : Scalr::OP_ANTIALIAS
43
+
44
+ @image_ref = Scalr.resize(@image_ref, method, mode, width, height, ops)
45
+ end
46
+
47
+ def width
48
+ @image_ref.width
49
+ end
50
+
51
+ def height
52
+ @image_ref.height
53
+ end
54
+
55
+ def to_blob(format=nil)
56
+ format ||= 'jpg'
57
+
58
+ out = ByteArrayOutputStream.new
59
+ ImageIO.write(@image_ref, format, out)
60
+ String.from_java_bytes(out.to_byte_array)
61
+ end
62
+
63
+ def clean!
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ __END__
72
+ class ImgScalrVoodoo
73
+ include Java
74
+
75
+ require 'java/imgscalr-lib-4.2.jar'
76
+
77
+ java_import javax.imageio.ImageIO
78
+ java_import org.imgscalr.Scalr
79
+ java_import java.awt.image.BufferedImage
80
+
81
+ JFile = java.io.File
82
+
83
+ def initialize(src)
84
+ @src = src
85
+ end
86
+
87
+ def self.with_image(path)
88
+ raise ArgumentError, "file does not exist" unless File.file?(path)
89
+ image = guard do
90
+ buffered_image = ImageIO.read(JFile.new(path))
91
+ buffered_image ? ImgScalrVoodoo.new(buffered_image) : nil
92
+ end
93
+
94
+ image && block_given? ? yield(image) : image
95
+ end
96
+
97
+
98
+ def self.with_bytes(bytes)
99
+ bytes = bytes.to_java_bytes if String === bytes
100
+ image = guard do
101
+ ImgScalrVoodoo.new(ImageIO.read(ByteArrayInputStream.new(bytes)))
102
+ end
103
+
104
+ block_given? ? yield(image) : image
105
+ end
106
+
107
+ def img_scalr_resize(width, height, options={})
108
+ method = options[:method]? options[:method] : Scalr::Method::QUALITY
109
+ mode = options[:mode]? options[:mode] : Scalr::Mode::FIT_EXACT
110
+ ops = options[:ops]? options[:ops] : Scalr::OP_ANTIALIAS
111
+
112
+ target = guard { Scalr.resize(@src, method, mode, width, height, ops) }
113
+
114
+ image = ImgScalrVoodoo.new(target)
115
+
116
+ block_given? ? yield(image) : image
117
+ rescue NativeException => ne
118
+ raise ArgumentError, ne.message
119
+ end
120
+
121
+ def resize(width, height)
122
+ image = img_scalr_resize(width, height)
123
+ block_given? ? yield(image) : image
124
+ end
125
+
126
+ def cropped_thumbnail(size)
127
+ l, t, r, b, half = 0, 0, width, height, (width - height).abs / 2
128
+ l, r = half, half + height if width > height
129
+ t, b = half, half + width if height > width
130
+
131
+ target = with_crop(l, t, r, b).thumbnail(size)
132
+ block_given? ? yield(target) : target
133
+ end
134
+
135
+ def save(file)
136
+ format = File.extname(file)
137
+ return false if format == ""
138
+ format = format[1..-1].downcase
139
+ guard { ImageIO.write(@src, format, JFile.new(file)) }
140
+ true
141
+ end
142
+
143
+ def scale(ratio)
144
+ new_width, new_height = (width * ratio).to_i, (height * ratio).to_i
145
+ target = resize(new_width, new_height)
146
+ block_given? ? yield(target) : target
147
+ end
148
+
149
+ def thumbnail(size)
150
+ target = scale(size.to_f / (width > height ? width : height))
151
+ block_given? ? yield(target) : target
152
+ end
153
+
154
+ def with_crop(left, top, right, bottom)
155
+ image = guard { ImgScalrVoodoo.new(Scalr.crop(@src, left, top, right-left, bottom-top)) }
156
+ block_given? ? yield(image) : image
157
+ end
158
+
159
+ def self.guard(&block)
160
+ begin
161
+ return block.call
162
+ rescue NoMethodError => e
163
+ "Unimplemented Feature: #{e}"
164
+ end
165
+ end
166
+
167
+ def guard(&block)
168
+ ImgScalrVoodoo.guard(&block)
169
+ end
170
+
171
+ def height
172
+ @src.height
173
+ end
174
+
175
+ def width
176
+ @src.width
177
+ end
178
+
179
+ def to_java
180
+ @src
181
+ end
182
+ end
@@ -0,0 +1,57 @@
1
+ module Imgry
2
+ module Processor
3
+
4
+ class MiniMagick < Adapter
5
+
6
+ def self.load_lib!
7
+ return if @lib_loaded
8
+
9
+ begin
10
+ require 'mini_magick'
11
+ rescue LoadError
12
+ raise "Cannot load mini_magick gem"
13
+ end
14
+
15
+ ::MiniMagick.processor = :gm
16
+ ::MiniMagick.timeout = 45
17
+
18
+ @lib_loaded = true
19
+ end
20
+
21
+ #-----
22
+
23
+ def load_image_blob!
24
+ begin
25
+ @image_ref = ::MiniMagick::Image.read(@img_blob)
26
+ rescue ::MiniMagick::Invalid => ex
27
+ raise InvalidImageError, ex.message
28
+ end
29
+ end
30
+
31
+ def resize!(geometry)
32
+ @image_ref.resize(geometry) if !geometry.nil?
33
+ nil
34
+ end
35
+
36
+ def width
37
+ @image_ref['width']
38
+ end
39
+
40
+ def height
41
+ @image_ref['height']
42
+ end
43
+
44
+ def to_blob
45
+ @image_ref.to_blob
46
+ end
47
+
48
+ def clean!
49
+ @image_ref.destroy!
50
+ @image_ref = nil
51
+ @img_blob = nil
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,3 @@
1
+ module Imgry
2
+ VERSION = '0.1.0'
3
+ end
Binary file
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Imgry::Geometry do
4
+
5
+ it 'scales to a strict size' do
6
+ size = Imgry::Geometry.scale(300, 500, "70x70!")
7
+ size.should == [70, 70]
8
+ end
9
+
10
+ it 'scales to a size with aspect ratio maintained' do
11
+ size = Imgry::Geometry.scale(1024, 768, "600x")
12
+ size.should == [600, 450]
13
+ end
14
+
15
+ it 'scales to a size with aspect ratio maintained' do
16
+ size = Imgry::Geometry.scale(1024, 768, "x300")
17
+ size.should == [400, 300]
18
+ end
19
+
20
+ it 'shrinks to a size when dimensions are larger then corresponding width/height' do
21
+ size = Imgry::Geometry.scale(1024, 768, "600x600>")
22
+
23
+ binding.pry
24
+
25
+ # size.should == [400, 300]
26
+ end
27
+
28
+ # it 'enlarges to a size when dimensions are smaller then corresponding width/height' do
29
+ # geo = "600x600<"
30
+ # # TODO..
31
+ # end
32
+
33
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Imgry::Processor::ImageVoodoo do
4
+
5
+ let (:img_data) { IO.read(SPEC_ROOT.join('support/335is.jpg')) }
6
+
7
+ context "a pretty picture" do
8
+
9
+ it "basic loading and resizing of an image" do
10
+ img = Imgry::Processor::ImageVoodoo.with_bytes(img_data)
11
+
12
+ img.width.should == 1024
13
+ img.height.should == 764
14
+
15
+ img.resize!("512x")
16
+ img.width.should == 512
17
+ img.height.should == 382
18
+
19
+ new_img_blob = img.to_blob
20
+ new_img_blob.length.should < img_data.length
21
+ new_img_blob.length.should == 32159
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Imgry::Processor::ImgScalr do
4
+
5
+ let (:img_data) { IO.read(SPEC_ROOT.join('support/335is.jpg')) }
6
+
7
+ context "a pretty picture" do
8
+
9
+ it "basic loading and resizing of an image" do
10
+ img = Imgry::Processor::ImgScalr.with_bytes(img_data)
11
+
12
+ img.width.should == 1024
13
+ img.height.should == 764
14
+
15
+ # img.resize!("512x")
16
+ img.resize!(512, 382)
17
+ img.width.should == 512
18
+ img.height.should == 382
19
+
20
+ new_img_blob = img.to_blob
21
+ new_img_blob.length.should < img_data.length
22
+ new_img_blob.length.should == 30319
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Imgry::Processor::MiniMagick do
4
+
5
+ let (:img_data) { IO.read(SPEC_ROOT.join('support/335is.jpg')) }
6
+
7
+ context "a pretty picture" do
8
+
9
+ it "basic loading and resizing of an image" do
10
+ img = Imgry::Processor::MiniMagick.with_bytes(img_data)
11
+
12
+ img.width.should == 1024
13
+ img.height.should == 764
14
+
15
+ img.resize!("512x")
16
+ img.width.should == 512
17
+ img.height.should == 382
18
+
19
+ new_img_blob = img.to_blob
20
+ new_img_blob.length.should < img_data.length
21
+ new_img_blob.length.should == 32671
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Imgry::Processor do
4
+
5
+ let (:img_data) { IO.read(SPEC_ROOT.join('support/335is.jpg')) }
6
+
7
+ context "a pretty picture" do
8
+
9
+ it "should do stuff..." do
10
+ # This will use the best processor for the VM .. etc.
11
+ # .. what about the format...?
12
+ img = Imgry.with_bytes(img_data)
13
+
14
+ # img.class # .. this will be ImageTools::Processor::MiniMagick or ::ImgScalr etc.
15
+
16
+ # img.resize!("300x200!")
17
+
18
+ # img = ImageTools.from_file(SPEC_ROOT.join('support/335is.jpg'))
19
+
20
+ binding.pry
21
+ x = 1
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'bundler'
3
+ Bundler.setup(:default, :development)
4
+ rescue LoadError => e
5
+ # Fall back on doing an unlocked resolve at runtime.
6
+ $stderr.puts e.message
7
+ $stderr.puts "Try running `bundle install`"
8
+ exit!
9
+ end
10
+
11
+ require 'pathname'
12
+
13
+ $:.unshift(File.dirname(__FILE__))
14
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ IMGRY_ROOT = Pathname.new(File.expand_path('../../', __FILE__))
16
+ SPEC_ROOT = IMGRY_ROOT.join('spec')
17
+
18
+ begin
19
+ require 'pry'
20
+ require 'pry-nav'
21
+ rescue LoadError
22
+ end
23
+
24
+ require 'imgry'
25
+ require 'rspec'
26
+
27
+ # Requires supporting files with custom matchers and macros, etc,
28
+ # in ./support/ and its subdirectories.
29
+ # Dir["#{SPEC_ROOT}/support/**/*.rb"].each {|f| require f}
30
+
31
+ RSpec.configure do |config|
32
+ config.mock_with :rspec
33
+ end
Binary file
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: imgry
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pressly
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.12'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.12'
30
+ description: Fast image resizing/cropping designed for JRuby with MRI support
31
+ email:
32
+ - info@pressly.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/imgry/geometry.rb
38
+ - lib/imgry/processor/image_voodoo.rb
39
+ - lib/imgry/processor/img_scalr.rb
40
+ - lib/imgry/processor/mini_magick.rb
41
+ - lib/imgry/processor.rb
42
+ - lib/imgry/version.rb
43
+ - lib/imgry.rb
44
+ - lib/java/imgscalr-lib-4.2.jar
45
+ - README.md
46
+ - spec/imgry/geometry_spec.rb
47
+ - spec/imgry/processor/image_voodoo_spec.rb
48
+ - spec/imgry/processor/img_scalr_spec.rb
49
+ - spec/imgry/processor/mini_magick_spec.rb
50
+ - spec/imgry/processor_spec.rb
51
+ - spec/spec_helper.rb
52
+ - spec/support/335is.gif
53
+ - spec/support/335is.jpg
54
+ - spec/support/335is.png
55
+ homepage: http://github.com/nulayer/imgry
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.6
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.24
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Fast image resizing/cropping designed for JRuby with MRI support
79
+ test_files:
80
+ - spec/imgry/geometry_spec.rb
81
+ - spec/imgry/processor/image_voodoo_spec.rb
82
+ - spec/imgry/processor/img_scalr_spec.rb
83
+ - spec/imgry/processor/mini_magick_spec.rb
84
+ - spec/imgry/processor_spec.rb
85
+ - spec/spec_helper.rb
86
+ - spec/support/335is.gif
87
+ - spec/support/335is.jpg
88
+ - spec/support/335is.png
89
+ has_rdoc: