attachment_zen 1.0.1

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.
@@ -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