airbrush 0.0.2 → 0.0.3
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/bin/airbrush +0 -0
- data/bin/airbrush-example-client +25 -5
- data/config/hoe.rb +5 -5
- data/lib/airbrush/processors/image/rmagick.rb +28 -21
- data/lib/airbrush/version.rb +1 -1
- data/spec/airbrush/processors/image/rmagick_spec.rb +52 -4
- metadata +76 -39
data/bin/airbrush
CHANGED
File without changes
|
data/bin/airbrush-example-client
CHANGED
@@ -51,10 +51,30 @@ raise "Source image does not exist: #{OPTIONS[:image]}" unless File.exists?(OPTI
|
|
51
51
|
require 'airbrush'
|
52
52
|
|
53
53
|
client = Airbrush::Client.new(OPTIONS[:memcache])
|
54
|
-
puts "Sending #{OPTIONS[:image]} for
|
54
|
+
puts "Sending #{OPTIONS[:image]} for processing"
|
55
|
+
|
56
|
+
# previews..
|
55
57
|
results = client.process('generate-previews', :previews, :image => File.read(OPTIONS[:image]), :filename => OPTIONS[:image], :sizes => {:small => [300], :large => [600]})
|
58
|
+
|
56
59
|
results.each do |k,v|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
next if k == :original
|
61
|
+
puts v[:format].inspect
|
62
|
+
File.open("#{OPTIONS[:output]}-#{k}.jpg", 'w') { |f| f << v[:image] }
|
63
|
+
puts "Saved preview #{OPTIONS[:output]}-#{k}.jpg (#{v[:width]}x#{v[:height]})" if OPTIONS[:verbose]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Exif data..
|
67
|
+
results = client.process('exif',:exif_data, :image => File.read(OPTIONS[:image]))
|
68
|
+
puts results.inspect
|
69
|
+
|
70
|
+
# resized_crop_mask..
|
71
|
+
results = client.process('crop-and-resize',
|
72
|
+
:resized_crop_mask,
|
73
|
+
:image => File.read(OPTIONS[:image]),
|
74
|
+
:filename => OPTIONS[:image],
|
75
|
+
:crop => {:x => 0, :y => 0, :width => 800, :height => 800 },
|
76
|
+
:size => { :width => 2890, :height => 1940 })
|
77
|
+
|
78
|
+
File.open("#{OPTIONS[:output]}.jpg", 'w'){|f| f << results[:image] }
|
79
|
+
puts "Saved resized image #{OPTIONS[:output]}.jpg" if OPTIONS[:verbose]
|
80
|
+
|
data/config/hoe.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'airbrush/version'
|
2
2
|
|
3
|
-
AUTHOR = '
|
4
|
-
EMAIL = "
|
5
|
-
DESCRIPTION = "
|
3
|
+
AUTHOR = ['Marcus Crafter', 'Nick Marfleet'] # can also be an array of Authors
|
4
|
+
EMAIL = "nick@whatevernext.org"
|
5
|
+
DESCRIPTION = "Distributed image processing server."
|
6
6
|
GEM_NAME = 'airbrush' # what ppl will type to install your gem
|
7
7
|
RUBYFORGE_PROJECT = 'airbrush' # The unix name for your project
|
8
8
|
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
@@ -10,7 +10,7 @@ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
|
10
10
|
|
11
11
|
@config_file = "~/.rubyforge/user-config.yml"
|
12
12
|
@config = nil
|
13
|
-
RUBYFORGE_USERNAME = "
|
13
|
+
RUBYFORGE_USERNAME = "nickm"
|
14
14
|
def rubyforge_username
|
15
15
|
unless @config
|
16
16
|
begin
|
@@ -59,7 +59,7 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
59
59
|
|
60
60
|
# == Optional
|
61
61
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
|
-
p.extra_deps = [ [ 'starling', '
|
62
|
+
p.extra_deps = [ [ 'starling', '= 0.9.8' ], [ 'activesupport', '= 2.3.5' ], [ 'ruby2ruby', '= 1.2.1' ], [ 'daemons', '= 1.0.10' ] ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
63
|
|
64
64
|
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
65
65
|
|
@@ -4,6 +4,9 @@ module Airbrush
|
|
4
4
|
module Processors
|
5
5
|
module Image
|
6
6
|
class Rmagick < ImageProcessor
|
7
|
+
|
8
|
+
VALID_OUTPUT_FORMATS = ['JPEG', 'PNG', 'JPG', 'GIF']
|
9
|
+
|
7
10
|
filter_params :image # ignore any argument called 'image' in any logging
|
8
11
|
|
9
12
|
def resize(image, width, height = nil)
|
@@ -33,6 +36,18 @@ module Airbrush
|
|
33
36
|
images[:original] = dimensions(image)
|
34
37
|
images
|
35
38
|
end
|
39
|
+
|
40
|
+
def exif_data(image)
|
41
|
+
data = Magick::Image.from_blob(image).first.get_exif_by_entry
|
42
|
+
data.inject({}) { |m, exif_entry| m[exif_entry[0]] = exif_entry[1]; m }
|
43
|
+
end
|
44
|
+
|
45
|
+
def resized_crop_mask(image, crop, size)
|
46
|
+
process image do
|
47
|
+
resize!(size[:width], size[:height])
|
48
|
+
crop!(crop[:x], crop[:y], crop[:width], crop[:height])
|
49
|
+
end
|
50
|
+
end
|
36
51
|
|
37
52
|
protected
|
38
53
|
|
@@ -40,11 +55,16 @@ module Airbrush
|
|
40
55
|
img = Magick::Image.from_blob(image).first
|
41
56
|
img.instance_eval &block
|
42
57
|
img.ensure_rgb!
|
43
|
-
img.format =
|
58
|
+
img.format = get_format(image) # ensure that resized output is a JPEG
|
44
59
|
{
|
45
|
-
:image => img.to_blob, :width => img.columns, :height => img.rows
|
60
|
+
:image => img.to_blob, :width => img.columns, :height => img.rows, :format => img.format
|
46
61
|
}
|
47
62
|
end
|
63
|
+
|
64
|
+
def get_format(image_data)
|
65
|
+
current_format = Magick::Image.from_blob(image_data).first.format
|
66
|
+
VALID_OUTPUT_FORMATS.include?(current_format) ? current_format : 'JPEG'
|
67
|
+
end
|
48
68
|
|
49
69
|
def dimensions(image_data)
|
50
70
|
image = Magick::Image.from_blob(image_data).first
|
@@ -53,32 +73,19 @@ module Airbrush
|
|
53
73
|
|
54
74
|
def calculate_dimensions(image_data, size)
|
55
75
|
image = Magick::Image.from_blob(image_data).first
|
56
|
-
return image.columns, image.rows
|
76
|
+
return image.columns, image.rows unless clipping_required?(image, size)
|
57
77
|
|
58
78
|
ratio = image.columns.to_f / image.rows.to_f
|
59
79
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
landscape image do
|
65
|
-
return [ ratio * size, size ]
|
66
|
-
end
|
80
|
+
return [ size, size.to_f / ratio ] if ratio > 1 # landscape
|
81
|
+
|
82
|
+
return [ ratio * size, size ] if ratio < 1 # Portrait
|
67
83
|
|
68
|
-
|
69
|
-
return [ size, size ]
|
84
|
+
return [ size, size ] # Square
|
70
85
|
end
|
71
86
|
|
72
87
|
def clipping_required?(image, size)
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
def portrait(image, &block)
|
77
|
-
block.call if image.columns > image.rows
|
78
|
-
end
|
79
|
-
|
80
|
-
def landscape(image, &block)
|
81
|
-
block.call if image.columns < image.rows
|
88
|
+
(image.columns > size) || (image.rows > size)
|
82
89
|
end
|
83
90
|
|
84
91
|
end
|
data/lib/airbrush/version.rb
CHANGED
@@ -12,8 +12,10 @@ describe Airbrush::Processors::Image::Rmagick do
|
|
12
12
|
@rm_image.stub!(:change_geometry).and_return
|
13
13
|
@rm_image.stub!(:crop!).and_return
|
14
14
|
@rm_image.stub!(:crop_resized!).and_return
|
15
|
+
@rm_image.stub!(:resize!).and_return
|
15
16
|
@rm_image.stub!(:ensure_rgb!).and_return
|
16
17
|
@rm_image.stub!(:to_blob).and_return(@blob)
|
18
|
+
@rm_image.stub!(:format).and_return('JPEG')
|
17
19
|
@rm_image.stub!(:format=).and_return('JPEG')
|
18
20
|
@rm_image.stub!(:columns).and_return(@columns)
|
19
21
|
@rm_image.stub!(:rows).and_return(@rows)
|
@@ -47,7 +49,7 @@ describe Airbrush::Processors::Image::Rmagick do
|
|
47
49
|
|
48
50
|
it 'should return the raw image data back to the caller' do
|
49
51
|
@rm_image.should_receive(:to_blob).and_return('blob')
|
50
|
-
@processor.resize(@image, 300, 200).should == { :image => "blob", :height => 1000, :width => 1500 }
|
52
|
+
@processor.resize(@image, 300, 200).should == { :format => "JPEG", :image => "blob", :height => 1000, :width => 1500 }
|
51
53
|
end
|
52
54
|
|
53
55
|
end
|
@@ -70,7 +72,7 @@ describe Airbrush::Processors::Image::Rmagick do
|
|
70
72
|
|
71
73
|
it 'should return the raw image data back to the caller' do
|
72
74
|
@rm_image.should_receive(:to_blob).and_return('blob')
|
73
|
-
@processor.crop(@image, 10, 10, 100, 100).should == { :image => "blob", :height => 1000, :width => 1500 }
|
75
|
+
@processor.crop(@image, 10, 10, 100, 100).should == { :format => "JPEG", :image => "blob", :height => 1000, :width => 1500 }
|
74
76
|
end
|
75
77
|
|
76
78
|
end
|
@@ -93,7 +95,7 @@ describe Airbrush::Processors::Image::Rmagick do
|
|
93
95
|
|
94
96
|
it 'should return the raw image data back to the caller' do
|
95
97
|
@rm_image.should_receive(:to_blob).and_return('blob')
|
96
|
-
@processor.crop_resize(@image, 75, 75).should == { :image => "blob", :height => 1000, :width => 1500 }
|
98
|
+
@processor.crop_resize(@image, 75, 75).should == { :format => "JPEG", :image => "blob", :height => 1000, :width => 1500 }
|
97
99
|
end
|
98
100
|
|
99
101
|
end
|
@@ -107,10 +109,56 @@ describe Airbrush::Processors::Image::Rmagick do
|
|
107
109
|
|
108
110
|
it 'should return the raw image data back to the caller' do
|
109
111
|
@rm_image.should_receive(:to_blob).twice.and_return('blob')
|
110
|
-
@processor.previews(@image, { :small => [200,100], :large => [500,250] }).should == {
|
112
|
+
@processor.previews(@image, { :small => [200,100], :large => [500,250] }).should == { :small=> { :format => "JPEG", :image => "blob", :height => 1000, :width => 1500 }, :large=> { :format => "JPEG", :image => "blob", :height => 1000, :width => 1500 }, :original => [1500, 1000] }
|
111
113
|
end
|
112
114
|
|
113
115
|
end
|
116
|
+
|
117
|
+
describe 'crop_and_resize' do
|
118
|
+
|
119
|
+
it 'should return an image with the specified sizes' do
|
120
|
+
@processor.resized_crop_mask(@image, {:x => 0, :y => 0, :width => 800, :height => 800 }, { :width => 2890, :height => 1940 }).should == {
|
121
|
+
:format => "JPEG", :image => "blob", :width => 1500, :height => 1000}
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should return an image cropped to the crop marks' do
|
126
|
+
@rm_image.should_receive(:crop!).with(0,20,800,900).once
|
127
|
+
@processor.resized_crop_mask(@image, {:x => 0, :y => 20, :width => 800, :height => 900 }, { :width => 2890, :height => 1940 }).should == {
|
128
|
+
:format => "JPEG", :image => "blob", :width => 1500, :height => 1000}
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should return the raw image data back to the caller' do
|
132
|
+
@rm_image.should_receive(:resize!).with(2890,1940).once
|
133
|
+
@processor.resized_crop_mask(@image, {:x => 0, :y => 0, :width => 800, :height => 800 }, { :width => 2890, :height => 1940 }).should == {
|
134
|
+
:format => "JPEG", :image => "blob", :width => 1500, :height => 1000}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "exif_data" do
|
139
|
+
it "should return a hash of exif data for the image" do
|
140
|
+
@rm_image.should_receive(:get_exif_by_entry).once.and_return([["Make", "Canon"], ["ShutterSpeedValue", "189/32"]])
|
141
|
+
@processor.exif_data(@image).should == { "Make" => "Canon", "ShutterSpeedValue" => "189/32" }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "get_format" do
|
146
|
+
|
147
|
+
Airbrush::Processors::Image::Rmagick::VALID_OUTPUT_FORMATS.each do |valid_format|
|
148
|
+
it "should not change the image format if it is a #{valid_format}" do
|
149
|
+
@rm_image.should_receive(:format).and_return(valid_format)
|
150
|
+
@processor.send(:get_format, @rm_image).should == valid_format
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should change the format to JPEG if a non valid return format is found" do
|
155
|
+
['TIF', 'TIFF', 'PDF'].each do |bad_format|
|
156
|
+
@rm_image.should_receive(:format).and_return(bad_format)
|
157
|
+
@processor.send(:get_format, @rm_image).should == 'JPEG'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
114
162
|
|
115
163
|
describe 'dimension calculation' do
|
116
164
|
|
metadata
CHANGED
@@ -1,69 +1,100 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airbrush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
|
-
-
|
12
|
+
- Marcus Crafter
|
13
|
+
- Nick Marfleet
|
8
14
|
autorequire:
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-05-18 00:00:00 +10:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: starling
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
|
-
- - "
|
27
|
+
- - "="
|
22
28
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 9
|
32
|
+
- 8
|
33
|
+
version: 0.9.8
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: activesupport
|
27
|
-
|
28
|
-
|
29
|
-
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
30
41
|
requirements:
|
31
|
-
- - "
|
42
|
+
- - "="
|
32
43
|
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 3
|
47
|
+
- 5
|
48
|
+
version: 2.3.5
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
35
51
|
- !ruby/object:Gem::Dependency
|
36
52
|
name: ruby2ruby
|
37
|
-
|
38
|
-
|
39
|
-
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
40
56
|
requirements:
|
41
|
-
- - "
|
57
|
+
- - "="
|
42
58
|
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
|
59
|
+
segments:
|
60
|
+
- 1
|
61
|
+
- 2
|
62
|
+
- 1
|
63
|
+
version: 1.2.1
|
64
|
+
type: :runtime
|
65
|
+
version_requirements: *id003
|
45
66
|
- !ruby/object:Gem::Dependency
|
46
67
|
name: daemons
|
47
|
-
|
48
|
-
|
49
|
-
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
50
71
|
requirements:
|
51
|
-
- - "
|
72
|
+
- - "="
|
52
73
|
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
|
74
|
+
segments:
|
75
|
+
- 1
|
76
|
+
- 0
|
77
|
+
- 10
|
78
|
+
version: 1.0.10
|
79
|
+
type: :runtime
|
80
|
+
version_requirements: *id004
|
55
81
|
- !ruby/object:Gem::Dependency
|
56
82
|
name: hoe
|
57
|
-
|
58
|
-
|
59
|
-
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
60
86
|
requirements:
|
61
87
|
- - ">="
|
62
88
|
- !ruby/object:Gem::Version
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
89
|
+
segments:
|
90
|
+
- 1
|
91
|
+
- 8
|
92
|
+
- 0
|
93
|
+
version: 1.8.0
|
94
|
+
type: :development
|
95
|
+
version_requirements: *id005
|
96
|
+
description: Distributed image processing server.
|
97
|
+
email: nick@whatevernext.org
|
67
98
|
executables:
|
68
99
|
- airbrush
|
69
100
|
- airbrush-example-client
|
@@ -132,6 +163,8 @@ files:
|
|
132
163
|
- website/template.rhtml
|
133
164
|
has_rdoc: true
|
134
165
|
homepage: http://airbrush.rubyforge.org
|
166
|
+
licenses: []
|
167
|
+
|
135
168
|
post_install_message:
|
136
169
|
rdoc_options:
|
137
170
|
- --main
|
@@ -139,23 +172,27 @@ rdoc_options:
|
|
139
172
|
require_paths:
|
140
173
|
- lib
|
141
174
|
required_ruby_version: !ruby/object:Gem::Requirement
|
175
|
+
none: false
|
142
176
|
requirements:
|
143
177
|
- - ">="
|
144
178
|
- !ruby/object:Gem::Version
|
179
|
+
segments:
|
180
|
+
- 0
|
145
181
|
version: "0"
|
146
|
-
version:
|
147
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
148
184
|
requirements:
|
149
185
|
- - ">="
|
150
186
|
- !ruby/object:Gem::Version
|
187
|
+
segments:
|
188
|
+
- 0
|
151
189
|
version: "0"
|
152
|
-
version:
|
153
190
|
requirements: []
|
154
191
|
|
155
192
|
rubyforge_project: airbrush
|
156
|
-
rubygems_version: 1.
|
193
|
+
rubygems_version: 1.3.7
|
157
194
|
signing_key:
|
158
|
-
specification_version:
|
159
|
-
summary:
|
195
|
+
specification_version: 3
|
196
|
+
summary: Distributed image processing server.
|
160
197
|
test_files: []
|
161
198
|
|