Skip to content

Commit ed87991

Browse files
authored
Merge pull request #2721 from clickworkorange/master
Added support for the "crop" method.
2 parents 8b33235 + a507b9f commit ed87991

File tree

8 files changed

+176
-8
lines changed

8 files changed

+176
-8
lines changed

README.md

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -979,14 +979,6 @@ class AvatarUploader < CarrierWave::Uploader::Base
979979
end
980980
```
981981

982-
#### List of available processing methods:
983-
984-
- `convert` - Changes the image encoding format to the given format(eg. jpg). This operation is treated specially to trigger the change of the file extension, so it matches with the format of the resulting file.
985-
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
986-
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
987-
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
988-
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
989-
990982
See `carrierwave/processing/mini_magick.rb` for details.
991983

992984
### Using RMagick
@@ -1018,6 +1010,54 @@ end
10181010
Check out the manipulate! method, which makes it easy for you to write your own
10191011
manipulation methods.
10201012

1013+
### Using Vips
1014+
1015+
CarrierWave version 2.2.0 added support for the `libvips` image processing library, through [ImageProcessing::Vips](https://github.com/janko/image_processing/blob/master/doc/vips.md). Its functionality matches that of the RMagick and MiniMagick processors, but it uses less memory and offers [faster processing](https://github.com/libvips/libvips/wiki/Speed-and-memory-use). To use the Vips processing module you must first install `libvips`, for example:
1016+
1017+
````bash
1018+
$ sudo apt install libvips
1019+
````
1020+
1021+
You also need to tell your uploader to use Vips:
1022+
1023+
````ruby
1024+
class ImageFileUploader < CarrierWave::Uploader::Base
1025+
include CarrierWave::Vips
1026+
end
1027+
````
1028+
1029+
### List of available processing methods:
1030+
1031+
> [!NOTE]
1032+
> While the intetion is to provide uniform interfaces to al three processing libraries the availability and implementation of processing methods can <a href="supported-processing-methods">vary slightly between them</a>.
1033+
1034+
- `convert` - Changes the image encoding format to the given format (eg. jpg). This operation is treated specially to trigger the change of the file extension, so it matches with the format of the resulting file.
1035+
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
1036+
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
1037+
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
1038+
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
1039+
- `crop` - Crop the image to the contents of a box with the specified height and width, positioned a given number of pixels from the top and left. The original image edge will be retained should the bottom and/or right edge of the box fall outside the image bounds.
1040+
1041+
#### Supported processing methods
1042+
1043+
The following table shows which processing methods are supported by each processing library, and which parameters they accept:
1044+
1045+
Method|RMagick|MiniMagick|Vips
1046+
------|-----------------|-----------------|-----------------|
1047+
`convert`|`format`|`format`, `page`<sup>1</sup>|`format`, `page`<sup>1</sup>
1048+
`resize_to_limit`|`width`, `height`|`width`, `height`|`width`, `height`
1049+
`resize_to_fit`|`width`, `height`|`width`, `height`|`width`, `height`
1050+
`resize_to_fill`|`width`, `height`, `gravity`<sup>2</sup>|`width`, `height`, `gravity`<sup>2</sup>|`width`, `height`
1051+
`resize_and_pad`|`width`, `height`, `background`, `gravity`<sup>2</sup>|`width`, `height`, `background`, `gravity`<sup>2</sup>|`width`, `height`, `background`, `gravity`<sup>2</sup>
1052+
`resize_to_geometry_string`|`geometry_string`<sup>3</sup>|*not implemented*|*not implemented*
1053+
`crop`|`left`, `top`, `width`, `height`|`left`, `top`, `width`, `height`|`left`, `top`, `width`, `height`
1054+
1055+
<sup>1</sup>`page` refers to the page number when converting from PDF, frame number when converting from GIF, and layer number when converting from PSD.
1056+
1057+
<sup>2</sup>`gravity` refers to an image position given as one of `Center`, `North`, `NorthWest`, `West`, `SouthWest`, `South`, `SouthEast`, `East`, or `NorthEast`.
1058+
1059+
<sup>3</sup>`geometry_string` is an [ImageMagick geometry string](https://rmagick.github.io/imusage.html#geometry).
1060+
10211061
## Migrating from Paperclip
10221062

10231063
If you are using Paperclip, you can use the provided compatibility module:

carrierwave.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
3737
s.add_development_dependency "fog-local"
3838
s.add_development_dependency "fog-rackspace"
3939
s.add_development_dependency "mini_magick", ">= 3.6.0"
40+
4041
if RUBY_ENGINE != 'jruby'
4142
s.add_development_dependency "rmagick", ">= 2.16"
4243
end

lib/carrierwave/processing/mini_magick.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ def resize_to_fill(width, height, gravity='Center')
7979
def resize_and_pad(width, height, background=:transparent, gravity='Center')
8080
process :resize_and_pad => [width, height, background, gravity]
8181
end
82+
83+
def crop(left, top, width, height)
84+
process :crop => [left, top, width, height]
85+
end
8286
end
8387

8488
##
@@ -210,6 +214,31 @@ def resize_and_pad(width, height, background=:transparent, gravity='Center', com
210214
end
211215
end
212216

217+
##
218+
# Crop the image to the contents of a box positioned at [left] and [top], with the dimensions given
219+
# by [width] and [height]. The original image bottom/right edge is preserved if the cropping box falls
220+
# outside the image bounds.
221+
#
222+
# === Parameters
223+
#
224+
# [left (integer)] left edge of area to extract
225+
# [top (integer)] top edge of area to extract
226+
# [width (Integer)] width of area to extract
227+
# [height (Integer)] height of area to extract
228+
#
229+
# === Yields
230+
#
231+
# [MiniMagick::Image] additional manipulations to perform
232+
#
233+
def crop(left, top, width, height, combine_options: {}, &block)
234+
width, height = resolve_dimensions(width, height)
235+
236+
minimagick!(block) do |builder|
237+
builder.crop(left, top, width, height)
238+
.apply(combine_options)
239+
end
240+
end
241+
213242
##
214243
# Returns the width of the image in pixels.
215244
#

lib/carrierwave/processing/rmagick.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::Cen
102102
def resize_to_geometry_string(geometry_string)
103103
process :resize_to_geometry_string => [geometry_string]
104104
end
105+
106+
def crop(left, top, width, height)
107+
process :crop => [left, top, width, height]
108+
end
105109
end
106110

107111
##
@@ -260,6 +264,33 @@ def resize_to_geometry_string(geometry_string)
260264
end
261265
end
262266

267+
##
268+
# Crop the image to the contents of a box positioned at [left] and [top], with the dimensions given
269+
# by [width] and [height]. The original image bottom/right edge is preserved if the cropping box falls
270+
# outside the image bounds.
271+
#
272+
# === Parameters
273+
#
274+
# [left (integer)] left edge of area to extract
275+
# [top (integer)] top edge of area to extract
276+
# [width (Integer)] width of area to extract
277+
# [height (Integer)] height of area to extract
278+
#
279+
# === Yields
280+
#
281+
# [Magick::Image] additional manipulations to perform
282+
#
283+
def crop(left, top, width, height, combine_options: {})
284+
width = dimension_from width
285+
height = dimension_from height
286+
287+
manipulate! do |img|
288+
img.crop!(left, top, width, height)
289+
img = yield(img) if block_given?
290+
img
291+
end
292+
end
293+
263294
##
264295
# Returns the width of the image.
265296
#

lib/carrierwave/processing/vips.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ def resize_to_fill(width, height, gravity='centre')
7878
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
7979
process :resize_and_pad => [width, height, background, gravity, alpha]
8080
end
81+
82+
def crop(left, top, width, height)
83+
process :crop => [left, top, width, height]
84+
end
8185
end
8286

8387
##
@@ -208,6 +212,33 @@ def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, c
208212
end
209213
end
210214

215+
##
216+
# Crop the image to the contents of a box positioned at [left] and [top], with the dimensions given
217+
# by [width] and [height]. The original image bottom/right edge is preserved if the cropping box falls
218+
# outside the image bounds.
219+
#
220+
# === Parameters
221+
#
222+
# [left (integer)] left edge of area to extract
223+
# [top (integer)] top edge of area to extract
224+
# [width (Integer)] width of area to extract
225+
# [height (Integer)] height of area to extract
226+
#
227+
# === Yields
228+
#
229+
# [Vips::Image] additional manipulations to perform
230+
#
231+
def crop(left, top, width, height, combine_options: {})
232+
width, height = resolve_dimensions(width, height)
233+
width = vips_image.width - left if width + left > vips_image.width
234+
height = vips_image.height - top if height + top > vips_image.height
235+
236+
vips! do |builder|
237+
builder.crop(left, top, width, height)
238+
.apply(combine_options)
239+
end
240+
end
241+
211242
##
212243
# Returns the width of the image in pixels.
213244
#

spec/processing/mini_magick_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,18 @@
188188
end
189189
end
190190

191+
describe "#crop" do
192+
it "extracts an area defined from the left and top positions, with the given width and height" do
193+
instance.crop(70, 40, 500, 400)
194+
expect(instance).to have_dimensions(500, 400)
195+
end
196+
197+
it "retains original image boundary if either edge of the cropping box falls outside it" do
198+
instance.crop(140, 80, 500, 480)
199+
expect(instance).to have_dimensions(500, 400)
200+
end
201+
end
202+
191203
describe "#width and #height" do
192204
it "returns the width and height of the image" do
193205
instance.resize_to_fill(200, 300)

spec/processing/rmagick_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@
185185
end
186186
end
187187

188+
describe "#crop" do
189+
it "extracts an area defined from the left and top positions, with the given width and height" do
190+
instance.crop(70, 40, 500, 400)
191+
expect(instance).to have_dimensions(500, 400)
192+
end
193+
194+
it "retains original image boundary if either edge of the cropping box falls outside it" do
195+
instance.crop(140, 80, 500, 480)
196+
expect(instance).to have_dimensions(500, 400)
197+
end
198+
end
199+
188200
describe "#manipulate!" do
189201
let(:image) { ::Magick::Image.read(landscape_file_path) }
190202

spec/processing/vips_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,18 @@
188188
end
189189
end
190190

191+
describe "#crop" do
192+
it "extracts an area defined from the left and top positions, with the given width and height" do
193+
instance.crop(70, 40, 500, 400)
194+
expect(instance).to have_dimensions(500, 400)
195+
end
196+
197+
it "retains original image boundary if either edge of the cropping box falls outside it" do
198+
instance.crop(140, 80, 500, 480)
199+
expect(instance).to have_dimensions(500, 400)
200+
end
201+
end
202+
191203
describe "#width and #height" do
192204
it "returns the width and height of the image" do
193205
instance.resize_to_fill(200, 300)

0 commit comments

Comments
 (0)