fleximage 1.0.0
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/.gitignore +27 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +257 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/autotest.rb +5 -0
- data/fleximage.gemspec +235 -0
- data/init.rb +1 -0
- data/lib/dsl_accessor.rb +52 -0
- data/lib/fleximage.rb +59 -0
- data/lib/fleximage/aviary_controller.rb +75 -0
- data/lib/fleximage/blank.rb +70 -0
- data/lib/fleximage/helper.rb +41 -0
- data/lib/fleximage/image_proxy.rb +69 -0
- data/lib/fleximage/legacy_view.rb +63 -0
- data/lib/fleximage/model.rb +689 -0
- data/lib/fleximage/operator/background.rb +62 -0
- data/lib/fleximage/operator/base.rb +189 -0
- data/lib/fleximage/operator/border.rb +50 -0
- data/lib/fleximage/operator/crop.rb +58 -0
- data/lib/fleximage/operator/image_overlay.rb +85 -0
- data/lib/fleximage/operator/resize.rb +92 -0
- data/lib/fleximage/operator/shadow.rb +87 -0
- data/lib/fleximage/operator/text.rb +104 -0
- data/lib/fleximage/operator/trim.rb +14 -0
- data/lib/fleximage/operator/unsharp_mask.rb +36 -0
- data/lib/fleximage/rmagick_image_patch.rb +5 -0
- data/lib/fleximage/string_patch.rb +5 -0
- data/lib/fleximage/view.rb +57 -0
- data/tasks/fleximage_tasks.rake +154 -0
- data/test/fixtures/100x1.jpg +0 -0
- data/test/fixtures/100x100.jpg +0 -0
- data/test/fixtures/1x1.jpg +0 -0
- data/test/fixtures/1x100.jpg +0 -0
- data/test/fixtures/cmyk.jpg +0 -0
- data/test/fixtures/not_a_photo.xml +1 -0
- data/test/fixtures/photo.jpg +0 -0
- data/test/mock_file.rb +21 -0
- data/test/rails_root/app/controllers/application.rb +10 -0
- data/test/rails_root/app/controllers/avatars_controller.rb +85 -0
- data/test/rails_root/app/controllers/photo_bares_controller.rb +85 -0
- data/test/rails_root/app/controllers/photo_dbs_controller.rb +85 -0
- data/test/rails_root/app/controllers/photo_files_controller.rb +85 -0
- data/test/rails_root/app/helpers/application_helper.rb +3 -0
- data/test/rails_root/app/helpers/avatars_helper.rb +2 -0
- data/test/rails_root/app/helpers/photo_bares_helper.rb +2 -0
- data/test/rails_root/app/helpers/photo_dbs_helper.rb +2 -0
- data/test/rails_root/app/helpers/photo_files_helper.rb +2 -0
- data/test/rails_root/app/locales/de.yml +7 -0
- data/test/rails_root/app/locales/en.yml +8 -0
- data/test/rails_root/app/models/abstract.rb +8 -0
- data/test/rails_root/app/models/avatar.rb +4 -0
- data/test/rails_root/app/models/photo_bare.rb +7 -0
- data/test/rails_root/app/models/photo_custom_error.rb +10 -0
- data/test/rails_root/app/models/photo_db.rb +3 -0
- data/test/rails_root/app/models/photo_file.rb +3 -0
- data/test/rails_root/app/views/avatars/edit.html.erb +17 -0
- data/test/rails_root/app/views/avatars/index.html.erb +20 -0
- data/test/rails_root/app/views/avatars/new.html.erb +16 -0
- data/test/rails_root/app/views/avatars/show.html.erb +8 -0
- data/test/rails_root/app/views/layouts/avatars.html.erb +17 -0
- data/test/rails_root/app/views/layouts/photo_bares.html.erb +17 -0
- data/test/rails_root/app/views/layouts/photo_dbs.html.erb +17 -0
- data/test/rails_root/app/views/layouts/photo_files.html.erb +17 -0
- data/test/rails_root/app/views/photo_bares/edit.html.erb +12 -0
- data/test/rails_root/app/views/photo_bares/index.html.erb +18 -0
- data/test/rails_root/app/views/photo_bares/new.html.erb +11 -0
- data/test/rails_root/app/views/photo_bares/show.html.erb +3 -0
- data/test/rails_root/app/views/photo_dbs/edit.html.erb +32 -0
- data/test/rails_root/app/views/photo_dbs/index.html.erb +26 -0
- data/test/rails_root/app/views/photo_dbs/new.html.erb +31 -0
- data/test/rails_root/app/views/photo_dbs/show.html.erb +23 -0
- data/test/rails_root/app/views/photo_files/edit.html.erb +27 -0
- data/test/rails_root/app/views/photo_files/index.html.erb +24 -0
- data/test/rails_root/app/views/photo_files/new.html.erb +26 -0
- data/test/rails_root/app/views/photo_files/show.html.erb +18 -0
- data/test/rails_root/config/boot.rb +109 -0
- data/test/rails_root/config/database.yml +7 -0
- data/test/rails_root/config/environment.rb +66 -0
- data/test/rails_root/config/environments/development.rb +18 -0
- data/test/rails_root/config/environments/production.rb +19 -0
- data/test/rails_root/config/environments/sqlite3.rb +0 -0
- data/test/rails_root/config/environments/test.rb +22 -0
- data/test/rails_root/config/initializers/inflections.rb +10 -0
- data/test/rails_root/config/initializers/load_translations.rb +4 -0
- data/test/rails_root/config/initializers/mime_types.rb +5 -0
- data/test/rails_root/config/routes.rb +43 -0
- data/test/rails_root/db/migrate/001_create_photo_files.rb +15 -0
- data/test/rails_root/db/migrate/002_create_photo_dbs.rb +16 -0
- data/test/rails_root/db/migrate/003_create_photo_bares.rb +12 -0
- data/test/rails_root/db/migrate/004_create_avatars.rb +13 -0
- data/test/rails_root/public/.htaccess +40 -0
- data/test/rails_root/public/404.html +30 -0
- data/test/rails_root/public/422.html +30 -0
- data/test/rails_root/public/500.html +30 -0
- data/test/rails_root/public/dispatch.cgi +10 -0
- data/test/rails_root/public/dispatch.fcgi +24 -0
- data/test/rails_root/public/dispatch.rb +10 -0
- data/test/rails_root/public/favicon.ico +0 -0
- data/test/rails_root/public/images/rails.png +0 -0
- data/test/rails_root/public/index.html +277 -0
- data/test/rails_root/public/javascripts/application.js +2 -0
- data/test/rails_root/public/javascripts/controls.js +963 -0
- data/test/rails_root/public/javascripts/dragdrop.js +972 -0
- data/test/rails_root/public/javascripts/effects.js +1120 -0
- data/test/rails_root/public/javascripts/prototype.js +4225 -0
- data/test/rails_root/public/robots.txt +5 -0
- data/test/rails_root/public/stylesheets/scaffold.css +74 -0
- data/test/rails_root/vendor/plugins/fleximage/init.rb +2 -0
- data/test/test_helper.rb +81 -0
- data/test/unit/abstract_test.rb +20 -0
- data/test/unit/basic_model_test.rb +30 -0
- data/test/unit/blank_test.rb +23 -0
- data/test/unit/default_image_path_option_test.rb +16 -0
- data/test/unit/dsl_accessor_test.rb +120 -0
- data/test/unit/file_upload_from_local_test.rb +31 -0
- data/test/unit/file_upload_from_strings_test.rb +23 -0
- data/test/unit/file_upload_from_url_test.rb +35 -0
- data/test/unit/file_upload_to_db_test.rb +41 -0
- data/test/unit/i18n_messages_test.rb +49 -0
- data/test/unit/image_directory_option_test.rb +18 -0
- data/test/unit/image_proxy_test.rb +17 -0
- data/test/unit/image_storage_format_option_test.rb +31 -0
- data/test/unit/magic_columns_test.rb +30 -0
- data/test/unit/minimum_image_size_test.rb +56 -0
- data/test/unit/operator_base_test.rb +124 -0
- data/test/unit/operator_resize_test.rb +18 -0
- data/test/unit/preprocess_image_option_test.rb +21 -0
- data/test/unit/require_image_option_test.rb +30 -0
- data/test/unit/temp_image_test.rb +17 -0
- data/test/unit/use_creation_date_based_directories_option_test.rb +16 -0
- metadata +279 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Fleximage
|
2
|
+
module Operator
|
3
|
+
|
4
|
+
# Composites a transparent image over a colored backgroud
|
5
|
+
#
|
6
|
+
# It accepts the following options:
|
7
|
+
#
|
8
|
+
# * +color+: the color of the background image.
|
9
|
+
# Use an RMagick named color or use the +color+ method in Fleximage::Controller, or a
|
10
|
+
# Magick::Pixel object.
|
11
|
+
#
|
12
|
+
# * +size+: The size of the background image, in Fleximage *size* format.
|
13
|
+
# By default the background image is the same size as the foreground image
|
14
|
+
#
|
15
|
+
# * +alignment+: A symbol that tells Fleximage where to put the foreground image on
|
16
|
+
# top of the background image. Can be any of the following:
|
17
|
+
# <tt>:center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left</tt>.
|
18
|
+
# Default is :+center+
|
19
|
+
#
|
20
|
+
# * +offset+: the number of pixels to offset the foreground image from it's :+alignment+ anchor, in FlexImage
|
21
|
+
# *size* format. Useful to give a bit a space between your image and the edge of the background, for instance.
|
22
|
+
# *NOTE:* Due to some unexpected (buggy?) RMagick behaviour :+offset+ will work strangely
|
23
|
+
# if :+alignment+ is set to a corner non-corner value, such as :+top+ or :+center+. Using :+offset+ in
|
24
|
+
# these cases will force the overlay into a corner anyway.
|
25
|
+
#
|
26
|
+
# * +blending+: The blending mode governs how the foreground image gets composited onto the background. You can
|
27
|
+
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
|
28
|
+
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
|
29
|
+
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
|
30
|
+
# +MultiplyCompositeOp+ becomes :+multiply+, and +CopyBlackCompositeOp+ becomes :+copy_black+.
|
31
|
+
|
32
|
+
class Background < Operator::Base
|
33
|
+
def operate(options = {})
|
34
|
+
options = options.symbolize_keys
|
35
|
+
|
36
|
+
#default to a white background if the color option is not set
|
37
|
+
color = options[:color] || 'white'
|
38
|
+
|
39
|
+
#use the existing image's size if the size option is not set
|
40
|
+
width, height = options.key?(:size) ? size_to_xy(options[:size]) : [@image.columns, @image.rows]
|
41
|
+
|
42
|
+
#create the background image onto which we will composite the foreground image
|
43
|
+
bg = Magick::Image.new(width, height) do
|
44
|
+
self.background_color = color
|
45
|
+
self.format = 'PNG'
|
46
|
+
end
|
47
|
+
|
48
|
+
#prepare attributes for composite operation
|
49
|
+
args = []
|
50
|
+
args << @image
|
51
|
+
args << symbol_to_gravity(options[:alignment] || :center)
|
52
|
+
args += size_to_xy(options[:offset]) if options[:offset]
|
53
|
+
args << symbol_to_blending_mode(options[:blending] || :over)
|
54
|
+
|
55
|
+
#composite the foreground image onto the background
|
56
|
+
bg.composite!(*args)
|
57
|
+
|
58
|
+
return bg
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module Fleximage
|
2
|
+
module Operator
|
3
|
+
|
4
|
+
class BadOperatorResult < Exception #:nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
class OperationNotImplemented < Exception #:nodoc:
|
8
|
+
end
|
9
|
+
|
10
|
+
# The Operator::Base class is what all other Operator classes inherit from.
|
11
|
+
# To write your own Operator class, simply inherit from this class, and
|
12
|
+
# implement your own operate methods, with your own arguments. Just
|
13
|
+
# return a new RMagick image object that represents the new image, and
|
14
|
+
# the model will be updated automatically.
|
15
|
+
#
|
16
|
+
# You have access to a few instance variables in the operate method:
|
17
|
+
#
|
18
|
+
# * @image : The current image from the model. Use this is a starting
|
19
|
+
# point for all transformations.
|
20
|
+
# * @model : The model instance that this image transformation is happenining
|
21
|
+
# in. Use it to get data out of your model for display in your image.
|
22
|
+
class Base
|
23
|
+
# Create a operator, capturing the model object to operate on
|
24
|
+
def initialize(proxy, image, model_obj) #:nodoc:
|
25
|
+
@proxy = proxy
|
26
|
+
@image = image
|
27
|
+
@model = model_obj
|
28
|
+
end
|
29
|
+
|
30
|
+
# Start the operation
|
31
|
+
def execute(*args) #:nodoc:
|
32
|
+
# Get the result of the Operators #operate method
|
33
|
+
result = operate(*args)
|
34
|
+
|
35
|
+
# Ensure that the result is an RMagick:Image object
|
36
|
+
unless result.is_a?(Magick::Image)
|
37
|
+
raise BadOperatorResult, "expected #{self.class}#operate to return an instance of Magick::Image. \n"+
|
38
|
+
"Got instance of #{result.class} instead."
|
39
|
+
end
|
40
|
+
|
41
|
+
# Save the result to the operator proxy
|
42
|
+
@proxy.image = result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Perform the operation. Override this method in your Operator::Base subclasses
|
46
|
+
# in order to write your own image operators.
|
47
|
+
def operate(*args)
|
48
|
+
raise OperationNotImplemented, "Override this method in your own subclass."
|
49
|
+
end
|
50
|
+
|
51
|
+
# ---
|
52
|
+
# - SUPPORT METHODS
|
53
|
+
# ---
|
54
|
+
|
55
|
+
# Allows access to size conversion globally. See size_to_xy for a more detailed explanation
|
56
|
+
def self.size_to_xy(size)
|
57
|
+
case
|
58
|
+
when size.is_a?(Array) && size.size == 2 # [320, 240]
|
59
|
+
size
|
60
|
+
|
61
|
+
when size.to_s.include?('x') # "320x240"
|
62
|
+
size.split('x').collect(&:to_i)
|
63
|
+
|
64
|
+
else # Anything else, convert the object to an integer and assume square dimensions
|
65
|
+
[size.to_i, size.to_i]
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# This method will return a valid color Magick::Pixel object. It will also auto adjust
|
71
|
+
# for the bit depth of your ImageMagick configuration.
|
72
|
+
#
|
73
|
+
# Usage:
|
74
|
+
#
|
75
|
+
# color('red') #=> Magick::Pixel with a red color
|
76
|
+
# color(0, 255, 0) #=> Magick::Pixel with a green color
|
77
|
+
# color(0, 0, 255, 47) #=> Magick::Pixel with a blue clolor and slightly transparent
|
78
|
+
#
|
79
|
+
# # on an ImageMagick with a QuantumDepth of 16
|
80
|
+
# color(0, 255, 0) #=> Magick::Pixel with rgb of (0, 65535, 0) (auto adjusted to 16bit channels)
|
81
|
+
#
|
82
|
+
def self.color(*args)
|
83
|
+
if args.size == 1 && args.first.is_a?(String)
|
84
|
+
args.first
|
85
|
+
else
|
86
|
+
|
87
|
+
# adjust color to proper bit depth
|
88
|
+
if Magick::QuantumDepth != 8
|
89
|
+
max = case Magick::QuantumDepth
|
90
|
+
when 16
|
91
|
+
65_535
|
92
|
+
when 32
|
93
|
+
4_294_967_295
|
94
|
+
end
|
95
|
+
|
96
|
+
args.map! do |value|
|
97
|
+
(value.to_f/255 * max).to_i
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# create the pixel
|
102
|
+
Magick::Pixel.new(*args)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def color(*args)
|
107
|
+
self.class.color(*args)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Converts a size object to an [x,y] array. Acceptible formats are:
|
111
|
+
#
|
112
|
+
# * 10
|
113
|
+
# * "10"
|
114
|
+
# * "10x20"
|
115
|
+
# * [10, 20]
|
116
|
+
#
|
117
|
+
# Usage:
|
118
|
+
#
|
119
|
+
# x, y = size_to_xy("10x20")
|
120
|
+
def size_to_xy(size)
|
121
|
+
self.class.size_to_xy size
|
122
|
+
end
|
123
|
+
|
124
|
+
# Scale the image, respecting aspect ratio.
|
125
|
+
# Operation will happen in the main <tt>@image</tt> unless you supply the +img+ argument
|
126
|
+
# to operate on instead.
|
127
|
+
def scale(size, img = @image)
|
128
|
+
img.change_geometry!(size_to_xy(size).join('x')) do |cols, rows, _img|
|
129
|
+
cols = 1 if cols < 1
|
130
|
+
rows = 1 if rows < 1
|
131
|
+
_img.resize!(cols, rows)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Scale to the desired size and crop edges off to get the exact dimensions needed.
|
136
|
+
# Operation will happen in the main <tt>@image</tt> unless you supply the +img+ argument
|
137
|
+
# to operate on instead.
|
138
|
+
def scale_and_crop(size, img = @image)
|
139
|
+
img.crop_resized!(*size_to_xy(size))
|
140
|
+
end
|
141
|
+
|
142
|
+
# Resize the image, with no respect to aspect ratio.
|
143
|
+
# Operation will happen in the main <tt>@image</tt> unless you supply the +img+ argument
|
144
|
+
# to operate on instead.
|
145
|
+
def stretch(size, img = @image)
|
146
|
+
img.resize!(*size_to_xy(size))
|
147
|
+
end
|
148
|
+
|
149
|
+
# Convert a symbol to an RMagick blending mode.
|
150
|
+
#
|
151
|
+
# The blending mode governs how the overlay gets composited onto the image. You can
|
152
|
+
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
|
153
|
+
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
|
154
|
+
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
|
155
|
+
# +MultiplyCompositeOp+ becomes :+multiply+, and +CopyBlackCompositeOp+ becomes :+copy_black+.
|
156
|
+
def symbol_to_blending_mode(mode)
|
157
|
+
"Magick::#{mode.to_s.camelize}CompositeOp".constantize
|
158
|
+
rescue NameError
|
159
|
+
raise ArgumentError, ":#{mode} is not a valid blending mode."
|
160
|
+
end
|
161
|
+
|
162
|
+
def symbol_to_gravity(gravity_name)
|
163
|
+
gravity = GRAVITIES[gravity_name]
|
164
|
+
|
165
|
+
if gravity
|
166
|
+
gravity
|
167
|
+
else
|
168
|
+
raise ArgumentError, ":#{gravity_name} is not a valid gravity name.\n\nValid names are :center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
end # Base
|
174
|
+
|
175
|
+
# Conversion table for mapping alignment symbols to their equivalent RMagick gravity constants.
|
176
|
+
GRAVITIES = {
|
177
|
+
:center => Magick::CenterGravity,
|
178
|
+
:top => Magick::NorthGravity,
|
179
|
+
:top_right => Magick::NorthEastGravity,
|
180
|
+
:right => Magick::EastGravity,
|
181
|
+
:bottom_right => Magick::SouthEastGravity,
|
182
|
+
:bottom => Magick::SouthGravity,
|
183
|
+
:bottom_left => Magick::SouthWestGravity,
|
184
|
+
:left => Magick::WestGravity,
|
185
|
+
:top_left => Magick::NorthWestGravity,
|
186
|
+
} unless defined?(GRAVITIES)
|
187
|
+
|
188
|
+
end # Operator
|
189
|
+
end # Fleximage
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Fleximage
|
2
|
+
module Operator
|
3
|
+
|
4
|
+
# Add a border to the outside of the image
|
5
|
+
#
|
6
|
+
# image.border(options = {})
|
7
|
+
#
|
8
|
+
# Use the following keys in the +options+ hash:
|
9
|
+
#
|
10
|
+
# * +size+: Width of the border on each side. You can use a 2 dimensional value ('5x10') if you want
|
11
|
+
# different widths for the sides and top borders, but a single integer will apply the same border on
|
12
|
+
# all sides.
|
13
|
+
#
|
14
|
+
# * +color+: the color of the border. Use an RMagick named color or use the +color+ method in
|
15
|
+
# FlexImage::Controller, or a Magick::Pixel object.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
#
|
19
|
+
# @photo.operate do |image|
|
20
|
+
# # Defaults
|
21
|
+
# image.border(
|
22
|
+
# :size => 10,
|
23
|
+
# :color => 'white' # or color(255, 255, 255)
|
24
|
+
# )
|
25
|
+
#
|
26
|
+
# # Big, pink and wide
|
27
|
+
# image.border(
|
28
|
+
# :size => '200x100',
|
29
|
+
# :color => color(255, 128, 128)
|
30
|
+
# )
|
31
|
+
# end
|
32
|
+
class Border < Operator::Base
|
33
|
+
def operate(options = {})
|
34
|
+
options = options.symbolize_keys if options.respond_to?(:symbolize_keys)
|
35
|
+
defaults = {
|
36
|
+
:size => '10',
|
37
|
+
:color => 'white'
|
38
|
+
}
|
39
|
+
options = options.is_a?(Hash) ? defaults.update(options) : defaults
|
40
|
+
|
41
|
+
# Get border size
|
42
|
+
options[:size] = size_to_xy(options[:size])
|
43
|
+
|
44
|
+
# apply border
|
45
|
+
@image.border!(options[:size][0], options[:size][1], options[:color])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Fleximage
|
2
|
+
module Operator
|
3
|
+
|
4
|
+
# Crops the image without doing any resizing first. The operation crops from the :+from+ coordinate,
|
5
|
+
# and returns an image of size :+size+ down and right from there.
|
6
|
+
#
|
7
|
+
# image.crop(options = {})
|
8
|
+
#
|
9
|
+
# Use the following keys in the +options+ hash:
|
10
|
+
#
|
11
|
+
# * +gravity+: Select gravity for the crop. Default is :top_left (Magick::NorthWestGravity)
|
12
|
+
# Choose from GRAVITITES constant defined in base.rb.
|
13
|
+
# * +from+: coorinates for the upper left corner of resulting image.
|
14
|
+
# * +size+: The size of the resulting image, going down and to the right of the :+from+ coordinate.
|
15
|
+
#
|
16
|
+
# size and from options are *required*.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# @photo.operate do |image|
|
21
|
+
# image.crop(
|
22
|
+
# :from => '100x50',
|
23
|
+
# :size => '500x350'
|
24
|
+
# )
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# or
|
28
|
+
#
|
29
|
+
# @photo.operate do |image|
|
30
|
+
# image.crop(
|
31
|
+
# :gravity => :center,
|
32
|
+
# :from => '100x50',
|
33
|
+
# :size => '500x350'
|
34
|
+
# )
|
35
|
+
# end
|
36
|
+
class Crop < Operator::Base
|
37
|
+
def operate(options = {})
|
38
|
+
options = options.symbolize_keys
|
39
|
+
options.reverse_merge!(:gravity => :top_left)
|
40
|
+
|
41
|
+
# required integer keys
|
42
|
+
[:from, :size].each do |key|
|
43
|
+
raise ArgumentError, ":#{key} must be included in crop options" unless options[key]
|
44
|
+
options[key] = size_to_xy(options[key])
|
45
|
+
end
|
46
|
+
|
47
|
+
# width and height must not be zero
|
48
|
+
options[:size].each do |dimension|
|
49
|
+
raise ArgumentError, ":size must not be zero for X or Y" if dimension.zero?
|
50
|
+
end
|
51
|
+
|
52
|
+
# crop
|
53
|
+
@image.crop!(symbol_to_gravity(options[:gravity]), options[:from][0], options[:from][1], options[:size][0], options[:size][1], true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Fleximage
|
2
|
+
module Operator
|
3
|
+
|
4
|
+
# Adds an overlay to the base image. It's useful for things like attaching a logo,
|
5
|
+
# watermark, or even a border to the image. It will work best with a 24-bit PNG with
|
6
|
+
# alpha channel since it will properly deal with partial transparency.
|
7
|
+
#
|
8
|
+
# image.resize(image_overlay_path, options = {})
|
9
|
+
#
|
10
|
+
# +image_overlay_path+ is the path, relative to +RAILS_ROOT+ where the image you want superimposed
|
11
|
+
# can be found.
|
12
|
+
#
|
13
|
+
# Use the following keys in the +options+ hash:
|
14
|
+
#
|
15
|
+
# * +size+: The size of the overlayed image, as <tt>'123x456'</tt> or <tt>[123, 456]</tt>.
|
16
|
+
# By default the overlay is not resized before compositing.
|
17
|
+
# Use this options if you want to resize the overlay, perhaps to have a small
|
18
|
+
# logo on thumbnails and a big logo on full size images. Other than just numerical dimensions, the
|
19
|
+
# size parameter takes 2 special values :+scale_to_fit+ and :+stretch_to_fit+. :+scale_to_fit+ will
|
20
|
+
# make the overlay fit as
|
21
|
+
# much as it can inside the image without changing the aspect ratio. :+stretch_to_fit+
|
22
|
+
# will make the overlay the exact same size as the image but with a distorted aspect ratio to make
|
23
|
+
# it fit. :+stretch_to_fit+ is designed to add border to images.
|
24
|
+
#
|
25
|
+
# * +alignment+: A symbol that tells Fleximage where to put the overlay. Can be any of the following:
|
26
|
+
# <tt>:center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left</tt>.
|
27
|
+
# Default is :+center+
|
28
|
+
#
|
29
|
+
# * +offset+: the number of pixels to offset the overlay from it's :+alignment+ anchor, in
|
30
|
+
# <tt>'123x456'</tt> or <tt>[123, 456]</tt> format. Useful to give a bit a space between your logo
|
31
|
+
# and the edge of the image, for instance.
|
32
|
+
# *NOTE:* Due to some unexpected (buggy?) RMagick behaviour :+offset+ will work strangely
|
33
|
+
# if :+alignment+ is set to a non-corner value, such as :+top+ or :+center+. Using :+offset+ in
|
34
|
+
# these cases will force the overlay into a corner anyway.
|
35
|
+
#
|
36
|
+
# * +blending+: The blending mode governs how the overlay gets composited onto the image. You can
|
37
|
+
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
|
38
|
+
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
|
39
|
+
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
|
40
|
+
# +MultiplyCompositeOp+ becomes :+multiply+, and +CopyBlackCompositeOp+ becomes :+copy_black+.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# @photo.operate do |image|
|
45
|
+
# image.image_overlay('images/my_logo_with_alpha.png',
|
46
|
+
# :size => '25x25',
|
47
|
+
# :alignment => :top_right,
|
48
|
+
# :blending => :screen
|
49
|
+
# )
|
50
|
+
# end
|
51
|
+
class ImageOverlay < Operator::Base
|
52
|
+
def operate(image_overlay_path, options = {})
|
53
|
+
options = options.symbolize_keys
|
54
|
+
|
55
|
+
#load overlay
|
56
|
+
overlay = Magick::Image.read(image_overlay_path).first
|
57
|
+
|
58
|
+
#resize overlay
|
59
|
+
if options[:size]
|
60
|
+
if options[:size] == :scale_to_fit || options[:size] == :stretch_to_fit
|
61
|
+
x, y = @image.columns, @image.rows
|
62
|
+
else
|
63
|
+
x, y = size_to_xy(options[:size])
|
64
|
+
end
|
65
|
+
|
66
|
+
method = options[:size] == :stretch_to_fit ? :stretch : :scale
|
67
|
+
send(method, [x, y], overlay)
|
68
|
+
end
|
69
|
+
|
70
|
+
#prepare arguments for composite!
|
71
|
+
args = []
|
72
|
+
args << overlay #overlay image
|
73
|
+
args << symbol_to_gravity(options[:alignment] || :center) #gravity
|
74
|
+
args += size_to_xy(options[:offset]) if options[:offset] #offset
|
75
|
+
args << symbol_to_blending_mode(options[:blending] || :over) #compositing mode
|
76
|
+
|
77
|
+
#composite
|
78
|
+
@image.composite!(*args)
|
79
|
+
|
80
|
+
return @image
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|