image_processing 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of image_processing might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8d0ca27be8b8095076a74f66c0b2c0300a83ced
4
- data.tar.gz: a237d603ae08dc18ecafb46cb9338d3234bc0559
3
+ metadata.gz: d0907be7d4bc1b7d37149c274483394cbba923cc
4
+ data.tar.gz: 794182bb527363c95bc3f6972e74af81435bd7c6
5
5
  SHA512:
6
- metadata.gz: 85d90cb3afff7bb64f6a6ab0e85bcfa0e3e5300163c72157e7c893050ebd6908c0a3224577df0bd246ea2a727dd61e5cf6c3d537971673d6f2c9449a2704b074
7
- data.tar.gz: 94823a28788aa45e276c16894678d7137d4092c397e0d3f962ab3e46685df08eb5ea09fc7eaaa713fb380cd26bfc79449f086622e5f1a01dffc38c9c55c340fa
6
+ metadata.gz: 4fcd9f523f50c4589b74deda0d7c41e10354faa4cdb9bfb1d26303c7246c152627b91bfcd920a59934104744a52e298f1fbcd9af63a5206219b02430c4bf3a96
7
+ data.tar.gz: 13d2d86ea9ed3940c12228a4af3adaf0073ae72be790641be8e9ad53f945902f6a0655246bb2f66a556426153a9055cb1c953a47fb70b1a14b9e9156e050c85d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## 0.10.0 (2018-03-21)
2
+
3
+ * [minimagick] Rewrite MiniMagick module to use the chainable API (@janko-m)
4
+
5
+ * [minimagick] Deprecate the old API (@janko-m)
6
+
7
+ * [minimagick] Raise an exception on processing warnings (@janko-m)
8
+
9
+ * [minimagick] Speed up `.valid_image?` by an order of magnitude (@janko-m)
10
+
11
+ * [minimagick] Don't accept arbitrary IO object anymore (@janko-m)
12
+
13
+ * [minimagick] Removed unnecessary `#crop` and `#resample` macros (@janko-m)
14
+
15
+ * [vips] Ignore undefined loader/saver options (@janko-m)
16
+
17
+ * [vips] Preserve transparent background in `#resize_to_pad` (@janko-m)
18
+
19
+ * [vips] Remove the ability to specify colors using names (@janko-m)
20
+
21
+ * [minimagick, vips] Autorotate images after loading them (@janko-m)
22
+
23
+ * [core] Delete result `Tempfile` object in case of processing errors (@janko-m)
24
+
25
+ * [core] Allow returning `nil` in the `#custom` block (@janko-m)
26
+
27
+ * [core] Allow specifying a path string as source file (@janko-m)
28
+
29
+ * [core] Allow saving to a specific location with the `:destination` call option (@janko-m)
30
+
1
31
  ## 0.9.0 (2018-03-16)
2
32
 
3
33
  * Added libvips module (@GustavoCaso, @janko-m)
data/README.md CHANGED
@@ -11,46 +11,34 @@ Refile, Dragonfly and ActiveStorage each implementing their own versions.
11
11
  ## Installation
12
12
 
13
13
  ```rb
14
- gem "image_processing"
14
+ gem "image_processing", "~> 0.10"
15
15
  ```
16
16
 
17
- ## ruby-vips
17
+ ## Usage
18
18
 
19
- The `ImageProcessing::Vips` module contains processing macros that use the
20
- [ruby-vips] gem, which you need to install:
19
+ Processing is performed through [`ImageProcessing::Vips`] or
20
+ [`ImageProcessing::MiniMagick`] modules. Both modules share the same chainable
21
+ API for defining the processing pipeline:
21
22
 
