Skip to content

Commit

Permalink
feat: add cropping via vips
Browse files Browse the repository at this point in the history
  • Loading branch information
knarewski committed Nov 28, 2024
1 parent fd87f71 commit cf25f98
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 3 deletions.
35 changes: 35 additions & 0 deletions lib/morandi/crop_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,40 @@ def apply_crop(pixbuf, x_coord, y_coord, width, height, fill_col = 0xffffffff)
end
pixbuf
end

def apply_crop_vips(img, x_coord, y_coord, width, height)
if x_coord.negative? ||
y_coord.negative? ||
((x_coord + width) > img.width) ||
((y_coord + height) > img.height)

extract_area_x = [0, x_coord].max
extract_area_y = [0, y_coord].max
area_to_copy = img.extract_area(extract_area_x, extract_area_y, img.width - extract_area_x,
img.height - extract_area_y)

fill_colour = [255, 255, 255]
pixel = (Vips::Image.black(1, 1).colourspace(:srgb) + fill_colour).cast(img.format)
canvas = pixel.embed 0, 0, width, height, extend: :copy
canvas = canvas.copy xres: img.xres, yres: img.yres

cropped = canvas.composite(area_to_copy, :over, x: [-x_coord, 0].max,
y: [-y_coord, 0].max,
compositing_space: area_to_copy.interpretation)

# Because image is drawn on an opaque white, alpha doesn't matter at this point anyway, so let's strip the
# alpha channel from the output. According to #composite docs, the resulting image always has alpha channel,
# but I added a guard to avoid regressions if that ever changes.
cropped = cropped.extract_band(0, n: cropped.bands - 1) if cropped.has_alpha?
cropped
else
x_coord = x_coord.clamp(0, img.width)
y_coord = y_coord.clamp(0, img.height)
width = width.clamp(1, img.width - x_coord)
height = height.clamp(1, img.height - y_coord)

img.crop(x_coord, y_coord, width, height)
end
end
end
end
19 changes: 19 additions & 0 deletions lib/morandi/vips_image_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def process!
@scale = 1.0
end

apply_crop!
apply_filters!

return unless @options['output.limit'] && @output_width && @output_height
Expand All @@ -82,6 +83,24 @@ def write_to_jpeg(write_to, quality = nil)

private

def apply_crop!
crop = @options['crop']

return if crop.nil? && @options['image.auto-crop'].eql?(false)

crop = crop.split(',').map(&:to_i) if crop.is_a?(String) && crop =~ /^\d+,\d+,\d+,\d+/

crop = nil unless crop.is_a?(Array) && crop.size.eql?(4) && crop.all? do |i|
i.is_a?(Numeric)
end
# can't crop, won't crop
return if @output_width.nil? && @output_height.nil? && crop.nil?

crop = crop.map { |s| (s.to_f * @scale).floor } if crop && not_equal_to_one(@scale)
crop ||= Morandi::CropUtils.autocrop_coords(@img.width, @img.height, @output_width, @output_height)
@img = Morandi::CropUtils.apply_crop_vips(@img, crop[0], crop[1], crop[2], crop[3])
end

def apply_filters!
filter_name = @options['fx']
return unless SUPPORTED_FILTERS.include?(filter_name)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions spec/morandi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
end
end

describe 'with a big image and a bigger cropped area to fill', vips_wip: processor_name == 'vips' do
describe 'with a big image and a bigger cropped area to fill' do
let(:options) do
{
'crop' => '0,477,15839,18804',
Expand Down Expand Up @@ -148,7 +148,7 @@
end
end

context 'when give a "crop" option', vips_wip: processor_name == 'vips' do
context 'when give a "crop" option' do
let(:cropped_width) { 300 }
let(:cropped_height) { 300 }

Expand Down Expand Up @@ -218,7 +218,7 @@
expect(processed_image_width).to eq(1)
expect(processed_image_height).to eq(1)

expect(file_out).to match_reference_image('plasma-cropped-1x1')
expect(file_out).to match_reference_image(reference_image_prefix, 'plasma-cropped-1x1')
end
end
end
Expand Down

0 comments on commit cf25f98

Please sign in to comment.