vips-process 0.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +22 -0
- data/README.md +195 -0
- data/Rakefile +7 -0
- data/lib/vips-process/auto-orient.rb +33 -0
- data/lib/vips-process/base.rb +16 -0
- data/lib/vips-process/convert.rb +21 -0
- data/lib/vips-process/gaussian-blur.rb +43 -0
- data/lib/vips-process/quality.rb +17 -0
- data/lib/vips-process/resize.rb +93 -0
- data/lib/vips-process/strip.rb +22 -0
- data/lib/vips-process/version.rb +5 -0
- data/lib/vips-process.rb +101 -0
- data/test/helpers.rb +13 -0
- data/test/vips-process-test.rb +36 -0
- data/vips-process.gemspec +21 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 28664d855e6a202bf807e8d88369d9033c5a2161
|
4
|
+
data.tar.gz: 5d784293a361981a59145d388c46107cac41703d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9473e1b51f4011cad6387e3c2994c36936ceb6cae84421e76fbea7230863c27c52f90e2b0f6475c6e516bc426c8214ee44e7b09c323d3af6780a499839d5494c
|
7
|
+
data.tar.gz: 60445478ea6e610c7c59b008eacdcda359fdd9b6a34c96889a3891dcc8b2614994387acc3894f1c121d866c10acd83932e1b2ac409400c0cf363ed0c209d0828
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
vips-process (0.0.1)
|
5
|
+
ruby-vips (~> 0.3.9)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.0)
|
11
|
+
cutest (1.2.1)
|
12
|
+
method_source (0.8.2)
|
13
|
+
pry (0.10.1)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.8.1)
|
16
|
+
slop (~> 3.4)
|
17
|
+
rake (10.3.2)
|
18
|
+
ruby-vips (0.3.9)
|
19
|
+
slop (3.6.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bundler (~> 1.7)
|
26
|
+
cutest (~> 1.2.1)
|
27
|
+
pry (~> 0.10.1)
|
28
|
+
rake (~> 10.0)
|
29
|
+
vips-process!
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Darío Javier Cravero
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Vips::Process
|
2
|
+
|
3
|
+
Process your images with ruby-vips using an operation-oriented approach.
|
4
|
+
|
5
|
+
Inspired by [carrierwave-vips](https://github.com/eltiare/carrierwave-vips/blob/master/lib/carrierwave/vips.rb)
|
6
|
+
and [@jcupitt's](https://github.com/jcupitt/ruby-vips/issues/60#issuecomment-56934898) gaussian blur example.
|
7
|
+
|
8
|
+
Made with <3 @[UXtemple](http://uxtemple.com). :)
|
9
|
+
|
10
|
+
[Vips](http://www.vips.ecs.soton.ac.uk/index.php?title=VIPS) is an open source and super fast image
|
11
|
+
processing library with a very low memory footprint.
|
12
|
+
You can use it as a replacement to `ImageMagick`, `MiniMagick` and the likes.
|
13
|
+
See the benchmarks [here](http://www.vips.ecs.soton.ac.uk/index.php?title=Speed_and_Memory_Use).
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
This gem requires `libvips` in order to run. For instructions on how to get it in OS X and Linux, follow [these installation guides](https://github.com/jcupitt/ruby-vips#installation-prerequisites).
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'vips-process'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install vips-process
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Define your image class and explicity include the processes you want to apply to it:
|
36
|
+
|
37
|
+
```
|
38
|
+
# example.rb
|
39
|
+
require 'vips-process'
|
40
|
+
require 'vips-process/gaussian-blur'
|
41
|
+
require 'vips-process/resize'
|
42
|
+
|
43
|
+
class MyImage
|
44
|
+
include Vips::Process
|
45
|
+
include Vips::Process::GaussianBlur
|
46
|
+
include Vips::Process::Resize
|
47
|
+
include Vips::Process::Quality
|
48
|
+
|
49
|
+
attr_accessor :src, :dst
|
50
|
+
|
51
|
+
def initialize(src, dst=nil)
|
52
|
+
@src = src
|
53
|
+
@dst = dst || src
|
54
|
+
end
|
55
|
+
|
56
|
+
version(:blurred) { gaussian_blur }
|
57
|
+
version(:thumb) { resize_to_fit 150, 150 }
|
58
|
+
version :blurred_thumb, [:thumb, :blurred]
|
59
|
+
version(:blurred_thumb, [:thumb, :blurred]) { quality 50 }
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
`Vips::Process` also comes with a `Base` helper class that takes care of defining `src`, `dst` and
|
64
|
+
including `Vips::Process` for you. It's the recommended way to go about it and you can use it as follows
|
65
|
+
|
66
|
+
```
|
67
|
+
require 'vips-process/base'
|
68
|
+
require 'vips-process/gaussian-blur'
|
69
|
+
|
70
|
+
class MyImage < Vips::Process::Base
|
71
|
+
include Vips::Process::GaussianBlur
|
72
|
+
|
73
|
+
version(:blurred) { gaussian_blur }
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
## Supported processes
|
78
|
+
|
79
|
+
All examples live in the [/examples](https://github.com/dariocravero/vips-process/tree/master/examples)
|
80
|
+
folder of this repo.
|
81
|
+
|
82
|
+
### AutoOrient
|
83
|
+
|
84
|
+
Read the camera EXIF data to determine orientation and adjust accordingly
|
85
|
+
|
86
|
+
Usage:
|
87
|
+
|
88
|
+
Include it in your image class `include Vips::Process::AutoOrient` and
|
89
|
+
call it `MyImage.new('/path/to/src').auto_orient`.
|
90
|
+
|
91
|
+
### GaussianBlur
|
92
|
+
|
93
|
+
Applies a gaussian blur to an image.
|
94
|
+
|
95
|
+
Usage:
|
96
|
+
|
97
|
+
Include it in your image class `include Vips::Process::GaussianBlur` and
|
98
|
+
call it `MyImage.new('/path/to/src').gaussian_blur(5, 0.2)`.
|
99
|
+
|
100
|
+
The first argument is the `radius` and the second one is the `minimium amplitude` we consider,
|
101
|
+
which sets how far out the mask goes; it's optional and defaults to `0.2`.
|
102
|
+
|
103
|
+
### Convert
|
104
|
+
|
105
|
+
Converts an image to a different format.
|
106
|
+
|
107
|
+
Usage:
|
108
|
+
|
109
|
+
Include it in your image class `include Vips::Process::Convert` and
|
110
|
+
call it `MyImage.new('/path/to/src.jpg').convert(:png)`.
|
111
|
+
|
112
|
+
The first argument is the `format` we'll conver the file to (jpeg or png) and
|
113
|
+
the second one is an optional hash of `options` to be passed to the converting function
|
114
|
+
(ie, :interlace => true for png).
|
115
|
+
|
116
|
+
### Quality
|
117
|
+
|
118
|
+
Changes quality of the image (if supported by the file format)
|
119
|
+
|
120
|
+
Usage:
|
121
|
+
|
122
|
+
Include it in your image class `include Vips::Process::Quality` and
|
123
|
+
call it `MyImage.new('/path/to/src.jpg').quality(50)`.
|
124
|
+
|
125
|
+
It takes the `quality` as an argument which defaults to `75`. This value should be between 0 and 100.
|
126
|
+
Currently `jpegs` are only supported. Using any other image formats will be a no-op.
|
127
|
+
|
128
|
+
### Resize
|
129
|
+
|
130
|
+
There are three resize methods. Each of them will sharpen the image after doing its work.
|
131
|
+
|
132
|
+
Usage:
|
133
|
+
|
134
|
+
Include it in your image class `include Vips::Process::Resize`.
|
135
|
+
All of them take the same arguments: `width` and `height`.
|
136
|
+
|
137
|
+
#### resize_to_fit
|
138
|
+
|
139
|
+
Resize the image to fit within the specified dimensions while retaining
|
140
|
+
the original aspect ratio. The image may be shorter or narrower than
|
141
|
+
specified in the smaller dimension but will not be larger than the
|
142
|
+
specified values.
|
143
|
+
|
144
|
+
Call it as follows: `MyImage.new('/path/to/src.jpg').resize_to_fit(150, 150)`
|
145
|
+
|
146
|
+
#### resize_to_fill
|
147
|
+
|
148
|
+
Resize the image to fit within the specified dimensions while retaining
|
149
|
+
the aspect ratio of the original image. If necessary, crop the image in
|
150
|
+
the larger dimension.
|
151
|
+
|
152
|
+
Call it as follows: `MyImage.new('/path/to/src.jpg').resize_to_fill(150, 150)`
|
153
|
+
|
154
|
+
#### resize_to_limit
|
155
|
+
|
156
|
+
Resize the image to fit within the specified dimensions while retaining
|
157
|
+
the original aspect ratio. Will only resize the image if it is larger than the
|
158
|
+
specified dimensions. The resulting image may be shorter or narrower than specified
|
159
|
+
in the smaller dimension but will not be larger than the specified values.
|
160
|
+
|
161
|
+
Call it as follows: `MyImage.new('/path/to/src.jpg').resize_to_limit(150, 150)`
|
162
|
+
|
163
|
+
### Strip
|
164
|
+
|
165
|
+
Remove all exif and icc data when writing to a file. This method does
|
166
|
+
not actually remove any metadata but rather marks it to be removed when
|
167
|
+
writing the file.
|
168
|
+
|
169
|
+
Usage:
|
170
|
+
|
171
|
+
Include it in your image class `include Vips::Process::Strip` and
|
172
|
+
call it `MyImage.new('/path/to/src.jpg').strip`.
|
173
|
+
|
174
|
+
## Version definition
|
175
|
+
|
176
|
+
You can also define versions and compose them as you wish.
|
177
|
+
|
178
|
+
A version is defined by calling `version` in your image class.
|
179
|
+
|
180
|
+
A version can have dependencies which will be executed sequentially. Of course composed versions
|
181
|
+
can also take a block and do more things. Maybe this can grow into a set of common versions?
|
182
|
+
|
183
|
+
## TODO
|
184
|
+
|
185
|
+
1. Increment test coverage.
|
186
|
+
2. Implement version caching.
|
187
|
+
3. Implement more processes. Feel free to add what you may need.
|
188
|
+
|
189
|
+
## Contributing
|
190
|
+
|
191
|
+
1. Fork it ( https://github.com/[my-github-username]/vips-process/fork )
|
192
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
193
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
194
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
195
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module AutoOrient
|
4
|
+
EMPTY_STRING = ''.freeze
|
5
|
+
EXIF_ORIENTATION = 'exif-Orientation'.freeze
|
6
|
+
EXIF_IFD0_ORIENTATION = 'exif-ifd0-Orientation'.freeze
|
7
|
+
|
8
|
+
# Read the camera EXIF data to determine orientation and adjust accordingly
|
9
|
+
def auto_orient
|
10
|
+
manipulate! do |image|
|
11
|
+
o = image.get(EXIF_ORIENTATION).to_i rescue nil
|
12
|
+
o ||= image.get(EXIF_IFD0_ORIENTATION).to_i rescue 1
|
13
|
+
|
14
|
+
case o
|
15
|
+
when 1
|
16
|
+
# Do nothing, everything is peachy
|
17
|
+
when 6
|
18
|
+
image.rot270
|
19
|
+
when 8
|
20
|
+
image.rot180
|
21
|
+
when 3
|
22
|
+
image.rot90
|
23
|
+
else
|
24
|
+
raise('Invalid value for Orientation: ' + o.to_s)
|
25
|
+
end
|
26
|
+
image.set EXIF_ORIENTATION, EMPTY_STRING
|
27
|
+
image.set EXIF_IFD0_ORIENTATION, EMPTY_STRING
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end # AutoOrient
|
32
|
+
end # Process
|
33
|
+
end # Vips
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module Convert
|
4
|
+
ALLOWED_FORMATS = [JPEG, PNG]
|
5
|
+
|
6
|
+
##
|
7
|
+
# Converts an image to a different format
|
8
|
+
#
|
9
|
+
# @param format String the format we'll convert the file to (jpeg, png)
|
10
|
+
# @param opts Hash options to be passed to converting function (ie, :interlace => true for png)
|
11
|
+
#
|
12
|
+
def convert(format, opts = {})
|
13
|
+
format = format.to_s.downcase
|
14
|
+
raise ArgumentError, "Format must be one of: #{ALLOWED_FORMATS.join(',')}" unless ALLOWED_FORMATS.include?(format)
|
15
|
+
@_format = format
|
16
|
+
@_format_opts = opts
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end # Convert
|
20
|
+
end # Process
|
21
|
+
end # Vips
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module GaussianBlur
|
4
|
+
##
|
5
|
+
# We normalise to 20 so that the mask sum stays under 255 for most blurs.
|
6
|
+
# This will let Vips use its fast SSE path for 8-bit images
|
7
|
+
NORMALISE_TO = 20.0
|
8
|
+
|
9
|
+
# The size of the biggest mask we support
|
10
|
+
BIGGEST_MASK = 10000
|
11
|
+
|
12
|
+
##
|
13
|
+
# Apply gaussian blur to an image.
|
14
|
+
#
|
15
|
+
# @param sigma Integer roughly the radius
|
16
|
+
# @param min_ampl Float minimium amplitude we consider, it sets how far out the mask goes
|
17
|
+
def gaussian_blur(sigma, min_ampl=0.2)
|
18
|
+
manipulate! do |image|
|
19
|
+
image.convsep gaussian_mask(sigma, min_ampl)
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Make a 1D int gaussian mask suitable for a separable convolution
|
25
|
+
private def gaussian_mask(sigma, min_ampl)
|
26
|
+
sigma2 = 2.0 * sigma ** 2.0
|
27
|
+
|
28
|
+
# Find the size of the mask
|
29
|
+
max_size = (1..BIGGEST_MASK).detect { |x| Math::exp(-x ** 2.0 / sigma2) < min_ampl }
|
30
|
+
throw :mask_too_large unless max_size
|
31
|
+
|
32
|
+
width = max_size * 2 + 1
|
33
|
+
mask = (0...width).map do |x|
|
34
|
+
d = (x - width / 2) ** 2
|
35
|
+
(NORMALISE_TO * Math::exp(-d / sigma2)).round
|
36
|
+
end
|
37
|
+
sum = mask.reduce(:+)
|
38
|
+
|
39
|
+
VIPS::Mask.new [mask], sum, 0
|
40
|
+
end
|
41
|
+
end # GaussianBlur
|
42
|
+
end # Process
|
43
|
+
end # Vips
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module Quality
|
4
|
+
##
|
5
|
+
# Changes quality of the image (if supported by the file format)
|
6
|
+
#
|
7
|
+
# @param percent Integer quality from 0 to 100
|
8
|
+
def quality(percent=75)
|
9
|
+
manipulate! do |image|
|
10
|
+
@_format_opts = { quality: percent } if jpeg? || @_format == JPEG
|
11
|
+
image
|
12
|
+
end
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end # Quality
|
16
|
+
end # Process
|
17
|
+
end # Vips
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module Resize
|
4
|
+
SHARPEN_MASK = begin
|
5
|
+
conv_mask = [
|
6
|
+
[ -1, -1, -1 ],
|
7
|
+
[ -1, 24, -1 ],
|
8
|
+
[ -1, -1, -1 ]
|
9
|
+
]
|
10
|
+
::VIPS::Mask.new conv_mask, 16
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Resize the image to fit within the specified dimensions while retaining
|
15
|
+
# the original aspect ratio. The image may be shorter or narrower than
|
16
|
+
# specified in the smaller dimension but will not be larger than the
|
17
|
+
# specified values.
|
18
|
+
#
|
19
|
+
# @param width Integer the width to scale the image to
|
20
|
+
# @param height Integer the height to scale the image to
|
21
|
+
def resize_to_fit(width, height)
|
22
|
+
manipulate! do |image|
|
23
|
+
resize_image image, width, height
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Resize the image to fit within the specified dimensions while retaining
|
30
|
+
# the aspect ratio of the original image. If necessary, crop the image in
|
31
|
+
# the larger dimension.
|
32
|
+
#
|
33
|
+
# @param width Integer the width to scale the image to
|
34
|
+
# @param height Integer the height to scale the image to
|
35
|
+
def resize_to_fill(width, height)
|
36
|
+
manipulate! do |image|
|
37
|
+
image = resize_image image, width, height, :max
|
38
|
+
top = 0
|
39
|
+
left = 0
|
40
|
+
|
41
|
+
if image.x_size > width
|
42
|
+
left = (image.x_size - width) / 2
|
43
|
+
elsif image.y_size > height
|
44
|
+
top = (image.y_size - height) / 2
|
45
|
+
end
|
46
|
+
|
47
|
+
image.extract_area left, top, width, height
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Resize the image to fit within the specified dimensions while retaining
|
54
|
+
# the original aspect ratio. Will only resize the image if it is larger than the
|
55
|
+
# specified dimensions. The resulting image may be shorter or narrower than specified
|
56
|
+
# in the smaller dimension but will not be larger than the specified values.
|
57
|
+
#
|
58
|
+
# @param width Integer the width to scale the image to
|
59
|
+
# @parma height Integer the height to scale the image to
|
60
|
+
def resize_to_limit(width, height)
|
61
|
+
manipulate! do |image|
|
62
|
+
image = resize_image(image, width, height) if width < image.x_size || height < image.y_size
|
63
|
+
image
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
private def resize_image(image, width, height, min_or_max = :min)
|
69
|
+
ratio = get_ratio image, width, height, min_or_max
|
70
|
+
return image if ratio == 1
|
71
|
+
if ratio > 1
|
72
|
+
image = image.affinei_resize :nearest, ratio
|
73
|
+
else
|
74
|
+
if ratio <= 0.5
|
75
|
+
factor = (1.0 / ratio).floor
|
76
|
+
image = image.shrink factor
|
77
|
+
image = image.tile_cache image.x_size, 1, 30
|
78
|
+
ratio = get_ratio image, width, height, min_or_max
|
79
|
+
end
|
80
|
+
image = image.affinei_resize :bicubic, ratio
|
81
|
+
image = image.conv SHARPEN_MASK
|
82
|
+
end
|
83
|
+
image
|
84
|
+
end
|
85
|
+
|
86
|
+
private def get_ratio(image, width,height, min_or_max = :min)
|
87
|
+
width_ratio = width.to_f / image.x_size
|
88
|
+
height_ratio = height.to_f / image.y_size
|
89
|
+
[width_ratio, height_ratio].send min_or_max
|
90
|
+
end
|
91
|
+
end # Resize
|
92
|
+
end # Process
|
93
|
+
end # Vips
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vips
|
2
|
+
module Process
|
3
|
+
module Strip
|
4
|
+
##
|
5
|
+
# Remove all exif and icc data when writing to a file. This method does
|
6
|
+
# not actually remove any metadata but rather marks it to be removed when
|
7
|
+
# writing the file.
|
8
|
+
#
|
9
|
+
def strip
|
10
|
+
manipulate! do |image|
|
11
|
+
@_on_process << ->(writer) do
|
12
|
+
writer.remove_exif
|
13
|
+
writer.remove_icc
|
14
|
+
end
|
15
|
+
|
16
|
+
image
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end # Strip
|
21
|
+
end # Process
|
22
|
+
end # Vips
|
data/lib/vips-process.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'vips'
|
2
|
+
require 'vips-process/version'
|
3
|
+
|
4
|
+
module Vips
|
5
|
+
module Process
|
6
|
+
JPEG = 'jpeg'.freeze
|
7
|
+
PNG = 'png'.freeze
|
8
|
+
|
9
|
+
##
|
10
|
+
# Manipulate the image with Vips. Saving of the image is delayed until after
|
11
|
+
# all the process blocks have been called. Make sure you always return an
|
12
|
+
# VIPS::Image object from the block
|
13
|
+
#
|
14
|
+
# This method yields VIPS::Image for further manipulation.
|
15
|
+
#
|
16
|
+
# It also raises an Exception if the manipulation failed.
|
17
|
+
def manipulate!
|
18
|
+
@_on_process ||= []
|
19
|
+
@_vimage ||= if jpeg?
|
20
|
+
VIPS::Image.jpeg @src, sequential: true
|
21
|
+
elsif png?
|
22
|
+
VIPS::Image.png @src, sequential: true
|
23
|
+
else
|
24
|
+
VIPS::Image.new @src
|
25
|
+
end
|
26
|
+
@_vimage = yield @_vimage
|
27
|
+
rescue => e
|
28
|
+
raise Exception.new("Failed to manipulate file, maybe it is not a supported image? Original Error: #{e}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def process!
|
32
|
+
if @_vimage
|
33
|
+
tmp_name = @dst.sub /(\.[[:alnum:]]+)$/i, '_tmp\1'
|
34
|
+
writer = writer_class.send :new, @_vimage, @_format_opts
|
35
|
+
|
36
|
+
@_on_process.each { |block| block.call writer }
|
37
|
+
|
38
|
+
writer.write tmp_name
|
39
|
+
FileUtils.mv tmp_name, @dst
|
40
|
+
|
41
|
+
reset!
|
42
|
+
@dst
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Allow changing src and chain it afterwards
|
47
|
+
def src!(src)
|
48
|
+
@src = src
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Allow changing dst and chain it afterwards
|
53
|
+
def dst!(dst)
|
54
|
+
@dst = dst
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
private def reset!
|
59
|
+
@_on_process = []
|
60
|
+
@_format_opts = nil
|
61
|
+
@_vimage = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private def writer_class
|
65
|
+
case @_format
|
66
|
+
when JPEG then VIPS::JPEGWriter
|
67
|
+
when PNG then VIPS::PNGWriter
|
68
|
+
else VIPS::Writer
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private def jpeg?(path = @src); path =~ /.*jpg$/i or path =~ /.*jpeg$/i; end
|
73
|
+
private def png?(path = @src); path =~ /.*png$/i; end
|
74
|
+
|
75
|
+
def self.included(base)
|
76
|
+
base.extend(ClassMethods)
|
77
|
+
end
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
##
|
81
|
+
# Define a version
|
82
|
+
#
|
83
|
+
# @param name String the version's name
|
84
|
+
# @param deps [] the version's dependencies, it's a list of version names
|
85
|
+
# @param &block block if you send a block to it
|
86
|
+
def version(name, deps = [], &block)
|
87
|
+
throw :need_block_or_deps unless block_given? || !deps.empty?
|
88
|
+
|
89
|
+
@@_versions ||= {}
|
90
|
+
@@_versions[name] = {deps: deps, block: block}
|
91
|
+
|
92
|
+
define_method "#{name}_version" do |new_dst=nil|
|
93
|
+
@dst = new_dst if new_dst
|
94
|
+
@@_versions[name][:deps].each { |dep| instance_eval &@@_versions[dep][:block] }
|
95
|
+
instance_eval &@@_versions[name][:block]
|
96
|
+
process!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/test/helpers.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'vips-process/base'
|
2
|
+
require 'vips-process/gaussian-blur'
|
3
|
+
require 'vips-process/resize'
|
4
|
+
|
5
|
+
class MyImage < Vips::Process::Base
|
6
|
+
include Vips::Process::GaussianBlur
|
7
|
+
include Vips::Process::Resize
|
8
|
+
|
9
|
+
version(:blurred) { gaussian_blur }
|
10
|
+
version(:thumb) { resize_to_fit 150, 150 }
|
11
|
+
version :blurred_thumb, [:thumb, :blurred]
|
12
|
+
version(:blurred_thumb, [:thumb, :blurred]) { quality 50 }
|
13
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'helpers'
|
2
|
+
|
3
|
+
test "it should allow the source to be changed and the image chained" do
|
4
|
+
original_src = '/path/to/src'
|
5
|
+
new_src = '/path/to/new/dest'
|
6
|
+
|
7
|
+
image = MyImage.new original_src, '/path/to/dst'
|
8
|
+
|
9
|
+
assert_equal image.src!(new_src), image
|
10
|
+
assert_equal image.src, new_src
|
11
|
+
end
|
12
|
+
|
13
|
+
test "it should allow the destination to be changed and the image chained" do
|
14
|
+
original_dst = '/path/to/dst'
|
15
|
+
new_dst = '/path/to/new/dst'
|
16
|
+
|
17
|
+
image = MyImage.new '/path/to/src', original_dst
|
18
|
+
|
19
|
+
assert_equal image.dst!(new_dst), image
|
20
|
+
assert_equal image.dst, new_dst
|
21
|
+
end
|
22
|
+
|
23
|
+
test "it should define a version" do
|
24
|
+
class BlurredImage
|
25
|
+
include Vips::Process
|
26
|
+
version(:blurred) { 'blurred' }
|
27
|
+
end
|
28
|
+
|
29
|
+
image = MyImage.new '/path/to/src'
|
30
|
+
|
31
|
+
assert image.respond_to?(:blurred_version)
|
32
|
+
end
|
33
|
+
|
34
|
+
test "it should allow a version to be composed out of other versions" do print 'S' end
|
35
|
+
test "it should allow a version to be composed out of other versions and have a block" do print 'S' end
|
36
|
+
test "it should reset the settings after process! runs" do print 'S' end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/vips-process/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'vips-process'
|
6
|
+
s.version = Vips::Process::VERSION
|
7
|
+
s.authors = ['Darío Javier Cravero']
|
8
|
+
s.email = ['dario@uxtemple.com']
|
9
|
+
s.summary = %q{ruby-vips operation-oriented processing}
|
10
|
+
s.description = %q{Process your images with ruby-vips using an operation-oriented approach.}
|
11
|
+
s.homepage = 'https://github.com/dariocravero/vips-process'
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.files = Dir['LICENSE', 'README.md', 'Rakefile', 'Gemfile', 'Gemfile.lock',
|
14
|
+
'vips-process.gemspec', 'lib/**/*.rb', 'test/*.rb']
|
15
|
+
|
16
|
+
s.add_dependency 'ruby-vips', '~> 0.3.9'
|
17
|
+
s.add_development_dependency 'pry', '~> 0.10.1'
|
18
|
+
s.add_development_dependency 'cutest', '~> 1.2.1'
|
19
|
+
s.add_development_dependency 'bundler', '~> 1.7'
|
20
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vips-process
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Darío Javier Cravero
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-vips
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.9
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.9
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.1
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.10.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cutest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
description: Process your images with ruby-vips using an operation-oriented approach.
|
84
|
+
email:
|
85
|
+
- dario@uxtemple.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- Gemfile
|
91
|
+
- Gemfile.lock
|
92
|
+
- LICENSE
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/vips-process.rb
|
96
|
+
- lib/vips-process/auto-orient.rb
|
97
|
+
- lib/vips-process/base.rb
|
98
|
+
- lib/vips-process/convert.rb
|
99
|
+
- lib/vips-process/gaussian-blur.rb
|
100
|
+
- lib/vips-process/quality.rb
|
101
|
+
- lib/vips-process/resize.rb
|
102
|
+
- lib/vips-process/strip.rb
|
103
|
+
- lib/vips-process/version.rb
|
104
|
+
- test/helpers.rb
|
105
|
+
- test/vips-process-test.rb
|
106
|
+
- vips-process.gemspec
|
107
|
+
homepage: https://github.com/dariocravero/vips-process
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.2.2
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: ruby-vips operation-oriented processing
|
131
|
+
test_files: []
|
132
|
+
has_rdoc:
|