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 CHANGED
File without changes
@@ -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 preview processing"
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
- next if k == :original
58
- File.open("#{OPTIONS[:output]}-#{k}.jpg", 'w') { |f| f << v[:image] }
59
- puts "Saved preview #{OPTIONS[:output]}-#{k}.jpg (#{v[:width]}x#{v[:height]})" if OPTIONS[:verbose]
60
- end
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 = 'FIXME full name' # can also be an array of Authors
4
- EMAIL = "FIXME email"
5
- DESCRIPTION = "description of gem"
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 = "unknown"
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', '>= 0.9.3' ], [ 'activesupport', '>= 2.0.2' ], [ 'ruby2ruby', '>= 1.1.8' ], [ 'daemons', '>= 1.0.9' ] ] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
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 = 'JPEG' # ensure that resized output is a JPEG
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 if clipping_required?(image, size)
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
- portrait image do
61
- return [ size, size.to_f / ratio ]
62
- end
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
- # Must be a square image.
69
- return [ size, size ]
84
+ return [ size, size ] # Square
70
85
  end
71
86
 
72
87
  def clipping_required?(image, size)
73
- size > image.columns or size > image.rows
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
@@ -2,7 +2,7 @@ module Airbrush #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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 == { :small=> { :image => "blob", :height => 1000, :width => 1500 }, :large=> { :image => "blob", :height => 1000, :width => 1500 }, :original => [1500, 1000] }
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
- version: 0.0.2
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
5
10
  platform: ruby
6
11
  authors:
7
- - FIXME full name
12
+ - Marcus Crafter
13
+ - Nick Marfleet
8
14
  autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2008-10-29 00:00:00 +11:00
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
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 0.9.3
24
- version:
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
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 2.0.2
34
- version:
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
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 1.1.8
44
- version:
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
- type: :runtime
48
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 1.0.9
54
- version:
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
- type: :development
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 1.8.2
64
- version:
65
- description: description of gem
66
- email: FIXME email
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.2.0
193
+ rubygems_version: 1.3.7
157
194
  signing_key:
158
- specification_version: 2
159
- summary: description of gem
195
+ specification_version: 3
196
+ summary: Distributed image processing server.
160
197
  test_files: []
161
198