image_processing 0.9.0 → 0.10.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.
Potentially problematic release.
This version of image_processing might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +45 -374
- data/lib/image_processing/chainable.rb +63 -0
- data/lib/image_processing/mini_magick/deprecated_api.rb +119 -0
- data/lib/image_processing/mini_magick.rb +67 -213
- data/lib/image_processing/pipeline.rb +59 -0
- data/lib/image_processing/version.rb +1 -1
- data/lib/image_processing/vips.rb +91 -5
- data/lib/image_processing.rb +6 -0
- metadata +5 -6
- data/lib/image_processing/vips/chainable.rb +0 -61
- data/lib/image_processing/vips/color.rb +0 -584
- data/lib/image_processing/vips/pipeline.rb +0 -30
- data/lib/image_processing/vips/processor.rb +0 -83
@@ -0,0 +1,119 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module ImageProcessing
|
5
|
+
module MiniMagick
|
6
|
+
module DeprecatedApi
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.deprecated_processing_method(name, &body)
|
12
|
+
define_method(name) do |*args, &block|
|
13
|
+
return ImageProcessing::MiniMagick.send(name, *args, &block) if self != ImageProcessing::MiniMagick
|
14
|
+
return super(*args, &block) unless args.first.respond_to?(:read)
|
15
|
+
|
16
|
+
warn "[IMAGE_PROCESSING DEPRECATION WARNING] This API is deprecated and will be removed in ImageProcessing 1.0. Please use the new chainable API."
|
17
|
+
|
18
|
+
file = args.shift
|
19
|
+
|
20
|
+
if file.respond_to?(:path)
|
21
|
+
instance_exec(file, *args, block, &body)
|
22
|
+
else
|
23
|
+
Utils.copy_to_tempfile(file) do |tempfile|
|
24
|
+
send(name, tempfile, *args, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method("#{name}!") do |*args, &block|
|
30
|
+
return ImageProcessing::MiniMagick.send(name, *args, &block) if self != ImageProcessing::MiniMagick
|
31
|
+
return super(*args, &block) unless args.first.respond_to?(:read)
|
32
|
+
|
33
|
+
processed = send(name, *args, &block)
|
34
|
+
source = args.first
|
35
|
+
|
36
|
+
if name == :convert
|
37
|
+
File.delete(source.path)
|
38
|
+
else
|
39
|
+
processed.close
|
40
|
+
FileUtils.mv processed.path, source.path
|
41
|
+
source.open if source.is_a?(Tempfile)
|
42
|
+
end
|
43
|
+
|
44
|
+
source
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
deprecated_processing_method :resize_to_limit do |file, *args, block|
|
49
|
+
source(file)
|
50
|
+
.custom(&block)
|
51
|
+
.resize_to_limit!(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
deprecated_processing_method :resize_to_fit do |file, *args, block|
|
55
|
+
source(file)
|
56
|
+
.custom(&block)
|
57
|
+
.resize_to_fit!(*args)
|
58
|
+
end
|
59
|
+
|
60
|
+
deprecated_processing_method :resize_to_fill do |file, *args, block|
|
61
|
+
source(file)
|
62
|
+
.custom(&block)
|
63
|
+
.resize_to_fill!(*args)
|
64
|
+
end
|
65
|
+
|
66
|
+
deprecated_processing_method :resize_and_pad do |file, *args, block|
|
67
|
+
source(file)
|
68
|
+
.custom(&block)
|
69
|
+
.resize_and_pad!(*args)
|
70
|
+
end
|
71
|
+
|
72
|
+
deprecated_processing_method :convert do |file, format, page = nil, block|
|
73
|
+
source(file, page: page)
|
74
|
+
.custom(&block)
|
75
|
+
.convert!(format)
|
76
|
+
end
|
77
|
+
|
78
|
+
deprecated_processing_method :auto_orient do |file, *args, block|
|
79
|
+
source(file)
|
80
|
+
.custom(&block)
|
81
|
+
.auto_orient!(*args)
|
82
|
+
end
|
83
|
+
|
84
|
+
deprecated_processing_method :resample do |file, width, height, block|
|
85
|
+
source(file)
|
86
|
+
.custom(&block)
|
87
|
+
.resample!("#{width}x#{height}")
|
88
|
+
end
|
89
|
+
|
90
|
+
deprecated_processing_method :crop do |file, width, height, x_offset, y_offset, block|
|
91
|
+
source(file)
|
92
|
+
.custom(&block)
|
93
|
+
.crop!("#{width}x#{height}+#{x_offset}+#{y_offset}")
|
94
|
+
end
|
95
|
+
|
96
|
+
deprecated_processing_method :corrupted? do |file, block|
|
97
|
+
valid_image?(file)
|
98
|
+
end
|
99
|
+
|
100
|
+
module Utils
|
101
|
+
module_function
|
102
|
+
|
103
|
+
def copy_to_tempfile(io)
|
104
|
+
extension = File.extname(io.path) if io.respond_to?(:path)
|
105
|
+
tempfile = Tempfile.new(["image_processing", extension.to_s], binmode: true)
|
106
|
+
|
107
|
+
IO.copy_stream(io, tempfile)
|
108
|
+
|
109
|
+
io.rewind
|
110
|
+
tempfile.open # refresh content
|
111
|
+
|
112
|
+
yield tempfile
|
113
|
+
ensure
|
114
|
+
tempfile.close! if tempfile
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -1,236 +1,90 @@
|
|
1
|
-
|
1
|
+
gem "mini_magick", "~> 4.0"
|
2
2
|
require "mini_magick"
|
3
|
-
require "tempfile"
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
4
|
+
require "image_processing"
|
5
|
+
require "image_processing/mini_magick/deprecated_api"
|
8
6
|
|
9
7
|
module ImageProcessing
|
10
8
|
module MiniMagick
|
11
|
-
def self.
|
12
|
-
|
13
|
-
|
9
|
+
def self.valid_image?(file)
|
10
|
+
::MiniMagick::Tool::Convert.new do |convert|
|
11
|
+
convert.regard_warnings
|
12
|
+
convert << file.path
|
13
|
+
convert << "null:"
|
14
14
|
end
|
15
|
-
|
15
|
+
true
|
16
|
+
rescue ::MiniMagick::Error
|
17
|
+
false
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def convert!(image, format, page = nil, &block)
|
28
|
-
with_minimagick(image) do |img|
|
29
|
-
img.format(format.downcase, page, &block)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
nondestructive_alias :convert, :convert!
|
33
|
-
|
34
|
-
# Adjusts the image so that its orientation is suitable for viewing.
|
35
|
-
#
|
36
|
-
# @see http://www.imagemagick.org/script/command-line-options.php#auto-orient
|
37
|
-
# @param [MiniMagick::Image] image the image to convert
|
38
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
39
|
-
# @return [File, Tempfile]
|
40
|
-
def auto_orient!(image)
|
41
|
-
with_minimagick(image) do |img|
|
42
|
-
img.combine_options do |cmd|
|
43
|
-
yield cmd if block_given?
|
44
|
-
cmd.auto_orient
|
20
|
+
class Processor
|
21
|
+
IMAGE_CLASS = ::MiniMagick::Tool
|
22
|
+
TRANSPARENT = "rgba(255,255,255,0.0)"
|
23
|
+
|
24
|
+
def apply_operation(name, magick, *args)
|
25
|
+
if respond_to?(name)
|
26
|
+
public_send(name, magick, *args)
|
27
|
+
else
|
28
|
+
magick.send(name, *args)
|
45
29
|
end
|
46
30
|
end
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# Resize the image to fit within the specified dimensions while retaining
|
51
|
-
# the original aspect ratio. Will only resize the image if it is larger
|
52
|
-
# than the specified dimensions. The resulting image may be shorter or
|
53
|
-
# narrower than specified in either dimension but will not be larger than
|
54
|
-
# the specified values.
|
55
|
-
#
|
56
|
-
# @param [MiniMagick::Image] image the image to convert
|
57
|
-
# @param [#to_s] width the maximum width
|
58
|
-
# @param [#to_s] height the maximum height
|
59
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
60
|
-
# @return [File, Tempfile]
|
61
|
-
def resize_to_limit!(image, width, height)
|
62
|
-
with_minimagick(image) do |img|
|
63
|
-
img.combine_options do |cmd|
|
64
|
-
yield cmd if block_given?
|
65
|
-
cmd.resize "#{width}x#{height}>"
|
66
|
-
end
|
31
|
+
|
32
|
+
def resize_to_limit(magick, width, height)
|
33
|
+
magick.resize "#{width}x#{height}>"
|
67
34
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# Resize the image to fit within the specified dimensions while retaining
|
72
|
-
# the original aspect ratio. The image may be shorter or narrower than
|
73
|
-
# specified in the smaller dimension but will not be larger than the
|
74
|
-
# specified values.
|
75
|
-
#
|
76
|
-
# @param [MiniMagick::Image] image the image to convert
|
77
|
-
# @param [#to_s] width the width to fit into
|
78
|
-
# @param [#to_s] height the height to fit into
|
79
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
80
|
-
# @return [File, Tempfile]
|
81
|
-
def resize_to_fit!(image, width, height)
|
82
|
-
with_minimagick(image) do |img|
|
83
|
-
img.combine_options do |cmd|
|
84
|
-
yield cmd if block_given?
|
85
|
-
cmd.resize "#{width}x#{height}"
|
86
|
-
end
|
35
|
+
|
36
|
+
def resize_to_fit(magick, width, height)
|
37
|
+
magick.resize "#{width}x#{height}"
|
87
38
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
# The resulting image will always be exactly as large as the specified
|
95
|
-
# dimensions.
|
96
|
-
#
|
97
|
-
# By default, the center part of the image is kept, and the remainder
|
98
|
-
# cropped off, but this can be changed via the `gravity` option.
|
99
|
-
#
|
100
|
-
# @param [MiniMagick::Image] image the image to convert
|
101
|
-
# @param [#to_s] width the width to fill out
|
102
|
-
# @param [#to_s] height the height to fill out
|
103
|
-
# @param [String] gravity which part of the image to focus on
|
104
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
105
|
-
# @return [File, Tempfile]
|
106
|
-
# @see http://www.imagemagick.org/script/command-line-options.php#gravity
|
107
|
-
def resize_to_fill!(image, width, height, gravity: "Center")
|
108
|
-
with_minimagick(image) do |img|
|
109
|
-
img.combine_options do |cmd|
|
110
|
-
yield cmd if block_given?
|
111
|
-
cmd.resize "#{width}x#{height}^"
|
112
|
-
cmd.gravity gravity
|
113
|
-
cmd.background "rgba(255,255,255,0.0)" # transparent
|
114
|
-
cmd.extent "#{width}x#{height}"
|
115
|
-
end
|
39
|
+
|
40
|
+
def resize_to_fill(magick, width, height, gravity: "Center")
|
41
|
+
magick.resize "#{width}x#{height}^"
|
42
|
+
magick.gravity gravity
|
43
|
+
magick.background TRANSPARENT
|
44
|
+
magick.extent "#{width}x#{height}"
|
116
45
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
#
|
126
|
-
# The resulting image will always be exactly as large as the specified
|
127
|
-
# dimensions.
|
128
|
-
#
|
129
|
-
# By default, the image will be placed in the center but this can be
|
130
|
-
# changed via the `gravity` option.
|
131
|
-
#
|
132
|
-
# @param [MiniMagick::image] image the image to convert
|
133
|
-
# @param [#to_s] width the width to fill out
|
134
|
-
# @param [#to_s] height the height to fill out
|
135
|
-
# @param [string] background the color to use as a background
|
136
|
-
# @param [string] gravity which part of the image to focus on
|
137
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
138
|
-
# @return [File, Tempfile]
|
139
|
-
# @see http://www.imagemagick.org/script/color.php
|
140
|
-
# @see http://www.imagemagick.org/script/command-line-options.php#gravity
|
141
|
-
def resize_and_pad!(image, width, height, background: "transparent", gravity: "Center")
|
142
|
-
with_minimagick(image) do |img|
|
143
|
-
img.combine_options do |cmd|
|
144
|
-
yield cmd if block_given?
|
145
|
-
cmd.resize "#{width}x#{height}"
|
146
|
-
if background == "transparent"
|
147
|
-
cmd.background "rgba(255, 255, 255, 0.0)"
|
148
|
-
else
|
149
|
-
cmd.background background
|
150
|
-
end
|
151
|
-
cmd.gravity gravity
|
152
|
-
cmd.extent "#{width}x#{height}"
|
153
|
-
end
|
46
|
+
|
47
|
+
def resize_and_pad(magick, width, height, background: TRANSPARENT, gravity: "Center")
|
48
|
+
background = TRANSPARENT if background == "transparent"
|
49
|
+
|
50
|
+
magick.resize "#{width}x#{height}"
|
51
|
+
magick.background background
|
52
|
+
magick.gravity gravity
|
53
|
+
magick.extent "#{width}x#{height}"
|
154
54
|
end
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
# Resample the image to fit within the specified resolution while retaining
|
159
|
-
# the original image size.
|
160
|
-
#
|
161
|
-
# The resulting image will always be the same pixel size as the source with
|
162
|
-
# an adjusted resolution dimensions.
|
163
|
-
#
|
164
|
-
# @param [MiniMagick::Image] image the image to convert
|
165
|
-
# @param [#to_s] width the dpi width
|
166
|
-
# @param [#to_s] height the dpi height
|
167
|
-
# @yield [MiniMagick::Tool::Mogrify, MiniMagick::Tool::Convert]
|
168
|
-
# @return [File, Tempfile]
|
169
|
-
# @see http://www.imagemagick.org/script/command-line-options.php#resample
|
170
|
-
def resample!(image, width, height)
|
171
|
-
with_minimagick(image) do |img|
|
172
|
-
img.combine_options do |cmd|
|
173
|
-
yield cmd if block_given?
|
174
|
-
cmd.resample "#{width}x#{height}"
|
175
|
-
end
|
55
|
+
|
56
|
+
def append(magick, *args)
|
57
|
+
magick.merge! args
|
176
58
|
end
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
def crop!(image, width, height, x_offset = 0, y_offset = 0, gravity: "NorthWest")
|
191
|
-
with_minimagick(image) do |img|
|
192
|
-
img.combine_options do |cmd|
|
193
|
-
yield cmd if block_given?
|
194
|
-
cmd.gravity gravity
|
195
|
-
cmd.crop "#{width}x#{height}+#{x_offset}+#{y_offset}"
|
59
|
+
|
60
|
+
def load_image(path_or_magick, page: nil, geometry: nil, fail: true, auto_orient: true)
|
61
|
+
if path_or_magick.is_a?(::MiniMagick::Tool)
|
62
|
+
magick = path_or_magick
|
63
|
+
else
|
64
|
+
source_path = path_or_magick
|
65
|
+
magick = ::MiniMagick::Tool::Convert.new
|
66
|
+
|
67
|
+
input_path = source_path
|
68
|
+
input_path += "[#{page}]" if page
|
69
|
+
input_path += "[#{geometry}]" if geometry
|
70
|
+
|
71
|
+
magick << input_path
|
196
72
|
end
|
73
|
+
|
74
|
+
magick.regard_warnings if fail
|
75
|
+
magick.auto_orient if auto_orient
|
76
|
+
|
77
|
+
magick
|
197
78
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
#
|
203
|
-
# @param [File] image
|
204
|
-
# @return [Boolean]
|
205
|
-
def corrupted?(image)
|
206
|
-
::MiniMagick::Tool::Identify.new do |identify|
|
207
|
-
identify.verbose
|
208
|
-
identify.regard_warnings
|
209
|
-
identify << image.path
|
79
|
+
|
80
|
+
def save_image(magick, destination_path, **options)
|
81
|
+
magick << destination_path
|
82
|
+
magick.call
|
210
83
|
end
|
211
|
-
false
|
212
|
-
rescue ::MiniMagick::Error
|
213
|
-
true
|
214
84
|
end
|
215
85
|
|
216
|
-
|
217
|
-
# and at the end return a File object.
|
218
|
-
def with_minimagick(image)
|
219
|
-
image = ::MiniMagick::Image.new(image.path, image)
|
220
|
-
yield image
|
221
|
-
tempfile = image.instance_variable_get("@tempfile")
|
222
|
-
tempfile.open if tempfile.is_a?(Tempfile)
|
223
|
-
tempfile
|
224
|
-
end
|
86
|
+
extend Chainable
|
225
87
|
|
226
|
-
|
227
|
-
# IO object that responds to `#read(length = nil, outbuf = nil)`.
|
228
|
-
def _copy_to_tempfile(file)
|
229
|
-
extension = File.extname(file.path) if file.respond_to?(:path)
|
230
|
-
tempfile = Tempfile.new(["image_processing-mini_magick", extension.to_s], binmode: true)
|
231
|
-
IO.copy_stream(file, tempfile.path)
|
232
|
-
file.rewind
|
233
|
-
tempfile
|
234
|
-
end
|
88
|
+
include DeprecatedApi
|
235
89
|
end
|
236
90
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
|
3
|
+
module ImageProcessing
|
4
|
+
class Pipeline
|
5
|
+
include Chainable
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@default_options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def call!(save: true, destination: nil)
|
12
|
+
fail Error, "source file is not provided" unless default_options[:source]
|
13
|
+
|
14
|
+
image_class = default_options[:processor]::IMAGE_CLASS
|
15
|
+
|
16
|
+
if default_options[:source].is_a?(image_class)
|
17
|
+
source = default_options[:source]
|
18
|
+
elsif default_options[:source].is_a?(String)
|
19
|
+
source = default_options[:source]
|
20
|
+
elsif default_options[:source].respond_to?(:path)
|
21
|
+
source = default_options[:source].path
|
22
|
+
elsif default_options[:source].respond_to?(:to_path)
|
23
|
+
source = default_options[:source].to_path
|
24
|
+
else
|
25
|
+
fail Error, "source file needs to respond to #path, or be a String, a Pathname, or a #{image_class} object"
|
26
|
+
end
|
27
|
+
|
28
|
+
processor = default_options[:processor].new
|
29
|
+
image = processor.load_image(source, default_options[:loader])
|
30
|
+
|
31
|
+
default_options[:operations].each do |name, args|
|
32
|
+
if name == :custom
|
33
|
+
image = args.first.call(image) || image
|
34
|
+
else
|
35
|
+
image = processor.apply_operation(name, image, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
return image unless save
|
40
|
+
|
41
|
+
return processor.save_image(image, destination, default_options[:saver]) if destination
|
42
|
+
|
43
|
+
source_path = source if source.is_a?(String)
|
44
|
+
format = default_options[:format] || File.extname(source_path.to_s)[1..-1] || "jpg"
|
45
|
+
|
46
|
+
result = Tempfile.new(["image_processing", ".#{format}"], binmode: true)
|
47
|
+
|
48
|
+
begin
|
49
|
+
processor.save_image(image, result.path, default_options[:saver])
|
50
|
+
rescue
|
51
|
+
result.close!
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
result.open
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,12 +1,98 @@
|
|
1
|
-
|
2
|
-
require "
|
3
|
-
require "image_processing/vips/processor"
|
1
|
+
gem "ruby-vips", "~> 2.0"
|
2
|
+
require "vips"
|
4
3
|
|
5
|
-
require "image_processing
|
4
|
+
require "image_processing"
|
5
|
+
|
6
|
+
fail "image_processing/vips requires libvips 8.6+" unless Vips.at_least_libvips?(8, 6)
|
6
7
|
|
7
8
|
module ImageProcessing
|
8
9
|
module Vips
|
9
|
-
|
10
|
+
def self.valid_image?(file)
|
11
|
+
::Vips::Image.new_from_file(file.path, access: :sequential, fail: true).avg
|
12
|
+
true
|
13
|
+
rescue ::Vips::Error
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
class Processor
|
18
|
+
IMAGE_CLASS = ::Vips::Image
|
19
|
+
# libvips has this arbitrary number as a sanity-check upper bound on image
|
20
|
+
# size.
|
21
|
+
MAX_COORD = 10_000_000
|
22
|
+
|
23
|
+
def apply_operation(name, image, *args)
|
24
|
+
if respond_to?(name)
|
25
|
+
public_send(name, image, *args)
|
26
|
+
else
|
27
|
+
result = image.send(name, *args)
|
28
|
+
result.is_a?(::Vips::Image) ? result : image
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def resize_to_limit(image, width, height, **options)
|
33
|
+
width, height = default_dimensions(width, height)
|
34
|
+
image.thumbnail_image(width, height: height, size: :down, **options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def resize_to_fit(image, width, height, **options)
|
38
|
+
width, height = default_dimensions(width, height)
|
39
|
+
image.thumbnail_image(width, height: height, **options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def resize_to_fill(image, width, height, **options)
|
43
|
+
image.thumbnail_image(width, height: height, crop: :centre, **options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def resize_and_pad(image, width, height, gravity: "centre", extend: nil, background: nil, alpha: nil, **options)
|
47
|
+
embed_options = { extend: extend, background: background }
|
48
|
+
embed_options.reject! { |name, value| value.nil? }
|
49
|
+
|
50
|
+
image = image.thumbnail_image(width, height: height, **options)
|
51
|
+
image = image.bandjoin(255) if alpha && image.bands == 3
|
52
|
+
image.gravity(gravity, width, height, **embed_options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def load_image(path_or_image, autorot: true, **options)
|
56
|
+
if path_or_image.is_a?(::Vips::Image)
|
57
|
+
image = path_or_image
|
58
|
+
else
|
59
|
+
source_path = path_or_image
|
60
|
+
loader = ::Vips.vips_foreign_find_load(source_path)
|
61
|
+
options = select_valid_options(loader, options) if loader
|
62
|
+
|
63
|
+
image = ::Vips::Image.new_from_file(source_path, fail: true, **options)
|
64
|
+
end
|
65
|
+
|
66
|
+
image = image.autorot if autorot
|
67
|
+
image
|
68
|
+
end
|
69
|
+
|
70
|
+
def save_image(image, destination_path, **options)
|
71
|
+
saver = ::Vips.vips_foreign_find_save(destination_path)
|
72
|
+
options = select_valid_options(saver, options) if saver
|
73
|
+
|
74
|
+
image.write_to_file(destination_path, **options)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def default_dimensions(width, height)
|
80
|
+
raise Error, "either width or height must be specified" unless width || height
|
81
|
+
|
82
|
+
[width || MAX_COORD, height || MAX_COORD]
|
83
|
+
end
|
84
|
+
|
85
|
+
def select_valid_options(operation_name, options)
|
86
|
+
operation = ::Vips::Operation.new(operation_name)
|
87
|
+
|
88
|
+
operation_options = operation.get_construct_args
|
89
|
+
.select { |name, flags| (flags & ::Vips::ARGUMENT_INPUT) != 0 }
|
90
|
+
.select { |name, flags| (flags & ::Vips::ARGUMENT_REQUIRED) == 0 }
|
91
|
+
.map(&:first).map(&:to_sym)
|
92
|
+
|
93
|
+
options.select { |name, value| operation_options.include?(name) }
|
94
|
+
end
|
95
|
+
end
|
10
96
|
|
11
97
|
extend Chainable
|
12
98
|
end
|
data/lib/image_processing.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
+
require "image_processing/chainable"
|
2
|
+
require "image_processing/pipeline"
|
3
|
+
require "image_processing/version"
|
4
|
+
|
1
5
|
module ImageProcessing
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
|
2
8
|
autoload :MiniMagick, 'image_processing/mini_magick'
|
3
9
|
autoload :Vips, 'image_processing/vips'
|
4
10
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: image_processing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -106,13 +106,12 @@ files:
|
|
106
106
|
- README.md
|
107
107
|
- image_processing.gemspec
|
108
108
|
- lib/image_processing.rb
|
109
|
+
- lib/image_processing/chainable.rb
|
109
110
|
- lib/image_processing/mini_magick.rb
|
111
|
+
- lib/image_processing/mini_magick/deprecated_api.rb
|
112
|
+
- lib/image_processing/pipeline.rb
|
110
113
|
- lib/image_processing/version.rb
|
111
114
|
- lib/image_processing/vips.rb
|
112
|
-
- lib/image_processing/vips/chainable.rb
|
113
|
-
- lib/image_processing/vips/color.rb
|
114
|
-
- lib/image_processing/vips/pipeline.rb
|
115
|
-
- lib/image_processing/vips/processor.rb
|
116
115
|
homepage: https://github.com/janko-m/image_processing
|
117
116
|
licenses:
|
118
117
|
- MIT
|