Module: ImagePack

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

Defined Under Namespace

Modules: Backend 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.1"

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



1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
# File 'ext/image_pack/image_pack.c', line 1749

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_context_t *ctx = ip_context_new();
    if (!ctx)
        rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");

    ip_output_kind_t out_kind = ip_parse_output_kind(output_kind);
    ctx->algo = ip_parse_algo(algo);
    ctx->quality = NUM2INT(quality);
    ctx->selected_quality = ctx->quality;
    ip_validate_quality_or_raise(ctx);
    ctx->min_ssim = NUM2DBL(min_ssim);
    ctx->ssim_guard_enabled = ctx->min_ssim > 0.0;
    ip_validate_min_ssim_or_raise(ctx);
    ctx->mozjpeg_trellis_enabled = ip_bool_value(mozjpeg_trellis);
    ctx->progressive = ip_bool_value(progressive);
    ctx->strip_metadata = ip_bool_value(strip_metadata);
    ctx->requested_execution = ip_parse_execution(execution);
    ctx->cancellable_requested = ip_bool_value(cancellable);
    ctx->has_scheduler = ip_bool_value(has_scheduler);
    apply_configuration(self, ctx);

    if (!ip_prepare_input_bytes(ctx, input, ip_parse_input_kind(input_kind)) ||
        !ip_prepare_output_path(ctx, output, out_kind)) {
        VALUE exception = ip_status_to_exception(ctx->status);
        char message[512];
        snprintf(message, sizeof(message), "%s", ctx->error_message);
        ip_context_free(ctx);
        rb_raise(exception, "%s", message[0] ? message : "invalid JPEG input");
    }

    if (ctx->requested_execution == IP_EXEC_AUTO && ctx->input_size < ctx->direct_input_threshold &&
        !ip_inspect_jpeg_header(ctx)) {
        VALUE exception = ip_status_to_exception(ctx->status);
        char message[512];
        snprintf(message, sizeof(message), "%s", ctx->error_message);
        ip_context_free(ctx);
        rb_raise(exception, "%s", message[0] ? message : "invalid JPEG input");
    }

    ip_run_context(ctx);
    VALUE result = ip_finish_output(ctx, out_kind);
    ip_context_free(ctx);
    return result;
}

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



1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
# File 'ext/image_pack/image_pack.c', line 1797

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 progressive, VALUE execution,
                                      VALUE cancellable, VALUE has_scheduler) {
    ip_context_t *ctx = ip_context_new();
    if (!ctx)
        rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");

    ip_output_kind_t out_kind = ip_parse_output_kind(output_kind);
    ctx->algo = ip_parse_algo(algo);
    ctx->quality = NUM2INT(quality);
    ip_validate_quality_or_raise(ctx);
    ctx->progressive = ip_bool_value(progressive);
    ctx->strip_metadata = 1;
    ctx->requested_execution = ip_parse_execution(execution);
    ctx->cancellable_requested = ip_bool_value(cancellable);
    ctx->has_scheduler = ip_bool_value(has_scheduler);
    apply_configuration(self, ctx);

    if (!ip_prepare_pixels(ctx, buffer, NUM2INT(width), NUM2INT(height), NUM2INT(channels)) ||
        !ip_prepare_output_path(ctx, output, out_kind)) {
        VALUE exception = ip_status_to_exception(ctx->status);
        char message[512];
        snprintf(message, sizeof(message), "%s", ctx->error_message);
        ip_context_free(ctx);
        rb_raise(exception, "%s", message[0] ? message : "invalid pixel input");
    }

    validate_limits_for_pixels(ctx);
    if (ctx->status != IP_OK) {
        VALUE exception = ip_status_to_exception(ctx->status);
        char message[512];
        snprintf(message, sizeof(message), "%s", ctx->error_message);
        ip_context_free(ctx);
        rb_raise(exception, "%s", message);
    }

    ip_run_context(ctx);
    VALUE result = ip_finish_output(ctx, out_kind);
    ip_context_free(ctx);
    return result;
}

.__inspect_image(input, input_kind) ⇒ Object



746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
# File 'ext/image_pack/image_pack.c', line 746

static VALUE ip_inspect_image_entry(VALUE self, VALUE input, VALUE input_kind) {
    (void)self;
    ip_context_t *ctx = ip_context_new();
    if (!ctx)
        rb_raise(rb_eImagePackOutOfMemoryError, "failed to allocate native context");

    if (!ip_prepare_input_bytes(ctx, input, ip_parse_input_kind(input_kind)) ||
        !ip_inspect_jpeg_header(ctx)) {
        VALUE exception = ip_status_to_exception(ctx->status);
        char message[512];
        snprintf(message, sizeof(message), "%s", ctx->error_message);
        ip_context_free(ctx);
        rb_raise(exception, "%s", message[0] ? message : "failed to inspect JPEG image");
    }

    VALUE hash = rb_hash_new();
    rb_hash_aset(hash, ID2SYM(rb_intern("format")), ID2SYM(rb_intern("jpeg")));
    rb_hash_aset(hash, ID2SYM(rb_intern("width")), INT2NUM(ctx->width));
    rb_hash_aset(hash, ID2SYM(rb_intern("height")), INT2NUM(ctx->height));
    rb_hash_aset(hash, ID2SYM(rb_intern("channels")), INT2NUM(ctx->channels));
    rb_hash_aset(hash, ID2SYM(rb_intern("bit_depth")), INT2NUM(ctx->bit_depth));
    rb_hash_aset(hash, ID2SYM(rb_intern("decoded_bytes")), SIZET2NUM(ctx->decoded_bytes));
    ip_context_free(ctx);
    return hash;
}

.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



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
# File 'lib/image_pack.rb', line 49

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_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



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
142
143
144
145
146
147
148
# File 'lib/image_pack.rb', line 86

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
    seed_jpeg = __compress_pixels(buffer,
                                  width.to_i, height.to_i, channels.to_i,
                                  nil, :return_string,
                                  ALGO_TO_NATIVE.fetch(algo), 95,
                                  progressive ? 1 : 0,
                                  :direct,
                                  0,
                                  0)
    return compress(seed_jpeg,
                    output: output,
                    algo: algo,
                    quality: quality,
                    min_ssim: min_ssim,
                    progressive: progressive,
                    execution: execution,
                    cancellable: cancellable)
  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,
                    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



150
151
152
# File 'lib/image_pack.rb', line 150

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