Module: ImagePack

Defined in:
lib/image_pack.rb,
lib/image_pack/errors.rb,
lib/image_pack/version.rb,
lib/image_pack/configuration.rb,
ext/image_pack/image_pack.c

Defined Under Namespace

Classes: CancelledError, Configuration, EncodeError, Error, InvalidArgumentError, InvalidImageError, LimitExceededError, OutOfMemoryError, QualityConstraintError, UnsupportedError

Constant Summary collapse

ALGOS =
%i[jpeg_turbo mozjpeg fast size].freeze
ALGO_TO_NATIVE =
{ jpeg_turbo: :jpeg_turbo, mozjpeg: :mozjpeg, fast: :jpeg_turbo, size: :mozjpeg }.freeze
EXECUTION_MODES =
%i[direct nogvl offload auto].freeze
DEFAULT_QUALITY =
82
DEFAULT_ALGO =
:mozjpeg
VERSION =
"0.2.2"
NATIVE_MOZJPEG_VERSION =
rb_str_new_cstr(VERSION)
NATIVE_SIMD =
Qfalse

Class Method Summary collapse

Class Method Details

.__compress_jpeg(input, input_kind, output, output_kind, algo, quality, min_ssim, mozjpeg_trellis, progressive, strip_metadata, execution, cancellable, has_scheduler) ⇒ Object



2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
# File 'ext/image_pack/image_pack.c', line 2242

