Class: Morandi::VipsImageProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/morandi/vips_image_processor.rb

Overview

An alternative to ImageProcessor which is based on libvips for concurrent and less memory-intensive processing

Constant Summary collapse

RGB_LUMINANCE_EXTRACTION_FACTORS =

Colour filter related constants

[0.3086, 0.6094, 0.0820].freeze
SEPIA_MODIFIER =
[25, 5, -25].freeze
BLUETONE_MODIFIER =
[-10, 5, 25].freeze
COLOUR_FILTER_MODIFIERS =
{
  'sepia' => SEPIA_MODIFIER,
  'bluetone' => BLUETONE_MODIFIER
}.freeze
SUPPORTED_FILTERS =
COLOUR_FILTER_MODIFIERS.keys + ['greyscale']

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, user_options) ⇒ VipsImageProcessor

Returns a new instance of VipsImageProcessor.



47
48
49
50
51
52
53
54
55
# File 'lib/morandi/vips_image_processor.rb', line 47

def initialize(path, user_options)
  @path = path

  @options = user_options

  @size_limit_on_load_px = @options['output.max']
  @output_width = @options['output.width']
  @output_height = @options['output.height']
end

Class Method Details

.supports?(input, options) ⇒ Boolean

Returns:

  • (Boolean)


21
22
23
24
25
26
27
28
29
30
31
# File 'lib/morandi/vips_image_processor.rb', line 21

def self.supports?(input, options)
  return false unless input.is_a?(String)
  return false if options['brighten'].to_f != 0
  return false if options['contrast'].to_f != 0
  return false if options['sharpen'].to_f != 0
  return false if options['redeye']&.any?
  return false if options['border-style']
  return false if options['background-style']

  true
end

.with_global_options(cache_max:, concurrency:) ⇒ Object

Vips options are global, this method sets them for yielding, then restores to original



34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/morandi/vips_image_processor.rb', line 34

def self.with_global_options(cache_max:, concurrency:)
  previous_cache_max = Vips.cache_max
  previous_concurrency = Vips.concurrency

  Vips.cache_set_max(cache_max)
  Vips.concurrency_set(concurrency)

  yield
ensure
  Vips.cache_set_max(previous_cache_max)
  Vips.concurrency_set(previous_concurrency)
end

Instance Method Details

#process!Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/morandi/vips_image_processor.rb', line 57

def process!
  source_file_path = Morandi::SrgbConversion.perform(@path) || @path
  begin
    @img = Vips::Image.new_from_file(source_file_path)
  rescue Vips::Error => e
    # Match the known errors
    raise UnknownTypeError if /is not a known file format/.match?(e.message)
    raise CorruptImageError if /Premature end of JPEG file/.match?(e.message)

    # Re-raise generic Error when unknown
    raise Error, e.message
  end
  if @size_limit_on_load_px
    @scale = @size_limit_on_load_px.to_f / [@img.width, @img.height].max
    @img = @img.resize(@scale) if not_equal_to_one(@scale)
  else
    @scale = 1.0
  end

  apply_gamma!
  apply_rotate!
  apply_crop!
  apply_filters!

  if @options['output.limit'] && @output_width && @output_height
    scale_factor = [@output_width, @output_height].max.to_f / [@img.width, @img.height].max
    @img = @img.resize(scale_factor) if scale_factor < 1.0
  end

  strip_alpha!
  ensure_srgb!
end

#write_to_jpeg(target_path, quality = nil) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/morandi/vips_image_processor.rb', line 94

def write_to_jpeg(target_path, quality = nil)
  process!

  quality ||= @options.fetch('quality', 97)

  target_path_jpg = "#{target_path}.jpg" # Vips chooses format based on file extension, this ensures jpg
  @img.write_to_file(target_path_jpg, Q: quality)
  FileUtils.mv(target_path_jpg, target_path)
end

#write_to_png(_write_to, _orientation = :any) ⇒ Object



90
91
92
# File 'lib/morandi/vips_image_processor.rb', line 90

def write_to_png(_write_to, _orientation = :any)
  raise 'not implemented'
end