smartimage 0.0.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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.textile +11 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/smart_image.rb +137 -0
- data/lib/smart_image/base_canvas.rb +46 -0
- data/lib/smart_image/java_canvas.rb +74 -0
- data/lib/smart_image/ratio_calculator.rb +42 -0
- data/lib/smart_image/rmagick_canvas.rb +57 -0
- data/smartimage.gemspec +71 -0
- data/spec/fixtures/mask.png +0 -0
- data/spec/fixtures/mongoose.jpg +0 -0
- data/spec/smart_image_spec.rb +44 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +5 -0
- metadata +103 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Tony Arcieri
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
h1. SmartImage
|
2
|
+
|
3
|
+
Hi. There will be a real README here soon, I promise.
|
4
|
+
|
5
|
+
h2. Credits
|
6
|
+
|
7
|
+
SmartImage assumes your Ruby interpreter supports the absurdly powerful RMagick
|
8
|
+
library, unless you're running JRuby, in which case it uses the absurdly
|
9
|
+
powerful Java Graphics2D library and AWT.
|
10
|
+
|
11
|
+
Mongoose courtesy Wikimedia Commons: http://en.wikipedia.org/wiki/File:Mongoose.jpg
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "smartimage"
|
8
|
+
gem.summary = %Q{It's like a Swiss Army Knife for images, but one of those tiny ones you can keep on your keychain}
|
9
|
+
gem.description = <<-EOD
|
10
|
+
SmartImage provides a cross-platform solution for image compositing that works on both MRI and JRuby.
|
11
|
+
If using RMagick feels like swatting a fly with a nucler missile, and ImageScience just doesn't get
|
12
|
+
you there, SmartImage is hopefully at that sweet spot in the middle
|
13
|
+
EOD
|
14
|
+
|
15
|
+
gem.email = "tony@medioh.com"
|
16
|
+
gem.homepage = "http://github.com/tarcieri/smartimage"
|
17
|
+
gem.authors = ["Tony Arcieri"]
|
18
|
+
gem.add_dependency "imagesize", ">= 0.1.1"
|
19
|
+
gem.add_dependency "rmagick", ">= 2.12.2"
|
20
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
35
|
+
spec.libs << 'lib' << 'spec'
|
36
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
37
|
+
spec.rcov = true
|
38
|
+
end
|
39
|
+
|
40
|
+
task :spec => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "smartimage #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/lib/smart_image.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'image_size'
|
2
|
+
require 'smart_image/ratio_calculator'
|
3
|
+
|
4
|
+
# Load the appropriate canvas class for the current environment
|
5
|
+
if defined? JRUBY_VERSION
|
6
|
+
require 'smart_image/java_canvas'
|
7
|
+
else
|
8
|
+
require 'smart_image/rmagick_canvas'
|
9
|
+
end
|
10
|
+
|
11
|
+
# SmartImage: it's like a Swiss Army Knife for images, but one of those tiny
|
12
|
+
# ones you can keep on your keychain.
|
13
|
+
class SmartImage
|
14
|
+
# I'm sorry, I couldn't understand the data you gave me
|
15
|
+
class FormatError < ArgumentError; end
|
16
|
+
|
17
|
+
# Struct type containing information about a given image
|
18
|
+
class Info < Struct.new(:width, :height, :type); end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Obtain basic information about the given image data
|
22
|
+
# Returns a SmartImage::Info object
|
23
|
+
def info(data)
|
24
|
+
img = ImageSize.new data
|
25
|
+
raise FormatError, "invalid image" if img.get_type == "OTHER"
|
26
|
+
|
27
|
+
Info.new(img.width, img.height, img.get_type.downcase.to_sym)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Obtain information about a file
|
31
|
+
# Returns a SmartImage::Info object
|
32
|
+
def file_info(path)
|
33
|
+
info File.read(path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create a new SmartImage of the given width and height. Always takes a
|
38
|
+
# block... no exceptions! Returns a destroyed SmartImage object.
|
39
|
+
#
|
40
|
+
# SmartImage.new(400, 300) do |compositor|
|
41
|
+
# compositor.image "foo/bar.jpg", :x => 10, :y => 10
|
42
|
+
# compositor.text "Hello, world!", :x => 20, :y => 20
|
43
|
+
# compositor.write "baz/qux.jpg"
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# When used with a block, all images are automatically freed from memory
|
47
|
+
def initialize(width, height, &block)
|
48
|
+
raise ArgumentError, "give me a block, pretty please" unless block_given?
|
49
|
+
|
50
|
+
@width, @height = Integer(width), Integer(height)
|
51
|
+
@canvas = SmartImage::Canvas.new @width, @height
|
52
|
+
|
53
|
+
yield self
|
54
|
+
@canvas.destroy
|
55
|
+
@canvas = DeadCanvas.new
|
56
|
+
end
|
57
|
+
|
58
|
+
class DeadCanvas
|
59
|
+
def method_missing(*args)
|
60
|
+
raise ArgumentError, "your image exists only within the SmartImage.new block"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Composite the given image data onto the SmartImage
|
65
|
+
#
|
66
|
+
# Accepts the following options:
|
67
|
+
#
|
68
|
+
# * x: coordinate of the upper left corner of the image (default 0)
|
69
|
+
# * y: ditto, it's the y coordinate
|
70
|
+
# * width: an alternate width
|
71
|
+
# * height: alternate height
|
72
|
+
# * preserve_aspect_ratio: should the aspect ratio be preserved? (default: true)
|
73
|
+
def composite(data, options = {})
|
74
|
+
info = self.class.info data
|
75
|
+
|
76
|
+
opts = {
|
77
|
+
:x => 0,
|
78
|
+
:y => 0,
|
79
|
+
:width => info.width,
|
80
|
+
:height => info.height,
|
81
|
+
:preserve_aspect_ratio => true
|
82
|
+
}.merge(options)
|
83
|
+
|
84
|
+
if opts[:preserve_aspect_ratio]
|
85
|
+
composited_size = SmartImage::RatioCalculator.new(
|
86
|
+
:source_width => info.width,
|
87
|
+
:source_height => info.height,
|
88
|
+
:dest_width => Integer(opts[:width]),
|
89
|
+
:dest_height => Integer(opts[:height])
|
90
|
+
).size
|
91
|
+
|
92
|
+
dest_width, dest_height = composited_size.width, composited_size.height
|
93
|
+
else
|
94
|
+
dest_width, dest_height = opts[:width], opts[:height]
|
95
|
+
end
|
96
|
+
|
97
|
+
@canvas.composite data, :width => Integer(dest_width),
|
98
|
+
:height => Integer(dest_height),
|
99
|
+
:x => opts[:x],
|
100
|
+
:y => opts[:y]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Composite a given image file onto the SmartImage. Accepts the same options
|
104
|
+
# as the composite method
|
105
|
+
def composite_file(file, options = {})
|
106
|
+
composite File.read(file), options
|
107
|
+
end
|
108
|
+
|
109
|
+
# Apply an alpha mask from the given image data. Doesn't accept any options
|
110
|
+
# right now, sorry. It's just another useless dangling options hash.
|
111
|
+
def alpha_mask(data, options = {})
|
112
|
+
@canvas.alpha_mask data
|
113
|
+
end
|
114
|
+
|
115
|
+
# Apply an alpha mask from the given file. Accepts the same options as the
|
116
|
+
# alpha_mask method.
|
117
|
+
def alpha_mask_file(file, options = {})
|
118
|
+
alpha_mask File.read(file), options
|
119
|
+
end
|
120
|
+
|
121
|
+
# Encode the image with the given format (a file extension) and return it
|
122
|
+
# as a string. Doesn't accept any options at present. The options hash is
|
123
|
+
# just there to annoy you and make you wish it had more options.
|
124
|
+
def encode(format, options = {})
|
125
|
+
# Sorry .jpeg lovers, I'm one of you too but the standard is jpg
|
126
|
+
format = :jpg if format.to_s == 'jpeg'
|
127
|
+
|
128
|
+
@canvas.encode format, options
|
129
|
+
end
|
130
|
+
|
131
|
+
# Write the resulting image out to disk. Picks format based on filename.
|
132
|
+
# Takes the same options as encode
|
133
|
+
def write(path, options = {})
|
134
|
+
format = File.extname(path).sub(/^\./, '')
|
135
|
+
File.open(path, 'w') { |file| file << encode(format, options) }
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class SmartImage
|
2
|
+
# Exception thrown for unimplemented features
|
3
|
+
class NotImplementedError < StandardError; end
|
4
|
+
|
5
|
+
# This class defines the set of methods all canvases are expected to implement
|
6
|
+
# It also documents the set of methods that should be available for a canvas
|
7
|
+
class BaseCanvas
|
8
|
+
# Create a new canvas object of the given width and height
|
9
|
+
def initialize(width, height)
|
10
|
+
raise NotImplementedError, "some silly person forgot to define a constructor"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Destroy the canvas (if you need to)
|
14
|
+
def destroy
|
15
|
+
not_implemented :destroy
|
16
|
+
end
|
17
|
+
|
18
|
+
# Has the canvas been destroyed already?
|
19
|
+
def destroyed?
|
20
|
+
not_implemented :destroyed?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Composite another image onto this canvas
|
24
|
+
def composite(image_data, width, height, options = {})
|
25
|
+
not_implemented :composite
|
26
|
+
end
|
27
|
+
|
28
|
+
# Load the given file as an alpha mask for the image
|
29
|
+
def alpha_mask(image_data, options = {})
|
30
|
+
not_implemented :alpha_mask
|
31
|
+
end
|
32
|
+
|
33
|
+
# Encode the image to the given format
|
34
|
+
def encode(format, options = {})
|
35
|
+
not_implemented :encode
|
36
|
+
end
|
37
|
+
|
38
|
+
#######
|
39
|
+
private
|
40
|
+
#######
|
41
|
+
|
42
|
+
def not_implemented(meth)
|
43
|
+
raise NotImplementedError, "#{meth} not implemented by #{self.class.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'smart_image/base_canvas'
|
2
|
+
|
3
|
+
class SmartImage
|
4
|
+
# Canvas object backed by Java Graphics2D
|
5
|
+
class JavaCanvas < BaseCanvas
|
6
|
+
java_import java.awt.image.BufferedImage
|
7
|
+
java_import javax.imageio.ImageIO
|
8
|
+
java_import java.io.ByteArrayInputStream
|
9
|
+
java_import java.io.ByteArrayOutputStream
|
10
|
+
|
11
|
+
def initialize(width, height)
|
12
|
+
@canvas = BufferedImage.new width, height, BufferedImage::TYPE_INT_ARGB
|
13
|
+
end
|
14
|
+
|
15
|
+
# Stub out destroy since Java actually garbage collects crap, unlike... C
|
16
|
+
def destroy
|
17
|
+
end
|
18
|
+
|
19
|
+
# Composite the given image data onto the canvas
|
20
|
+
def composite(image_data, options = {})
|
21
|
+
info = SmartImage.info image_data
|
22
|
+
opts = {
|
23
|
+
:width => info.width,
|
24
|
+
:height => info.height,
|
25
|
+
:x => 0,
|
26
|
+
:y => 0
|
27
|
+
}.merge(options)
|
28
|
+
|
29
|
+
input_stream = ByteArrayInputStream.new image_data.to_java_bytes
|
30
|
+
image = ImageIO.read input_stream
|
31
|
+
raise FormatError, "invalid image" unless image
|
32
|
+
|
33
|
+
graphics = @canvas.graphics
|
34
|
+
graphics.draw_image image, opts[:x], opts[:y], opts[:width], opts[:height], nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load the given file as an alpha mask for the image
|
38
|
+
def alpha_mask(image_data, options = {})
|
39
|
+
input_stream = ByteArrayInputStream.new image_data.to_java_bytes
|
40
|
+
mask = ImageIO.read input_stream
|
41
|
+
|
42
|
+
width = mask.width
|
43
|
+
image_data, mask_data = Java::int[width].new, Java::int[width].new
|
44
|
+
|
45
|
+
mask.height.times do |y|
|
46
|
+
# fetch a line of data from each image
|
47
|
+
@canvas.get_rgb 0, y, width, 1, image_data, 0, 1
|
48
|
+
mask.get_rgb 0, y, width, 1, mask_data, 0, 1
|
49
|
+
|
50
|
+
width.times do |x|
|
51
|
+
# mask away the alpha
|
52
|
+
color = image_data[x] & 0x00FFFFFF
|
53
|
+
|
54
|
+
# turn red from the mask into alpha
|
55
|
+
alpha = (mask_data[x] & 0x00FF0000) << 8
|
56
|
+
|
57
|
+
image_data[x] = color | alpha
|
58
|
+
end
|
59
|
+
|
60
|
+
@canvas.set_rgb 0, y, width, 1, image_data, 0, 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Encode the image to the given format
|
65
|
+
def encode(format, options = {})
|
66
|
+
output_stream = ByteArrayOutputStream.new
|
67
|
+
ImageIO.write(@canvas, format.to_s, output_stream)
|
68
|
+
String.from_java_bytes output_stream.to_byte_array
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Java is our Canvas on Java, duh!
|
73
|
+
Canvas = JavaCanvas
|
74
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class SmartImage
|
2
|
+
class RatioCalculator
|
3
|
+
# Create a new RatioCalculator object with the given options
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = options
|
6
|
+
end
|
7
|
+
|
8
|
+
# Calculate the resulting size given a particular set of contraints
|
9
|
+
def size(options = {})
|
10
|
+
opts = @options.merge(options)
|
11
|
+
|
12
|
+
source = Size.new opts[:source_width], opts[:source_height]
|
13
|
+
bounds = Size.new opts[:dest_width], opts[:dest_height]
|
14
|
+
|
15
|
+
# Calculate what the width would be if we matched the dest height
|
16
|
+
naive_width = bounds.height * source.aspect_ratio
|
17
|
+
|
18
|
+
# If it fits, use it!
|
19
|
+
if naive_width <= bounds.width
|
20
|
+
width = naive_width
|
21
|
+
height = naive_width / source.aspect_ratio
|
22
|
+
# Otherwise, the height must fit
|
23
|
+
else
|
24
|
+
height = bounds.width / source.aspect_ratio
|
25
|
+
width = height * source.aspect_ratio
|
26
|
+
end
|
27
|
+
|
28
|
+
return Size.new(width, height)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Struct to hold the resulting size and compute aspect ratios
|
33
|
+
#
|
34
|
+
class Size < Struct.new(:width, :height, :aspect_ratio)
|
35
|
+
def initialize(width, height)
|
36
|
+
width, height = Integer(width), Integer(height)
|
37
|
+
aspect_ratio = width.to_f / height
|
38
|
+
super(width, height, aspect_ratio)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
require 'smart_image/base_canvas'
|
3
|
+
|
4
|
+
class SmartImage
|
5
|
+
# Canvas object, backed by RMagick
|
6
|
+
class RMagickCanvas < BaseCanvas
|
7
|
+
include Magick
|
8
|
+
|
9
|
+
def initialize(width, height)
|
10
|
+
@canvas = Image.new width, height do
|
11
|
+
self.background_color = "transparent"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def destroy
|
16
|
+
@canvas.destroy!
|
17
|
+
end
|
18
|
+
|
19
|
+
def composite(image_data, options = {})
|
20
|
+
image = ImageList.new
|
21
|
+
image.from_blob image_data
|
22
|
+
|
23
|
+
opts = {
|
24
|
+
:width => image.columns,
|
25
|
+
:height => image.rows,
|
26
|
+
:x => 0,
|
27
|
+
:y => 0
|
28
|
+
}.merge(options)
|
29
|
+
|
30
|
+
image.thumbnail! opts[:width], opts[:height]
|
31
|
+
@canvas.composite! image, opts[:x], opts[:y], OverCompositeOp
|
32
|
+
ensure
|
33
|
+
image.destroy!
|
34
|
+
end
|
35
|
+
|
36
|
+
# Load the given file as an alpha mask for the image
|
37
|
+
def alpha_mask(image_data, options = {})
|
38
|
+
mask = ImageList.new
|
39
|
+
mask.from_blob image_data
|
40
|
+
|
41
|
+
# Disable this image's alpha channel to use the opacity data as a mask
|
42
|
+
mask.matte = false
|
43
|
+
@canvas.composite! mask, NorthWestGravity, CopyOpacityCompositeOp
|
44
|
+
ensure
|
45
|
+
mask.destroy!
|
46
|
+
end
|
47
|
+
|
48
|
+
# Encode this image into the given format (as a file extension)
|
49
|
+
def encode(format, options = {})
|
50
|
+
@canvas.format = format.to_s.upcase
|
51
|
+
@canvas.to_blob
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# RMagick is our Canvas on everything besides JRuby. Hope it works for you!
|
56
|
+
Canvas = RMagickCanvas
|
57
|
+
end
|
data/smartimage.gemspec
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{smartimage}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tony Arcieri"]
|
12
|
+
s.date = %q{2010-03-26}
|
13
|
+
s.description = %q{ SmartImage provides a cross-platform solution for image compositing that works on both MRI and JRuby.
|
14
|
+
If using RMagick feels like swatting a fly with a nucler missile, and ImageScience just doesn't get
|
15
|
+
you there, SmartImage is hopefully at that sweet spot in the middle
|
16
|
+
}
|
17
|
+
s.email = %q{tony@medioh.com}
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE",
|
20
|
+
"README.textile"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".document",
|
24
|
+
".gitignore",
|
25
|
+
"LICENSE",
|
26
|
+
"README.textile",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"lib/smart_image.rb",
|
30
|
+
"lib/smart_image/base_canvas.rb",
|
31
|
+
"lib/smart_image/java_canvas.rb",
|
32
|
+
"lib/smart_image/ratio_calculator.rb",
|
33
|
+
"lib/smart_image/rmagick_canvas.rb",
|
34
|
+
"smartimage.gemspec",
|
35
|
+
"spec/fixtures/mask.png",
|
36
|
+
"spec/fixtures/mongoose.jpg",
|
37
|
+
"spec/smart_image_spec.rb",
|
38
|
+
"spec/spec.opts",
|
39
|
+
"spec/spec_helper.rb",
|
40
|
+
"spec/tmp/.gitignore"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/tarcieri/smartimage}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{It's like a Swiss Army Knife for images, but one of those tiny ones you can keep on your keychain}
|
47
|
+
s.test_files = [
|
48
|
+
"spec/smart_image_spec.rb",
|
49
|
+
"spec/spec_helper.rb"
|
50
|
+
]
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_runtime_dependency(%q<imagesize>, [">= 0.1.1"])
|
58
|
+
s.add_runtime_dependency(%q<rmagick>, [">= 2.12.2"])
|
59
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<imagesize>, [">= 0.1.1"])
|
62
|
+
s.add_dependency(%q<rmagick>, [">= 2.12.2"])
|
63
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<imagesize>, [">= 0.1.1"])
|
67
|
+
s.add_dependency(%q<rmagick>, [">= 2.12.2"])
|
68
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,44 @@
|
|
1
|
+
SPEC_DIR = File.dirname(__FILE__)
|
2
|
+
require SPEC_DIR + '/spec_helper'
|
3
|
+
|
4
|
+
describe SmartImage do
|
5
|
+
before :all do
|
6
|
+
@mongoose = SPEC_DIR + '/fixtures/mongoose.jpg'
|
7
|
+
@mask = SPEC_DIR + '/fixtures/mask.png'
|
8
|
+
@output_dir = SPEC_DIR + '/tmp/'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "obtains image information" do
|
12
|
+
info = SmartImage.file_info(@mongoose)
|
13
|
+
|
14
|
+
info.type.should == :jpeg
|
15
|
+
info.width.should == 1327
|
16
|
+
info.height.should == 1260
|
17
|
+
end
|
18
|
+
|
19
|
+
it "composites images" do
|
20
|
+
SmartImage.new(800, 400) do |image|
|
21
|
+
image.composite_file @mongoose, :y => 15
|
22
|
+
image.write @output_dir + 'composited.png'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "scales when compositing" do
|
27
|
+
SmartImage.new(800, 400) do |image|
|
28
|
+
image.composite_file @mongoose, :y => 15
|
29
|
+
image.composite_file @mongoose, :x => 100, :y => 30, :width => 250, :height => 100
|
30
|
+
image.write @output_dir + 'scaled.png'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "alpha masks images" do
|
35
|
+
SmartImage.new(115, 95) do |image|
|
36
|
+
image.composite_file @mongoose, :width => 115,
|
37
|
+
:height => 95,
|
38
|
+
:preserve_aspect_ratio => false
|
39
|
+
|
40
|
+
image.alpha_mask_file @mask
|
41
|
+
image.write @output_dir + 'alpha_mask.png'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smartimage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tony Arcieri
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-26 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: imagesize
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rmagick
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.12.2
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.2.9
|
44
|
+
version:
|
45
|
+
description: " SmartImage provides a cross-platform solution for image compositing that works on both MRI and JRuby.\n If using RMagick feels like swatting a fly with a nucler missile, and ImageScience just doesn't get \n you there, SmartImage is hopefully at that sweet spot in the middle\n"
|
46
|
+
email: tony@medioh.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- LICENSE
|
53
|
+
- README.textile
|
54
|
+
files:
|
55
|
+
- .document
|
56
|
+
- .gitignore
|
57
|
+
- LICENSE
|
58
|
+
- README.textile
|
59
|
+
- Rakefile
|
60
|
+
- VERSION
|
61
|
+
- lib/smart_image.rb
|
62
|
+
- lib/smart_image/base_canvas.rb
|
63
|
+
- lib/smart_image/java_canvas.rb
|
64
|
+
- lib/smart_image/ratio_calculator.rb
|
65
|
+
- lib/smart_image/rmagick_canvas.rb
|
66
|
+
- smartimage.gemspec
|
67
|
+
- spec/fixtures/mask.png
|
68
|
+
- spec/fixtures/mongoose.jpg
|
69
|
+
- spec/smart_image_spec.rb
|
70
|
+
- spec/spec.opts
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
- spec/tmp/.gitignore
|
73
|
+
has_rdoc: true
|
74
|
+
homepage: http://github.com/tarcieri/smartimage
|
75
|
+
licenses: []
|
76
|
+
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --charset=UTF-8
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
version:
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
version:
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.3.5
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: It's like a Swiss Army Knife for images, but one of those tiny ones you can keep on your keychain
|
101
|
+
test_files:
|
102
|
+
- spec/smart_image_spec.rb
|
103
|
+
- spec/spec_helper.rb
|