Class: SafeImage::Processor

Inherits:
Object
  • Object
show all
Defined in:
lib/safe_image/processor.rb

Constant Summary collapse

SUPPORTED_INPUTS =
%w[jpg jpeg png gif webp heic heif avif jxl].freeze
SUPPORTED_OUTPUTS =
%w[jpg jpeg png gif webp avif jxl].freeze
OPTIMIZABLE_OUTPUTS =

Formats the post-processing optimizer tools understand; other outputs skip the optimize pass instead of erroring.

%w[jpg png].freeze

Instance Method Summary collapse

Constructor Details

#initialize(max_pixels: nil, chroma_subsampling: :auto) ⇒ Processor

Returns a new instance of Processor.



15
16
17
18
# File 'lib/safe_image/processor.rb', line 15

def initialize(max_pixels: nil, chroma_subsampling: :auto)
  @max_pixels = max_pixels || SafeImage.config.max_pixels
  @chroma_subsampling = chroma_subsampling
end

Instance Method Details

#probe(path) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/safe_image/processor.rb', line 20

def probe(path)
  input = safe_existing_file!(path)
  info = Native.probe(input.to_s)
  validate_pixels!(info.fetch(:width), info.fetch(:height))
  Result.new(
    input: input.to_s,
    output: nil,
    input_format: info.fetch(:format),
    output_format: nil,
    width: info.fetch(:width),
    height: info.fetch(:height),
    filesize: File.size(input),
    backend: "libvips-direct",
    duration_ms: info.fetch(:duration_ms),
    optimizer: nil
  )
end

#thumbnail(input:, output:, width:, height:, format: nil, quality: 85, optimize: false, optimize_mode: :lossless) ⇒ Object

Raises:

  • (ArgumentError)


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
89
90
91
92
93
# File 'lib/safe_image/processor.rb', line 38

def thumbnail(input:, output:, width:, height:, format: nil, quality: 85, optimize: false, optimize_mode: :lossless)
  input = safe_existing_file!(input)
  output = safe_output_path!(output)
  width = Integer(width)
  height = Integer(height)
  quality = Integer(quality)
  raise ArgumentError, "width and height must be positive" if width <= 0 || height <= 0
  raise ArgumentError, "quality must be 1..100" unless (1..100).cover?(quality)

  out_format = (format || output.extname.delete_prefix(".")).downcase
  out_format = "jpg" if out_format == "jpeg"
  unless SUPPORTED_OUTPUTS.include?(out_format)
    raise UnsupportedFormatError, "unsupported output format: #{out_format.inspect}"
  end

  output.dirname.mkpath
  backend = SafeImage.config.backend
  info =
    if out_format == "jpg" && use_jpegli_for_generated_jpeg?(backend)
      jpegli_thumbnail(input: input, output: output, width: width, height: height, quality: quality, source_format: input.extname.delete_prefix(".").downcase)
    else
      case backend
      when :vips
        Native.thumbnail(input.to_s, output.to_s, width, height, out_format, quality, @max_pixels)
      when :imagemagick
        probe_info = ImageMagickBackend.probe(input.to_s)
        validate_pixels!(probe_info.fetch(:width), probe_info.fetch(:height))
        ImageMagickBackend.thumbnail(
          input: input.to_s,
          output: output.to_s,
          width: width,
          height: height,
          format: out_format,
          quality: quality
        )
      end
    end

  opt_info = nil
  if optimize && OPTIMIZABLE_OUTPUTS.include?(out_format)
    opt_info = Optimizer.optimize(output, mode: optimize_mode, strip_metadata: true, quality: out_format == "jpg" ? quality : nil)
  end

  Result.new(
    input: input.to_s,
    output: output.to_s,
    input_format: info.fetch(:input_format),
    output_format: info.fetch(:output_format),
    width: info.fetch(:width),
    height: info.fetch(:height),
    filesize: File.size(output),
    backend: result_backend(info, backend),
    duration_ms: info.fetch(:duration_ms),
    optimizer: opt_info&.fetch(:tools, nil)
  )
end