22
23
  ```rb
23
- # Gemfile
24
- gem "ruby-vips", "~> 2.0"
25
- ```
26
-
27
- Note that you'll need to have [libvips] 8.6 or higher installed; see
28
- the [installation instructions][libvips installation] for more details.
29
-
30
- ### Usage
31
-
32
- `ImageProcessing::Vips` lets you define the processing pipeline using a
33
- chainable API:
34
-
35
- ```rb
36
- require "image_processing/vips"
24
+ require "image_processing/mini_magick"
37
25
 
38
- processed = ImageProcessing::Vips
26
+ processed = ImageProcessing::MiniMagick
39
27
  .source(file)
40
- .autorot
41
28
  .resize_to_limit(400, 400)
42
29
  .convert("png")
43
30
  .call
44
31
 
45
- processed #=> #<File:/var/folders/.../image_processing-vips20180316-18446-1j247h6.png>
32
+ processed #=> #<File:/var/folders/.../image_processing20180316-18446-1j247h6.png>
46
33
  ```
47
34
 
48
35
  This allows easy branching when generating multiple derivatives:
49
36
 
50
37
  ```rb
38
+ require "image_processing/vips"
39
+
51
40
  pipeline = ImageProcessing::Vips
52
41
  .source(file)
53
- .autorot
54
42
  .convert("png")
55
43
 
56
44
  large = pipeline.resize_to_limit!(800, 800)
@@ -62,14 +50,14 @@ The processing is executed on `#call` or when a processing method is called
62
50
  with a bang (`!`).
63
51
 
64
52
  ```rb
65
- processed = ImageProcessing::Vips
53
+ processed = ImageProcessing::MiniMagick
66
54
  .convert("png")
67
55
  .resize_to_limit(400, 400)
68
56
  .call(image)
69
57
 
70
58
  # OR
71
59
 
72
- processed = ImageProcessing::Vips
60
+ processed = ImageProcessing::MiniMagick
73
61
  .source(image) # declare source image
74
62
  .convert("png")
75
63
  .resize_to_limit(400, 400)
@@ -77,366 +65,64 @@ processed = ImageProcessing::Vips
77
65
 
78
66
  # OR
79
67
 
80
- processed = ImageProcessing::Vips
68
+ processed = ImageProcessing::MiniMagick
81
69
  .source(image)
82
70
  .convert("png")
83
71
  .resize_to_limit!(400, 400) # bang method
84
72
  ```
85
73
 