static VALUE ip_compress_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
                                    VALUE output_kind, VALUE algo, VALUE quality, VALUE min_ssim,
                                    VALUE mozjpeg_trellis, VALUE progressive, VALUE strip_metadata,
                                    VALUE execution, VALUE cancellable, VALUE has_scheduler) {
    ip_compress_jpeg_call_t call = {
        self,           input,     input_kind,  output,          output_kind,
        algo,           quality,   min_ssim,    mozjpeg_trellis, progressive,
        strip_metadata, execution, cancellable, has_scheduler,   NULL};
    return rb_ensure(ip_compress_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
}

.__compress_pixels(buffer, width, height, channels, output, output_kind, algo, quality, min_ssim, progressive, execution, cancellable, has_scheduler) ⇒ Object



2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
# File 'ext/image_pack/image_pack.c', line 2290

static VALUE ip_compress_pixels_entry(VALUE self, VALUE buffer, VALUE width, VALUE height,
                                      VALUE channels, VALUE output, VALUE output_kind, VALUE algo,
                                      VALUE quality, VALUE min_ssim, VALUE progressive,
                                      VALUE execution, VALUE cancellable, VALUE has_scheduler) {
    ip_compress_pixels_call_t call = {
        self,    buffer,   width,       height,    channels,    output,        output_kind, algo,
        quality, min_ssim, progressive, execution, cancellable, has_scheduler, NULL};
    return rb_ensure(ip_compress_pixels_entry_body, (VALUE)&call, ip_call_cleanup,
                     (VALUE)&call.ctx);
}

.__inspect_image(input, input_kind) ⇒ Object



1007
1008
1009
1010
# File 'ext/image_pack/image_pack.c', line 1007

static VALUE ip_inspect_image_entry(VALUE self, VALUE input, VALUE input_kind) {
    ip_inspect_call_t call = {self, input, input_kind, NULL};
    return rb_ensure(ip_inspect_image_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
}

.__optimize_jpeg(input, input_kind, output, output_kind, progressive, strip_metadata, execution, cancellable, has_scheduler) ⇒ Object



2327
2328
2329
2330
2331
2332
2333
2334
# File 'ext/image_pack/image_pack.c', line 2327

static VALUE ip_optimize_jpeg_entry(VALUE self, VALUE input, VALUE input_kind, VALUE output,
                                    VALUE output_kind, VALUE progressive, VALUE strip_metadata,
                                    VALUE execution, VALUE cancellable, VALUE has_scheduler) {
    ip_optimize_jpeg_call_t call = {
        self,           input,     input_kind,  output,        output_kind, progressive,
        strip_metadata, execution, cancellable, has_scheduler, NULL};
    return rb_ensure(ip_optimize_jpeg_entry_body, (VALUE)&call, ip_call_cleanup, (VALUE)&call.ctx);
}

.build_infoObject



49
50
51
52
53
54
55
# File 'lib/image_pack.rb', line 49

def build_info
  {
    version: VERSION,
    mozjpeg: defined?(NATIVE_MOZJPEG_VERSION) ? NATIVE_MOZJPEG_VERSION : nil,
    simd: defined?(NATIVE_SIMD) ? NATIVE_SIMD : nil
  }
end

.compress(input, output: nil, algo: DEFAULT_ALGO, quality: nil, min_ssim: nil, mozjpeg_trellis: true, progressive: false, strip_metadata: true, execution: nil, cancellable: false) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/image_pack.rb', line 106

def compress(input,
             output: nil,
             algo: DEFAULT_ALGO,
             quality: nil,
             min_ssim: nil,
             mozjpeg_trellis: true,
             progressive: false,
             strip_metadata: true,
             execution: nil,
             cancellable: false)
  validate_algo!(algo)
  validate_min_ssim!(min_ssim)
  validate_mozjpeg_trellis!(mozjpeg_trellis)
  quality_was_given = !quality.nil?
  effective_quality = quality_was_given ? quality : DEFAULT_QUALITY
  effective_quality = 1 if min_ssim && !quality_was_given
  validate_quality!(effective_quality)
  execution ||= configuration.execution
  validate_execution!(execution)
  validate_cancellable!(algo, execution, cancellable)

  normalized_input_kind = input_kind!(input)
  normalized_output_kind = output_kind!(output)
  has_scheduler = fiber_scheduler_active?

  __compress_jpeg(input, normalized_input_kind,
                  output, normalized_output_kind,
                  ALGO_TO_NATIVE.fetch(algo), effective_quality.to_i,
                  min_ssim ? min_ssim.to_f : 0.0,
                  mozjpeg_trellis ? 1 : 0,
                  progressive ? 1 : 0,
                   ? 1 : 0,
                  execution,
                  cancellable ? 1 : 0,
                  has_scheduler ? 1 : 0)
end

.compress_bytes(bytes, **options) ⇒ Object



57
58
59
60
61
# File 'lib/image_pack.rb', line 57

def compress_bytes(bytes, **options)
  raise InvalidArgumentError, "bytes must be a String" unless bytes.is_a?(String)

  compress(bytes.b, **options)
end

.compress_file(path, **options) ⇒ Object



63
64
65
66
67
68
# File 'lib/image_pack.rb', line 63

def compress_file(path, **options)
  pathname = Pathname(path)
  raise InvalidArgumentError, "input path does not exist: #{pathname}" unless pathname.file?

  compress(pathname, **options)
end

.compress_pixels(buffer, width:, height:, channels:, output: nil, algo: DEFAULT_ALGO, quality: DEFAULT_QUALITY, min_ssim: nil, progressive: false, drop_alpha: nil, execution: nil, cancellable: false) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/image_pack.rb', line 143

def compress_pixels(buffer,
                    width:,
                    height:,
                    channels:,
                    output: nil,
                    algo: DEFAULT_ALGO,
                    quality: DEFAULT_QUALITY,
                    min_ssim: nil,
                    progressive: false,
                    drop_alpha: nil,
                    execution: nil,
                    cancellable: false)
  validate_algo!(algo)
  validate_min_ssim!(min_ssim)
  validate_quality!(quality)
  validate_dimensions!(width, height, channels)
  execution ||= configuration.execution
  validate_execution!(execution)
  validate_cancellable!(algo, execution, cancellable)

  if channels.to_i == 4
    case drop_alpha
    when nil
      warn "ImagePack.compress_pixels: RGBA input has its alpha channel " \
           "discarded (JPEG cannot store alpha). Pass drop_alpha: true to " \
           "silence this warning, or drop_alpha: false to raise instead."
    when false
      raise UnsupportedError,
            "JPEG cannot store an alpha channel. Pass drop_alpha: true to drop it explicitly."
    end
  end

  normalized_output_kind = output_kind!(output)
  has_scheduler = fiber_scheduler_active?

  if min_ssim && channels.to_i == 4
    raise UnsupportedError, "min_ssim is not supported for RGBA input"
  end

  __compress_pixels(buffer,
                    width.to_i, height.to_i, channels.to_i,
                    output, normalized_output_kind,
                    ALGO_TO_NATIVE.fetch(algo), quality.to_i,
                    min_ssim ? min_ssim.to_f : 0.0,
                    progressive ? 1 : 0,
                    execution,
                    cancellable ? 1 : 0,
                    has_scheduler ? 1 : 0)
end

.configurationObject



38
39
40
# File 'lib/image_pack.rb', line 38

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:



42
43
44
45
46
47
# File 'lib/image_pack.rb', line 42

def configure
  return configuration unless block_given?

  yield(configuration)
  configuration
end

.inspect_image(input) ⇒ Object



193
194
195
# File 'lib/image_pack.rb', line 193

def inspect_image(input)
  __inspect_image(input, input_kind!(input))
end

.optimize_bytes(bytes, **options) ⇒ Object



70
71
72
73
74
# File 'lib/image_pack.rb', line 70

def optimize_bytes(bytes, **options)
  raise InvalidArgumentError, "bytes must be a String" unless bytes.is_a?(String)

  optimize_jpeg(bytes.b, **options)
end

.optimize_file(path, **options) ⇒ Object



76
77
78
79
80
81
# File 'lib/image_pack.rb', line 76

def optimize_file(path, **options)
  pathname = Pathname(path)
  raise InvalidArgumentError, "input path does not exist: #{pathname}" unless pathname.file?

  optimize_jpeg(pathname, **options)
end

.optimize_jpeg(input, output: nil, progressive: true, strip_metadata: false, execution: nil, cancellable: false) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/image_pack.rb', line 83

def optimize_jpeg(input,
                  output: nil,
                  progressive: true,
                  strip_metadata: false,
                  execution: nil,
                  cancellable: false)
  execution ||= configuration.execution
  validate_execution!(execution)
  validate_cancellable!(:lossless_optimize, execution, cancellable)

  normalized_input_kind = input_kind!(input)
  normalized_output_kind = output_kind!(output)
  has_scheduler = fiber_scheduler_active?

  __optimize_jpeg(input, normalized_input_kind,
                  output, normalized_output_kind,
                  progressive ? 1 : 0,
                   ? 1 : 0,
                  execution,
                  cancellable ? 1 : 0,
                  has_scheduler ? 1 : 0)
end