updated top the the new version of attachment_fu plugin to work out some
[selectricity-live] / vendor / plugins / attachment_fu / vendor / red_artisan / core_image / processor.rb
1 require 'rubygems'
2 require 'osx/cocoa'
3 require 'active_support'
4
5 require 'red_artisan/core_image/filters/scale'
6 require 'red_artisan/core_image/filters/color'
7 require 'red_artisan/core_image/filters/watermark'
8 require 'red_artisan/core_image/filters/quality'
9 require 'red_artisan/core_image/filters/perspective'
10 require 'red_artisan/core_image/filters/effects'
11
12 # Generic image processor for scaling images based on CoreImage via RubyCocoa.
13 #
14 # Example usage:
15 #
16 # p = Processor.new OSX::CIImage.from(path_to_image)
17 # p.resize(640, 480)
18 # p.render do |result|
19 #   result.save('resized.jpg', OSX::NSJPEGFileType)
20 # end
21 #
22 # This will resize the image to the given dimensions exactly, if you'd like to ensure that aspect ratio is preserved:
23 #
24 # p = Processor.new OSX::CIImage.from(path_to_image)
25 # p.fit(640)
26 # p.render do |result|
27 #   result.save('resized.jpg', OSX::NSJPEGFileType)
28 # end
29 #
30 # fit(size) will attempt its best to resize the image so that the longest width/height (depending on image orientation) will match
31 # the given size. The second axis will be calculated automatically based on the aspect ratio.
32 #
33 # Scaling is performed by first clamping the image so that its external bounds become infinite, this helps when scaling so that any
34 # rounding discrepencies in dimensions don't affect the resultant image. We then perform a Lanczos transform on the image which scales
35 # it to the target size. We then crop the image to the traget dimensions.
36 #
37 # If you are generating smaller images such as thumbnails where high quality rendering isn't as important, an additional method is
38 # available:
39 #
40 # p = Processor.new OSX::CIImage.from(path_to_image)
41 # p.thumbnail(100, 100)
42 # p.render do |result|
43 #   result.save('resized.jpg', OSX::NSJPEGFileType)
44 # end
45 #
46 # This will perform a straight affine transform and scale the X and Y boundaries to the requested size. Generally, this will be faster
47 # than a lanczos scale transform, but with a scaling quality trade.
48 #
49 # More than welcome to intregrate any patches, improvements - feel free to mail me with ideas.
50 #
51 # Thanks to
52 # * Satoshi Nakagawa for working out that OCObjWrapper needs inclusion when aliasing method_missing on existing OSX::* classes.
53 # * Vasantha Crabb for general help and inspiration with Cocoa
54 # * Ben Schwarz for example image data and collaboration during performance testing
55 #
56 # Copyright (c) Marcus Crafter <crafterm@redartisan.com> released under the MIT license
57 #
58 module RedArtisan
59   module CoreImage
60     class Processor
61   
62       def initialize(original)
63         if original.respond_to? :to_str
64           @original = OSX::CIImage.from(original.to_str)
65         else
66           @original = original
67         end
68       end
69   
70       def render(&block)
71         raise "unprocessed image: #{@original}" unless @target
72         block.call @target
73       end
74       
75       include Filters::Scale, Filters::Color, Filters::Watermark, Filters::Quality, Filters::Perspective, Filters::Effects
76   
77       private
78   
79         def create_core_image_context(width, height)
80                 output = OSX::NSBitmapImageRep.alloc.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel(nil, width, height, 8, 4, true, false, OSX::NSDeviceRGBColorSpace, 0, 0)
81                 context = OSX::NSGraphicsContext.graphicsContextWithBitmapImageRep(output)
82                 OSX::NSGraphicsContext.setCurrentContext(context)
83                 @ci_context = context.CIContext
84         end
85         
86         def vector(x, y, w, h)
87           OSX::CIVector.vectorWithX_Y_Z_W(x, y, w, h)
88         end
89     end
90   end
91 end
92
93 module OSX
94   class CIImage
95     include OCObjWrapper
96   
97     def method_missing_with_filter_processing(sym, *args, &block)
98       f = OSX::CIFilter.filterWithName("CI#{sym.to_s.camelize}")
99       return method_missing_without_filter_processing(sym, *args, &block) unless f
100     
101       f.setDefaults if f.respond_to? :setDefaults
102       f.setValue_forKey(self, 'inputImage')
103       options = args.last.is_a?(Hash) ? args.last : {}
104       options.each { |k, v| f.setValue_forKey(v, k.to_s) }
105     
106       block.call f.valueForKey('outputImage')
107     end
108   
109     alias_method_chain :method_missing, :filter_processing
110       
111     def save(target, format = OSX::NSJPEGFileType, properties = nil)
112       bitmapRep = OSX::NSBitmapImageRep.alloc.initWithCIImage(self)
113       blob = bitmapRep.representationUsingType_properties(format, properties)
114       blob.writeToFile_atomically(target, false)
115     end
116   
117     def self.from(filepath)
118       raise Errno::ENOENT, "No such file or directory - #{filepath}" unless File.exists?(filepath)
119       OSX::CIImage.imageWithContentsOfURL(OSX::NSURL.fileURLWithPath(filepath))
120     end
121   end
122 end
123

Benjamin Mako Hill || Want to submit a patch?