attachment_zen 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,56 @@
1
+ require 'red_artisan/core_image/processor'
2
+
3
+ module Technoweenie # :nodoc:
4
+ module AttachmentFu # :nodoc:
5
+ module Processors
6
+ module CoreImageProcessor
7
+ def self.included(base)
8
+ base.send :extend, ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+ def with_image(file, &block)
13
+ block.call OSX::CIImage.from(file)
14
+ end
15
+ end
16
+
17
+ protected
18
+ def _process_attachment
19
+ with_image do |img|
20
+ self.width = img.extent.size.width if respond_to?(:width)
21
+ self.height = img.extent.size.height if respond_to?(:height)
22
+ resize_image_or_thumbnail! img
23
+ end if image?
24
+ end
25
+
26
+ # Performs the actual resizing operation for a thumbnail
27
+ def resize_image(img, size)
28
+ processor = ::RedArtisan::CoreImage::Processor.new(img)
29
+ size = size.first if size.is_a?(Array) && size.length == 1
30
+ if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
31
+ if size.is_a?(Fixnum)
32
+ processor.fit(size)
33
+ else
34
+ processor.resize(size[0], size[1])
35
+ end
36
+ else
37
+ new_size = [img.extent.size.width, img.extent.size.height] / size.to_s
38
+ processor.resize(new_size[0], new_size[1])
39
+ end
40
+
41
+ processor.render do |result|
42
+ self.width = result.extent.size.width if respond_to?(:width)
43
+ self.height = result.extent.size.height if respond_to?(:height)
44
+
45
+ # Get a new temp_path for the image before saving
46
+ temp_paths.unshift Tempfile.new(random_tempfile_filename, Technoweenie::AttachmentFu.tempfile_path).path
47
+ result.save self.temp_path, OSX::NSJPEGFileType
48
+ self.size = File.size(self.temp_path)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,52 @@
1
+ require 'gd2'
2
+ module Technoweenie # :nodoc:
3
+ module AttachmentFu # :nodoc:
4
+ module Processors
5
+ module Gd2Processor
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ base.alias_method_chain :process_attachment, :processing
9
+ end
10
+
11
+ module ClassMethods
12
+ # Yields a block containing a GD2 Image for the given binary data.
13
+ def with_image(file, &block)
14
+ im = GD2::Image.import(file)
15
+ block.call(im)
16
+ end
17
+ end
18
+
19
+ protected
20
+ def process_attachment_with_processing
21
+ return unless process_attachment_without_processing && image?
22
+ with_image do |img|
23
+ resize_image_or_thumbnail! img
24
+ self.width = img.width
25
+ self.height = img.height
26
+ end
27
+ end
28
+
29
+ # Performs the actual resizing operation for a thumbnail
30
+ def resize_image(img, size)
31
+ size = size.first if size.is_a?(Array) && size.length == 1
32
+ if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
33
+ if size.is_a?(Fixnum)
34
+ # Borrowed from image science's #thumbnail method and adapted
35
+ # for this.
36
+ scale = size.to_f / (img.width > img.height ? img.width.to_f : img.height.to_f)
37
+ img.resize!((img.width * scale).round(1), (img.height * scale).round(1), false)
38
+ else
39
+ img.resize!(size.first, size.last, false)
40
+ end
41
+ else
42
+ w, h = [img.width, img.height] / size.to_s
43
+ img.resize!(w, h, false)
44
+ end
45
+ temp_paths.unshift random_tempfile_filename
46
+ self.size = img.export(self.temp_path)
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,59 @@
1
+ require 'image_science'
2
+ module Technoweenie # :nodoc:
3
+ module AttachmentFu # :nodoc:
4
+ module Processors
5
+ module ImageScienceProcessor
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Yields a block containing an Image Science image for the given binary data.
12
+ def with_image(file, &block)
13
+ ::ImageScience.with_image file, &block
14
+ end
15
+ end
16
+
17
+ protected
18
+ def _process_attachment
19
+ return unless image?
20
+ with_image do |img|
21
+ self.width = img.width if respond_to?(:width)
22
+ self.height = img.height if respond_to?(:height)
23
+ resize_image_or_thumbnail! img
24
+ end
25
+ end
26
+
27
+ # Performs the actual resizing operation for a thumbnail
28
+ def resize_image(img, size)
29
+ # create a dummy temp file to write to
30
+ # ImageScience doesn't handle all gifs properly, so it converts them to
31
+ # pngs for thumbnails. It has something to do with trying to save gifs
32
+ # with a larger palette than 256 colors, which is all the gif format
33
+ # supports.
34
+ filename.sub! /gif$/, 'png'
35
+ content_type.sub!(/gif$/, 'png')
36
+ temp_paths.unshift write_to_temp_file(filename)
37
+ grab_dimensions = lambda do |img|
38
+ self.width = img.width if respond_to?(:width)
39
+ self.height = img.height if respond_to?(:height)
40
+ img.save self.temp_path
41
+ self.size = File.size(self.temp_path)
42
+ end
43
+
44
+ size = size.first if size.is_a?(Array) && size.length == 1
45
+ if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
46
+ if size.is_a?(Fixnum)
47
+ img.thumbnail(size, &grab_dimensions)
48
+ else
49
+ img.resize(size[0], size[1], &grab_dimensions)
50
+ end
51
+ else
52
+ new_size = [img.width, img.height] / size.to_s
53
+ img.resize(new_size[0], new_size[1], &grab_dimensions)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,126 @@
1
+ require 'mini_magick'
2
+ module Technoweenie # :nodoc:
3
+ module AttachmentFu # :nodoc:
4
+ module Processors
5
+ module MiniMagickProcessor
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Yields a block containing an MiniMagick Image for the given binary data.
12
+ def with_image(file, &block)
13
+ begin
14
+ binary_data = file.is_a?(MiniMagick::Image) ? file : MiniMagick::Image.open(file) unless !Object.const_defined?(:MiniMagick)
15
+ block.call binary_data if block && binary_data
16
+ rescue Exception => e
17
+ # Log the failure to load the image.
18
+ logger.debug("Exception working with image: #{$!}")
19
+ binary_data = nil
20
+ end
21
+ ensure
22
+ !binary_data.nil?
23
+ end
24
+ end
25
+
26
+ protected
27
+ def _process_attachment
28
+ with_image do |img|
29
+ resize_image_or_thumbnail! img
30
+ self.width = img[:width] if respond_to?(:width)
31
+ self.height = img[:height] if respond_to?(:height)
32
+ end if image?
33
+ end
34
+
35
+ # Performs the actual resizing operation for a thumbnail
36
+ def resize_image(img, size)
37
+ size = size.first if size.is_a?(Array) && size.length == 1
38
+ img.combine_options do |commands|
39
+ commands.strip unless attachment_options[:keep_profile]
40
+
41
+ if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
42
+ if size.is_a?(Fixnum)
43
+ size = [size, size]
44
+ commands.resize(size.join('x'))
45
+ else
46
+ commands.resize(size.join('x') + '!')
47
+ end
48
+ # extend to thumbnail size
49
+ elsif size.is_a?(String) and size =~ /e$/
50
+ size = size.gsub(/e/, '')
51
+ commands.resize(size.to_s + '>')
52
+ commands.background('#ffffff')
53
+ commands.gravity('center')
54
+ commands.extent(size)
55
+ # crop thumbnail, the smart way
56
+ elsif size.is_a?(String) and size =~ /c$/
57
+ size = size.gsub(/c/, '')
58
+
59
+ # calculate sizes and aspect ratio
60
+ thumb_width, thumb_height = size.split("x")
61
+ thumb_width = thumb_width.to_f
62
+ thumb_height = thumb_height.to_f
63
+
64
+ thumb_aspect = thumb_width.to_f / thumb_height.to_f
65
+ image_width, image_height = img[:width].to_f, img[:height].to_f
66
+ image_aspect = image_width / image_height
67
+
68
+ # only crop if image is not smaller in both dimensions
69
+ unless image_width < thumb_width and image_height < thumb_height
70
+ command = calculate_offset(image_width,image_height,image_aspect,thumb_width,thumb_height,thumb_aspect)
71
+
72
+ # crop image
73
+ commands.extract(command)
74
+ end
75
+
76
+ # don not resize if image is not as height or width then thumbnail
77
+ if image_width < thumb_width or image_height < thumb_height
78
+ commands.background('#ffffff')
79
+ commands.gravity('center')
80
+ commands.extent(size)
81
+ # resize image
82
+ else
83
+ commands.resize("#{size.to_s}")
84
+ end
85
+ # crop end
86
+ else
87
+ commands.resize(size.to_s)
88
+ end
89
+ end
90
+ return temp_paths.unshift img
91
+ rescue MiniMagick::Error => e
92
+ logger.error("Error resizing image #{img}: #{e}")
93
+ self.content_type = "application/unknown"
94
+ return temp_paths
95
+ end
96
+
97
+ def calculate_offset(image_width,image_height,image_aspect,thumb_width,thumb_height,thumb_aspect)
98
+ # only crop if image is not smaller in both dimensions
99
+
100
+ # special cases, image smaller in one dimension then thumbsize
101
+ if image_width < thumb_width
102
+ offset = (image_height / 2) - (thumb_height / 2)
103
+ command = "#{image_width}x#{thumb_height}+0+#{offset}"
104
+ elsif image_height < thumb_height
105
+ offset = (image_width / 2) - (thumb_width / 2)
106
+ command = "#{thumb_width}x#{image_height}+#{offset}+0"
107
+
108
+ # normal thumbnail generation
109
+ # calculate height and offset y, width is fixed
110
+ elsif (image_aspect <= thumb_aspect or image_width < thumb_width) and image_height > thumb_height
111
+ height = image_width / thumb_aspect
112
+ offset = (image_height / 2) - (height / 2)
113
+ command = "#{image_width}x#{height}+0+#{offset}"
114
+ # calculate width and offset x, height is fixed
115
+ else
116
+ width = image_height * thumb_aspect
117
+ offset = (image_width / 2) - (width / 2)
118
+ command = "#{width}x#{image_height}+#{offset}+0"
119
+ end
120
+ # crop image
121
+ command
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,54 @@
1
+ require 'RMagick'
2
+ module Technoweenie # :nodoc:
3
+ module AttachmentFu # :nodoc:
4
+ module Processors
5
+ module RmagickProcessor
6
+ def self.included(base)
7
+ base.send :extend, ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Yields a block containing an RMagick Image for the given binary data.
12
+ def with_image(file, &block)
13
+ begin
14
+ binary_data = file.is_a?(Magick::Image) ? file : Magick::Image.read(file).first unless !Object.const_defined?(:Magick)
15
+ rescue
16
+ # Log the failure to load the image. This should match ::Magick::ImageMagickError
17
+ # but that would cause acts_as_attachment to require rmagick.
18
+ logger.debug("Exception working with image: #{$!}")
19
+ binary_data = nil
20
+ end
21
+ block.call binary_data if block && binary_data
22
+ ensure
23
+ !binary_data.nil?
24
+ end
25
+ end
26
+
27
+ protected
28
+ def _process_attachment
29
+ with_image do |img|
30
+ resize_image_or_thumbnail! img
31
+ self.width = img.columns if respond_to?(:width)
32
+ self.height = img.rows if respond_to?(:height)
33
+ end if image?
34
+ end
35
+
36
+ # Performs the actual resizing operation for a thumbnail
37
+ def resize_image(img, size)
38
+ size = size.first if size.is_a?(Array) && size.length == 1 && !size.first.is_a?(Fixnum)
39
+ if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
40
+ size = [size, size] if size.is_a?(Fixnum)
41
+ img.thumbnail!(*size)
42
+ elsif size.is_a?(String) && size =~ /^c.*$/ # Image cropping - example geometry string: c75x75
43
+ dimensions = size[1..size.size].split("x")
44
+ img.crop_resized!(dimensions[0].to_i, dimensions[1].to_i)
45
+ else
46
+ img.change_geometry(size.to_s) { |cols, rows, image| image.resize!(cols<1 ? 1 : cols, rows<1 ? 1 : rows) }
47
+ end
48
+ img.strip! unless attachment_options[:keep_profile]
49
+ temp_paths.unshift write_to_temp_file(img.to_blob)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ module RedArtisan
2
+ module CoreImage
3
+ module Filters
4
+ module Color
5
+
6
+ def greyscale(color = nil, intensity = 1.00)
7
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
8
+
9
+ color = OSX::CIColor.colorWithString("1.0 1.0 1.0 1.0") unless color
10
+
11
+ @original.color_monochrome :inputColor => color, :inputIntensity => intensity do |greyscale|
12
+ @target = greyscale
13
+ end
14
+ end
15
+
16
+ def sepia(intensity = 1.00)
17
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
18
+
19
+ @original.sepia_tone :inputIntensity => intensity do |sepia|
20
+ @target = sepia
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module RedArtisan
2
+ module CoreImage
3
+ module Filters
4
+ module Effects
5
+
6
+ def spotlight(position, points_at, brightness, concentration, color)
7
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
8
+
9
+ @original.spot_light :inputLightPosition => vector3(*position), :inputLightPointsAt => vector3(*points_at),
10
+ :inputBrightness => brightness, :inputConcentration => concentration, :inputColor => color do |spot|
11
+ @target = spot
12
+ end
13
+ end
14
+
15
+ def edges(intensity = 1.00)
16
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
17
+
18
+ @original.edges :inputIntensity => intensity do |edged|
19
+ @target = edged
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def vector3(x, y, w)
26
+ OSX::CIVector.vectorWithX_Y_Z(x, y, w)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module RedArtisan
2
+ module CoreImage
3
+ module Filters
4
+ module Perspective
5
+
6
+ def perspective(top_left, top_right, bottom_left, bottom_right)
7
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
8
+
9
+ @original.perspective_transform :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |transformed|
10
+ @target = transformed
11
+ end
12
+ end
13
+
14
+ def perspective_tiled(top_left, top_right, bottom_left, bottom_right)
15
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
16
+
17
+ @original.perspective_tile :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |tiled|
18
+ @target = tiled
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module RedArtisan
2
+ module CoreImage
3
+ module Filters
4
+ module Quality
5
+
6
+ def reduce_noise(level = 0.02, sharpness = 0.4)
7
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
8
+
9
+ @original.noise_reduction :inputNoiseLevel => level, :inputSharpness => sharpness do |noise_reduced|
10
+ @target = noise_reduced
11
+ end
12
+ end
13
+
14
+ def adjust_exposure(input_ev = 0.5)
15
+ create_core_image_context(@original.extent.size.width, @original.extent.size.height)
16
+
17
+ @original.exposure_adjust :inputEV => input_ev do |adjusted|
18
+ @target = adjusted
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end