86
- The source image needs to be an object that responds to `#path` or a
87
- `Vips::Image` object. The result is a `Tempfile` object, or a `Vips::Image`
88
- object if `save: false` is passed in.
89
-
90
- ```rb
91
- pipeline = ImageProcessing::Vips.source(image)
92
-
93
- tempfile = pipeline.call
94
- tempfile #=> #<Tempfile ...>
95
-
96
- vips_image = pipeline.call(save: false)
97
- vips_image #=> #<Vips::Image ...>
98
- ```
99
-
100
- #### `#resize_to_limit`
101
-
102
- Downsizes the image to fit within the specified dimensions while retaining the
103
- original aspect ratio. Will only resize the image if it's larger than the
104
- specified dimensions.
105
-
106
- ```rb
107
- pipeline = ImageProcessing::Vips.source(image) # 600x800
108
-
109
- result = pipeline.resize_to_limit!(400, 400)
110
-
111
- Vips::Image.new_from_file(result.path).size
112
- #=> [300, 400]
113
- ```
114
-
115
- It's possible to omit one dimension, in which case the image will be resized
116
- only by the provided dimension.
117
-
118
- ```rb
119
- pipeline.resize_to_limit!(400, nil)
120
- # or
121
- pipeline.resize_to_limit!(nil, 400)
122
- ```
123
-
124
- Any additional options are forwarded to [`Vips::Image#thumbnail_image`]:
125
-
126
- ```rb
127
- pipeline.resize_to_limit!(400, 400, linear: true)
128
- ```
129
-
130
- See [`vips_thumbnail()`] for more details.
131
-
132
- #### `#resize_to_fit`
133
-
134
- Resizes the image to fit within the specified dimensions while retaining the
135
- original aspect ratio. Will downsize the image if it's larger than the
136
- specified dimensions or upsize if it's smaller.
137
-
138
- ```rb
139
- pipeline = ImageProcessing::Vips.source(image) # 600x800
140
-
141
- result = pipeline.resize_to_fit!(400, 400)
142
-
143
- Vips::Image.new_from_file(result.path).size
144
- #=> [300, 400]
145
- ```
146
-
147
- It's possible to omit one dimension, in which case the image will be resized
148
- only by the provided dimension.
149
-
150
- ```rb
151
- pipeline.resize_to_fit!(400, nil)
152
- # or
153
- pipeline.resize_to_fit!(nil, 400)
154
- ```
155
-
156
- Any additional options are forwarded to [`Vips::Image#thumbnail_image`]:
157
-
158
- ```rb
159
- pipeline.resize_to_fit!(400, 400, linear: true)
160
- ```
161
-
162
- See [`vips_thumbnail()`] for more details.
163
-
164
- #### `#resize_to_fill`
165
-
166
- Resizes the image to fill the specified dimensions while retaining the original
167
- aspect ratio. If necessary, will crop the image in the larger dimension.
168
-
169
- ```rb
170
- pipeline = ImageProcessing::Vips.source(image) # 600x800
171
-
172
- result = pipeline.resize_to_fill!(400, 400)
173
-
174
- Vips::Image.new_from_file(result.path).size
175
- #=> [400, 400]
176
- ```
177
-
178
- Any additional options are forwarded to [`Vips::Image#thumbnail_image`]:
179
-
180
- ```rb
181
- pipeline.resize_to_fill!(400, 400, crop: :attention) # smart crop
182
- ```
183
-
184
- See [`vips_thumbnail()`] for more details.
185
-
186
- #### `#resize_and_pad`
187
-
188
- Resizes the image to fit within the specified dimensions while retaining the
189
- original aspect ratio. If necessary, will pad the remaining area with the given
190
- color, which defaults to transparent (for GIF and PNG, white for JPEG).
191
-
192
- ```rb
193
- pipeline = ImageProcessing::Vips.source(image) # 600x800
194
-
195
- result = pipeline.resize_and_pad!(400, 400)
196
-
197
- Vips::Image.new_from_file(result.path).size
198
- #=> [400, 400]
199
- ```
200
-
201
- You can specify the background [color] that will be used for padding:
202
-
203
- ```rb
204
- pipeline.resize_and_pad!(400, 400, color: "RoyalBlue")
205
- ```
206
-
207
- You can also specify the [direction] where the source image will be positioned:
208
-
209
- ```rb
210
- pipeline.resize_and_pad!(400, 400, gravity: "north-west")
211
- ```
212
-
213
- Any additional options are forwarded to [`Vips::Image#thumbnail_image`]:
214
-
215
- ```rb
216
- pipeline.resize_to_fill!(400, 400, linear: true)
217
- ```
218
-
219
- See [`vips_thumbnail()`] and [`vips_gravity()`] for more details.
220
-
221
- #### `#convert`
222
-
223
- Specifies the output format.
74
+ The source object needs to responds to `#path`, or be a String, a Pathname, or
75
+ a `Vips::Image`/`MiniMagick::Tool` object.
224
76
 
225
77
  ```rb
226
- pipeline = ImageProcessing::Vips.source(image)
227
-
228
- result = pipeline.convert!("png")
229
-
230
- File.extname(result.path)
231
- #=> ".png"
78
+ ImageProcessing::Vips.source(File.open("source.jpg"))
79
+ ImageProcessing::Vips.source("source.jpg")
80
+ ImageProcessing::Vips.source(Pathname.new("source.jpg"))
81
+ ImageProcessing::Vips.source(Vips::Image.new_from_file("source.jpg"))
232
82
  ```
233
83
 
234
- By default the original format is retained when writing the image to a file. If
235
- the source file doesn't have a file extension, the format will default to JPEG.
236
-
237
- #### `#set`, `#set_type`
238
-
239
- Sets `Vips::Image` metadata. Delegates to [`Vips::Image#set`] and
240
- [`Vips::Image#set_type`].
84
+ Without any call options the result of processing is a newly created `Tempfile`
85
+ object. You can save processing result to a specific location by passing
86
+ `:destination` to `#call`. You can also pass `save: false` to `#call` to
87
+ retrieve the raw `Vips::Image`/`MiniMagick::Tool` object.
241
88
 
