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