core_image 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Spencer Rogers
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.md ADDED
@@ -0,0 +1,47 @@
1
+ ## Core Image Gem ##
2
+ Simple image manipulation using Apple's Core Image technology.
3
+
4
+ ### Installation ###
5
+ `gem install core_image`
6
+
7
+ ### Usage ###
8
+ require 'core_image'
9
+ image = CoreImage.new(location) # more on this below
10
+ image.rotate(90)
11
+ image.scale_to_size(300)
12
+ image.save
13
+
14
+ ### Supports Chaining ###
15
+ image.rotate(90).scale(0.6).save(new_path)
16
+
17
+ ### Extra Bits ###
18
+ image.flip_horizontally
19
+ image.flip_vertically
20
+ image.save # oh no!
21
+ image.revert #=> reverts to the original image
22
+
23
+ ### One More Thing! ###
24
+ image.tint({:red => 0, :green => 0, :blue => 255, :alpha => 1.0})
25
+ image.crop(x, y, w, h) # starts from lower left
26
+ image.color_at(x, y) # => RGB Hash of specified pixel
27
+ image.overlay(another_image) # follows the same instantiation rules
28
+
29
+
30
+ ### Instantiating ###
31
+ You can instantiate a new Core Image object with the following:
32
+ * A path to a file
33
+ * A url to a file
34
+ * An Apple image object: NSImage, CIImage, CGImage
35
+ * Another Core Image object
36
+
37
+ ### File Types ###
38
+ Core Image can open and save the following formats: jpeg, png, tiff, gif, pct, and bmp. It can also open pdfs and save them as one of the previously listed image formats.
39
+
40
+ ### Requirements ###
41
+ Requires Mac OS X to run.
42
+
43
+ ### Tests ###
44
+ Core Image testing is done via the [Riot](http://rubygems.org/gems/riot) gem and can be run by typing `rake` from the root directory of this repository (you must have Riot installed to do so).
45
+
46
+ ### Support ###
47
+ Serveral updates are in the works but feel free to report issues, make comments, voice concerns and I'll get to them when I can! Enjoy!
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ desc "Run all our tests"
6
+ task :test do
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "test"
9
+ t.pattern = "test/**/*_test.rb"
10
+ t.verbose = false
11
+ end
12
+ end
13
+
14
+ task :default => :test
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "core_image"
3
+ spec.version = "0.0.3"
4
+ spec.date = "2012-01-31"
5
+ spec.summary = "Manipulate images via Apple's built-in Core Image technology"
6
+ spec.description = "Simple image manipulation using Apple's Core Image technology. Scale, rotate, flip, convert, overlay, and more."
7
+ spec.author = "Spencer Rogers"
8
+ spec.email = "SpencerRogers@gmail.com"
9
+ spec.files = `git ls-files`.split("\n")
10
+ spec.test_files = ['test/core_image_test.rb']
11
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ spec.require_paths = ["lib"]
13
+ spec.add_development_dependency "rubygems"
14
+ spec.add_development_dependency "riot"
15
+ spec.homepage = "https://github.com/serogers/core_image"
16
+ spec.requirements << ["Mac OS X"]
17
+ end
data/lib/core_image.rb CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  class CoreImage
6
6
  require 'osx/cocoa'
7
+ require 'uri'
7
8
 
8
9
  attr_accessor :image_path
9
10
  attr_accessor :original_image
@@ -24,29 +25,31 @@ class CoreImage
24
25
  scale(dimensions[:ratio])
25
26
  end
26
27
 
27
- def rotate(degrees = 0)
28
+ def rotate(degrees = 90)
28
29
  radians = degrees_to_radians(degrees)
29
30
  transform = OSX::CGAffineTransformMakeRotation(radians)
30
31
  self.ciimage = self.ciimage.imageByApplyingTransform(transform)
31
32
  self
32
33
  end
33
34
 
34
- def flip_horizontal
35
+ def flip_horizontally
35
36
  transform = OSX::CGAffineTransformMakeScale(-1.0, 1.0)
36
37
  self.ciimage = self.ciimage.imageByApplyingTransform(transform)
37
38
  self
38
39
  end
39
40
 
40
- def crop(x, y, w, h)
41
- # coordinates start in lower left
41
+ def flip_vertically
42
+ flip_horizontally.rotate(180)
43
+ end
44
+
45
+ def crop(x, y, w, h) # coordinates start in lower left
42
46
  self.ciimage = self.ciimage.imageByCroppingToRect(OSX::CGRectMake(x, y, w, h))
43
47
  self
44
48
  end # crop
45
49
 
46
- def tint(rgb_string)
47
- image_size = size
48
- context = create_ci_context(image_size[:width], image_size[:height])
49
- colored_image = OSX::CIImage.imageWithColor(OSX::CIColor.colorWithString(rgb_string))
50
+ def tint(rgb)
51
+ context = set_context
52
+ colored_image = OSX::CIImage.imageWithColor(OSX::CIColor.colorWithString(rgb_hash_to_string(rgb)))
50
53
  filter = OSX::CIFilter.filterWithName("CIMultiplyCompositing")
51
54
  filter.setValue_forKey(colored_image, "inputImage")
52
55
  filter.setValue_forKey(self.ciimage, "inputBackgroundImage")
@@ -56,10 +59,9 @@ class CoreImage
56
59
  self
57
60
  end
58
61
 
59
- def overlay_image(object)
62
+ def overlay(object)
60
63
  image = open_object(object)
61
- image_size = size
62
- context = create_ci_context(image_size[:width], image_size[:height])
64
+ context = set_context
63
65
  filter = OSX::CIFilter.filterWithName("CISourceOverCompositing")
64
66
  filter.setValue_forKey(image, "inputImage")
65
67
  filter.setValue_forKey(self.ciimage, "inputBackgroundImage")
@@ -69,8 +71,15 @@ class CoreImage
69
71
  self
70
72
  end
71
73
 
72
- def color_at(x, y)
73
- to_bitmap.colorAtX_y(x, y) # returns NSColor
74
+ def color_at(x, y) # coordinates start in upper left
75
+ set_context
76
+ nscolor = to_bitmap.colorAtX_y(x, y)
77
+ rgb = {}
78
+ rgb[:red] = (nscolor.redComponent.to_f * 255.0).to_i
79
+ rgb[:green] = (nscolor.greenComponent.to_f * 255.0).to_i
80
+ rgb[:blue] = (nscolor.blueComponent.to_f * 255.0).to_i
81
+ rgb[:alpha] = nscolor.alphaComponent
82
+ rgb
74
83
  end
75
84
 
76
85
  def to_bitmap
@@ -81,24 +90,31 @@ class CoreImage
81
90
  to_bitmap.CGImage
82
91
  end
83
92
 
93
+ def to_nsimage
94
+ image_size = self.size
95
+ nsimage_rep = OSX::NSCIImageRep.imageRepWithCIImage(self.ciimage)
96
+ nsimage = OSX::NSImage.alloc.initWithSize(OSX::NSMakeSize(image_size[:width], image_size[:height]))
97
+ nsimage.addRepresentation(nsimage_rep)
98
+ nsimage
99
+ end
100
+
84
101
  def size
85
102
  width, height = 0, 0
86
103
 
87
104
  begin # sometimes ciimage.extent throws an error
88
- size = self.ciimage.extent.size
89
- width = size.width
90
- height = size.height
105
+ image_size = self.ciimage.extent.size
106
+ width = image_size.width
107
+ height = image_size.height
91
108
  rescue
92
- # if image.extent fails, use another method for measuring size (by converting to a cgimage)
93
109
  begin
94
110
  cgimage = self.to_cgimage
95
111
  width = OSX::CGImageGetWidth(cgimage)
96
112
  height = OSX::CGImageGetHeight(cgimage)
97
- rescue # if the second image size function fails set zero
113
+ rescue
98
114
  width = 0
99
115
  height = 0
100
- end # begin/rescue
101
- end # begin/rescue
116
+ end
117
+ end
102
118
 
103
119
  {:width => width, :height => height}
104
120
  end
@@ -136,18 +152,30 @@ class CoreImage
136
152
  def open_object(object)
137
153
  case object.class.to_s
138
154
  when "String"
139
- ciimage = File.extname(object).downcase == ".pdf" ? open_from_pdf_path(object) : open_from_path(object)
155
+ open_from_location(object)
140
156
  when "OSX::CIImage"
141
- ciimage = object
157
+ object
158
+ when "OSX::CGImage", "OSX::NSObject"
159
+ OSX::CIImage.imageWithCGImage(object)
160
+ when "OSX::NSImage"
161
+ tiff_data = object.TIFFRepresentation
162
+ OSX::CIImage.imageWithData(tiff_data)
163
+ when "CoreImage"
164
+ object.ciimage
142
165
  end
143
166
  end
144
167
 
145
- def open_from_path(path_to_image)
146
- OSX::CIImage.imageWithContentsOfURL(OSX::NSURL.fileURLWithPath(path_to_image))
147
- end # open
168
+ def open_from_location(string)
169
+ nsurl = valid_url?(string) ? nsurl_from_url(string) : nsurl_from_path(string)
170
+ File.extname(string).downcase == ".pdf" ? open_from_pdf(nsurl) : open_from_image_file(nsurl)
171
+ end
172
+
173
+ def open_from_image_file(nsurl)
174
+ OSX::CIImage.imageWithContentsOfURL(nsurl)
175
+ end
148
176
 
149
- def open_from_pdf_path(pdf_path, scale = 1, dpi = 72.0, preserve_alpha = false)
150
- data = OSX::NSData.dataWithContentsOfURL(OSX::NSURL.fileURLWithPath(pdfPath))
177
+ def open_from_pdf(nsurl, scale = 1, dpi = 72.0, preserve_alpha = false)
178
+ data = OSX::NSData.dataWithContentsOfURL(nsurl)
151
179
  pdf_rep = OSX::NSPDFImageRep.imageRepWithData(data)
152
180
  nsimage = OSX::NSImage.alloc.initWithData(data)
153
181
  nssize = nsimage.size
@@ -174,7 +202,7 @@ class CoreImage
174
202
  cgimage = OSX::CGBitmapContextCreateImage(context.graphicsPort)
175
203
 
176
204
  # return a ciimage
177
- OSX::CIImage.initWithCGImage(cgimage)
205
+ OSX::CIImage.imageWithCGImage(cgimage)
178
206
  end
179
207
 
180
208
  def degrees_to_radians(degrees)
@@ -198,7 +226,11 @@ class CoreImage
198
226
  when ".bmp"
199
227
  OSX::NSBMPFileType
200
228
  end
201
-
229
+ end
230
+
231
+ def set_context
232
+ image_size = size
233
+ create_ci_context(image_size[:width], image_size[:height])
202
234
  end
203
235
 
204
236
  def create_ci_context(width, height)
@@ -206,10 +238,30 @@ class CoreImage
206
238
  end
207
239
 
208
240
  def create_ns_context(width, height)
209
- blank_bitmap = OSX::NSBitmapImageRep.alloc.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(nil, width, height, 8, 4, true, false, OSX::NSDeviceRGBColorSpace, 0, 0)
241
+ blank_bitmap = OSX::NSBitmapImageRep.alloc.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(nil, width, height, 8, 4, true, false, OSX::NSCalibratedRGBColorSpace, 0, 0)
210
242
  context = OSX::NSGraphicsContext.graphicsContextWithBitmapImageRep(blank_bitmap)
211
243
  OSX::NSGraphicsContext.setCurrentContext(context)
212
244
  context
213
- end # createContext
245
+ end
214
246
 
247
+ def rgb_hash_to_string(rgb)
248
+ rgb[:alpha] = 1.0 if rgb[:alpha].nil?
249
+ "#{rgb[:red].to_f / 255.0} #{rgb[:green].to_f / 255.0} #{rgb[:blue].to_f / 255.0} #{rgb[:alpha].to_f}"
250
+ end
251
+
252
+ def valid_url?(string)
253
+ uri = URI.parse(string)
254
+ %w( http https ).include?(uri.scheme) and uri.path.nil? == false
255
+ rescue URI::BadURIError
256
+ false
257
+ end
258
+
259
+ def nsurl_from_path(path)
260
+ OSX::NSURL.fileURLWithPath(path)
261
+ end
262
+
263
+ def nsurl_from_url(url)
264
+ OSX::NSURL.URLWithString(url)
265
+ end
266
+
215
267
  end
@@ -0,0 +1,103 @@
1
+ require 'rubygems'
2
+ require 'riot'
3
+ require './lib/core_image.rb'
4
+
5
+ context 'Core Image' do
6
+ setup {CoreImage.new("./test/images/test.png")}
7
+ helper(:solid_blue_rgb) { {:red => 0, :green => 0, :blue => 255, :alpha => 1.0} }
8
+
9
+ context 'opens file' do
10
+ asserts("Opens image file") {topic.ciimage.nil? == false}
11
+ asserts("Opens pdf file") {CoreImage.new("./test/images/test.pdf").ciimage.nil? == false}
12
+ asserts("Opens file from the internet") {CoreImage.new("http://dl.dropbox.com/u/1848018/core_image_test.png").ciimage.nil? == false}
13
+ end
14
+
15
+ context 'opens apple image object' do
16
+ asserts("Opens ciimage") {CoreImage.new(topic.ciimage).ciimage.class.to_s == "OSX::CIImage"}
17
+ asserts("Opens cgimage") {CoreImage.new(topic.to_cgimage).ciimage.class.to_s == "OSX::CIImage"}
18
+ asserts("Opens nsimage") {CoreImage.new(topic.to_nsimage).ciimage.class.to_s == "OSX::CIImage"}
19
+ end
20
+
21
+ context 'opens image from self' do
22
+ asserts("Opens from core_image instantiation") {CoreImage.new(topic).ciimage.class.to_s == "OSX::CIImage"}
23
+ end
24
+
25
+ context '.scale(1.5)' do
26
+ asserts("Image was scaled up by 150%") {topic.scale(1.5).size == {:width => 75, :height => 75}}
27
+ end
28
+
29
+ context '.scale(0.6)' do
30
+ asserts("Image was scaled down by 60%") {topic.scale(0.6).size == {:width => 30, :height => 30}}
31
+ end
32
+
33
+ context '.scale_to_size(300)' do
34
+ asserts("Image was scaled to fit within 500px") {topic.scale_to_size(300).size == {:width => 300, :height => 300}}
35
+ end
36
+
37
+ context '.rotate(90)' do
38
+ setup {topic.rotate(90).color_at(10, 40)}
39
+ asserts("Image was rotated by 90 degrees") {topic == solid_blue_rgb}
40
+ end
41
+
42
+ context '.rotate(-90)' do
43
+ setup {topic.rotate(-90).color_at(40, 10)}
44
+ asserts("Image was rotated by -90 degrees") {topic == solid_blue_rgb}
45
+ end
46
+
47
+ context '.flip_horizontally' do
48
+ setup {topic.flip_horizontally.color_at(40, 10)}
49
+ asserts("Image was flipped on its x-axis") {topic == solid_blue_rgb}
50
+ end
51
+
52
+ context '.flip_vertically' do
53
+ setup {topic.flip_vertically.color_at(10, 40)}
54
+ asserts("Image was flipped on its y-axis") {topic == solid_blue_rgb}
55
+ end
56
+
57
+ context '.crop' do
58
+ setup {topic.crop(0, 0, 25, 25)}
59
+ asserts("Image was cropped to a 25 pixel box starting in lower left") {topic.size == {:width => 25, :height => 25} and topic.color_at(10, 2) == {:red => 0, :green => 128, :blue => 0, :alpha => 1.0}}
60
+ end
61
+
62
+ context '.tint' do
63
+ setup {topic.tint(solid_blue_rgb)}
64
+ asserts("Image was tinted a dark blue") {topic.color_at(40, 10) == solid_blue_rgb}
65
+ end
66
+
67
+ context '.overlay' do
68
+ setup {topic.overlay(CoreImage.new("./test/images/overlay.png")).color_at(40, 10) == solid_blue_rgb}
69
+ end
70
+
71
+ context '.color_at' do
72
+ setup {topic.color_at(10, 10)} # select pixel from blue area
73
+ asserts("Blue equals R:0, G:0, B:255, A:1.0") {topic == solid_blue_rgb}
74
+ end
75
+
76
+ context '.to_bitmap' do
77
+ setup {topic.to_bitmap}
78
+ asserts("Is a bitmap") {topic.class.to_s == "OSX::NSBitmapImageRep"}
79
+ end
80
+
81
+ context '.to_cgimage' do
82
+ setup {topic.to_cgimage}
83
+ asserts("Is a cgimage") {topic.class.to_s == "OSX::NSObject"}
84
+ end
85
+
86
+ context '.to_nsimage' do
87
+ setup {topic.to_nsimage}
88
+ asserts("Is a nsimage") {topic.class.to_s == "OSX::NSImage"}
89
+ end
90
+
91
+ context '.size' do
92
+ asserts("Size equals 50x50") {topic.size == {:width => 50, :height => 50}}
93
+ end
94
+
95
+ context '.size_to_fit_maximum(350)' do
96
+ asserts("Returns 350x350 ratio of 7.0") {topic.size_to_fit_maximum(350) == {:height=>350, :width=>350, :ratio=>7.0}}
97
+ end
98
+
99
+ context '.revert' do
100
+ asserts("Image reverts to original") {topic.ciimage = topic.flip_horizontally.revert.ciimage}
101
+ end
102
+
103
+ end
Binary file
Binary file
Binary file
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: core_image
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Spencer Rogers
@@ -15,10 +15,37 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-24 00:00:00 -08:00
18
+ date: 2012-01-31 00:00:00 -08:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubygems
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: riot
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
22
49
  description: Simple image manipulation using Apple's Core Image technology. Scale, rotate, flip, convert, overlay, and more.
23
50
  email: SpencerRogers@gmail.com
24
51
  executables: []
@@ -28,9 +55,18 @@ extensions: []
28
55
  extra_rdoc_files: []
29
56
 
30
57
  files:
58
+ - .gitignore
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - core_image.gemspec
31
63
  - lib/core_image.rb
64
+ - test/core_image_test.rb
65
+ - test/images/overlay.png
66
+ - test/images/test.pdf
67
+ - test/images/test.png
32
68
  has_rdoc: true
33
- homepage: https://github.com/serogers
69
+ homepage: https://github.com/serogers/core_image
34
70
  licenses: []
35
71
 
36
72
  post_install_message:
@@ -62,6 +98,6 @@ rubyforge_project:
62
98
  rubygems_version: 1.5.3
63
99
  signing_key:
64
100
  specification_version: 3
65
- summary: Manipulate images via Apple's Core Image technology
66
- test_files: []
67
-
101
+ summary: Manipulate images via Apple's built-in Core Image technology
102
+ test_files:
103
+ - test/core_image_test.rb