242
89
  ```rb
243
90
  pipeline = ImageProcessing::Vips.source(image)
244
91
 
245
- pipeline.set("icc-profile-data", profile).call
246
- # or
247
- pipeline.set_type(Vips::BLOB_TYPE, "icc-profile-data", profile).call
248
- ```
249
-
250
- #### `#method_missing`
251
-
252
- Any unknown methods will be delegated to [`Vips::Image`].
253
-
254
- ```rb
255
- ImageProcessing::Vips
256
- .crop(0, 0, 300, 300)
257
- .invert
258
- .gaussblur(2)
259
- # ...
260
- ```
261
-
262
- #### `#custom`
263
-
264
- Calls the provided block with the intermediary `Vips::Image` object. The return
265
- value of the provided block must be a `Vips::Image` object.
266
-
267
- ```rb
268
- ImageProcessing::Vips
269
- .source(file)
270
- .resize_to_limit(400, 400)
271
- .custom { |image| image + image.invert }
272
- .call
273
- ```
274
-
275
- #### `#loader`
276
-
277
- Specifies options that will be forwarded to [`Vips::Image.new_from_file`].
278
-
279
- ```rb
280
- ImageProcessing::Vips
281
- .loader(access: :sequential)
282
- .resize_to_limit(400, 400)
283
- .call(source)
92
+ pipeline.call #=> #<Tempfile ...>
93
+ pipeline.call(save: false) #=> #<Vips::Image ...>
94
+ pipeline.call(destination: "/path/to/destination")
284
95
  ```
285
96
 
286
- See [`vips_jpegload()`], [`vips_pngload()`] etc. for more details on
287
- format-specific load options.
288
-
289
- If you would like to have more control over loading, you can load the image
290
- directly using `Vips::Image`, and just pass the `Vips::Image` object as the
291
- source file.
97
+ You can continue reading the API documentation for specific modules:
292
98
 
293
- ```rb
294
- vips_image = Vips::Image.magickload(file.path, n: -1)
295
-
296
- ImageProcessing::Vips
297
- .source(vips_image)
298
- # ...
299
- ```
99
+ * **[`ImageProcessing::Vips`]**
100
+ * **[`ImageProcessing::MiniMagick`]**
300
101
 
301
- #### `#saver`
102
+ ## Optimization
302
103
 
303
- Specifies options that will be forwarded to [`Vips::Image#write_to_file`].
104
+ After images have been processed, you might want to additionally optimize them
105
+ to reduce their filesize. You can do that with the [image_optim] gem:
304
106
 
305
107
  ```rb
306
- ImageProcessing::Vips
307
- .saver(Q: 100)
308
- .resize_to_limit(400, 400)
309
- .call(source)
310
- ```
311
-
312
- See [`vips_jpegsave()`], [`vips_pngsave()`] etc. for more details on
313
- format-specific save options.
108
+ require "image_optim"
314
109
 
315
- If you would like to have more control over saving, you can call `#call(save:
316
- false)` to get the `Vips::Image` object, and call the saver on it directly.
317
-
318
- ```rb
319
- vips_image = ImageProcessing::Vips
110
+ result = ImageProcessing::Vips
320
111
  .resize_to_limit(400, 400)
321
- .call(save: false)
322
-
323
- vips_image.write_to_file("/path/to/destination", **options)
324
- ```
325
-
326
- ## MiniMagick
327
-
328
- The `ImageProcessing::MiniMagick` module contains processing methods that use
329
- the [MiniMagick] gem, which you need to install:
330
-
331
- ```rb
332
- # Gemfile
333
- gem "mini_magick", ">= 4.3.5"
334
- ```
335
-
336
- Typically you will include the module in your class:
337
-
338
- ```rb
339
- require "image_processing/mini_magick"
340
-
341
- include ImageProcessing::MiniMagick
342
-
343
- original = File.open("path/to/image.jpg")
344
-
345
- converted = convert(original, "png") # makes a converted copy
346
- converted #=> #<File:/var/folders/.../mini_magick20151003-23030-9e1vjz.png (closed)>
347
- File.exist?(original.path) #=> true
348
-
349
- converted = convert!(original, "png") # converts the file in-place
350
- converted #=> #<File:/var/folders/.../mini_magick20151003-23030-9e1vjz.png (closed)>
351
- File.exist?(original.path) #=> false
352
- ```
353
-
354
- You can also call processing methods directly on the module:
355
-
356
- ```rb
357
- image = File.open("path/to/image.jpg")
358
-
359
- ImageProcessing::MiniMagick.resize_to_fit(image, 400, 400)
360
- ```
361
-
362
- ### Methods
363
-
364
- The following is the list of processing methods provided by
365
- `ImageProcessing::MiniMagick` (each one has both a destructive and a
366
- nondestructive version):
367
-
368
- ```rb
369
- # Adjust an image so that its orientation is suitable for viewing.
370
- auto_orient[!](file)
371
-
372
- # Converts file to the specified format, and you can specify to convert only a
373
- # certain page for multilayered formats.
374
- convert[!](file, format, page = nil)
375
-
376
- # Crop image to the defined area.
377
- crop[!](file, width, height, x_offset, y_offset, gravity: "NorthWest")
378
-
379
- # Resizes image to fit the specified dimensions (shrinks if larger, enlarges if
380
- # smaller, but keeps the aspect ratio).
381
- resize_to_fit[!](file, width, height)
382
-
383
- # Resizes image in limit of the specified dimensions (shrinks if larger, keeps
384
- # if smaller, but keeps the aspect ratio).
385
- resize_to_limit[!](file, width, height)
386
-
387
- # Resizes image to fill the specified dimensions (shrinks if larger,
388
- # enlarges if smaller, crops the longer side).
389
- resize_to_fill[!](file, width, height, gravity: "Center")
390
-
391
- # Resizes image to the specified dimensions and pads missing space (shrinks if
392
- # larger, enlarges if smaller, fills the shorter side with specified color).
393
- resize_and_pad[!](file, width, height, background: "transparent", gravity: "Center")
394
-
395
- # Resamples the image to a different resolution
396
- resample[!](file, horizontal, vertical)
397
-
398
- # Returns true if the given image is corrupted
399
- corrupted?(file)
400
- ```
401
-
402
- The `#resize_to_limit[!]` and `#resize_to_fit[!]` allow specifying only one
403
- dimension:
404
-
405
- ```rb
406
- resize_to_limit(image, 300, nil)
407
- resize_to_fit(image, nil, 500)
408
- ```
409
-
410
- ### Dropping to MiniMagick
411
-
412
- If you want to do custom MiniMagick processing, each of the above optionally
413
- yields an instance of `MiniMagick::Tool`, which you can use for additional
414
- processing:
415
-
416
- ```rb
417
- convert(file, "png") do |cmd|
418
- cmd.background("none")
419
- end
420
- ```
112
+ .saver(Q: 85)
113
+ .call(image)
421
114
 
422
- There is also a helper method for doing MiniMagick processing directly (though
423
- note that this will process the image in-place!):
115
+ image_optim = ImageOptim.new(...)
116
+ image_optim.optimize_image!(result.path)
117
+ result.open # refresh file descriptor
424
118
 
425
- ```rb
426
- processed = with_minimagick(file) do |image|
427
- image #=> #<MiniMagick::Image ...>
428
- image.combine_options do |cmd|
429
- # ...
430
- end
431
- end
432
-
433
- processed #=> #<File ...>
119
+ result # optimized image
434
120
  ```
435
121
 
436
122
  ## Contributing
437
123
 
438
- Test suite requires `imagemagick`, `graphicsmagick` and `libvips` be installed.
439
- On Mac OS you can install them with Homebrew:
124
+ Test suite requires `imagemagick`, `graphicsmagick` and `libvips` to be
125
+ installed. On Mac OS you can install them with Homebrew:
440
126
 
441
127
  ```
442
128
  $ brew install imagemagick graphicsmagick vips
@@ -460,22 +146,7 @@ The `ImageProcessing::MiniMagick` functionality was extracted from
460
146
  [ImageMagick]: https://www.imagemagick.org
461
147
  [GraphicsMagick]: http://www.graphicsmagick.org
462
148
  [VIPS]: http://jcupitt.github.io/libvips/
463
- [MiniMagick]: https://github.com/minimagick/minimagick
464
- [ruby-vips]: https://github.com/jcupitt/ruby-vips
465
- [libvips]: https://github.com/jcupitt/libvips
466
- [libvips installation]: https://github.com/jcupitt/libvips/wiki#building-and-installing
149
+ [`ImageProcessing::Vips`]: /doc/vips.md#imageprocessingvips
150
+ [`ImageProcessing::MiniMagick`]: /doc/minimagick.md#imageprocessingminimagick
151
+ [image_optim]: https://github.com/toy/image_optim
467
152
  [refile-mini_magick]: https://github.com/refile/refile-mini_magick
468
- [`Vips::Image`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image
469
- [`Vips::Image.new_from_file`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image#new_from_file-class_method
470
- [`Vips::Image#write_to_file`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image#write_to_file-instance_method
471
- [`Vips::Image#thumbnail_image`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image#thumbnail_image-instance_method
472
- [`Vips::Image#set`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image#set-instance_method
473
- [`Vips::Image#set_type`]: http://www.rubydoc.info/gems/ruby-vips/Vips/Image#set_type-instance_method
474
- [`vips_thumbnail()`]: https://jcupitt.github.io/libvips/API/current/libvips-resample.html#vips-thumbnail
475
- [`vips_gravity()`]: http://jcupitt.github.io/libvips/API/current/libvips-conversion.html#vips-gravity
476
- [`vips_jpegload()`]: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegload
477
- [`vips_pngload()`]: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-pngload
478
- [`vips_jpegsave()`]: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave
479
- [`vips_pngsave()`]: https://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-pngsave
480
- [color]: https://www.imagemagick.org/script/color.php#color_names
481
- [direction]: http://jcupitt.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
@@ -0,0 +1,63 @@
1
+ module ImageProcessing
2
+ module Chainable
3
+ def source(file)
4
+ branch default_options.merge(source: file)
5
+ end
6
+
7
+ def convert(format)
8
+ branch default_options.merge(format: format)
9
+ end
10
+
11
+ def loader(**options)
12
+ loader = default_options[:loader].merge(options)
13
+ branch default_options.merge(loader: loader)
14
+ end
15
+
16
+ def saver(**options)
17
+ saver = default_options[:saver].merge(options)
18
+ branch default_options.merge(saver: saver)
19
+ end
20
+
21
+ def operation(name, *args)
22
+ operations = default_options[:operations] + [[name, args]]
23
+ branch default_options.merge(operations: operations)
24
+ end
25
+
26
+ def custom(&block)
27
+ block ? operation(:custom, block) : self
28
+ end
29
+
30
+ def method_missing(name, *args)
31
+ if name.to_s.end_with?("!")
32
+ send(name.to_s.chomp("!"), *args).call!
33
+ elsif name.to_s.end_with?("?")
34
+ super
35
+ else
36
+ operation(name, *args)
37
+ end
38
+ end
39
+
40
+ def call(file = nil, **call_options)
41
+ options = default_options
42
+ options = options.merge(source: file) if file
43
+
44
+ branch(options).call!(**call_options)
45
+ end
46
+
47
+ def branch(options)
48
+ options = options.merge(processor: self::Processor) if self.is_a?(Module)
49
+ Pipeline.new(options)
50
+ end
51
+
52
+ def default_options
53
+ @default_options ||= {
54
+ source: nil,
55
+ loader: {},
56
+ saver: {},
57
+ format: nil,
58
+ operations: [],
59
+ processor: nil,
60
+ }
61
+ end
62
+ end
63
+ end