pura-image 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +55 -128
- data/lib/image_processing/pura.rb +14 -25
- data/lib/pura/image/legacy_variation.rb +16 -0
- data/lib/pura/image/operations.rb +20 -9
- data/lib/pura/image/processor.rb +7 -8
- data/lib/pura/image/railtie.rb +16 -37
- data/lib/pura/image/transformer.rb +28 -0
- data/lib/pura/image/version.rb +1 -1
- data/pura-image.gemspec +7 -7
- metadata +45 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e595cde6db52041e2cfd43fe678c8f42d2f02396db44d6d1f72a77db96f15d7
|
|
4
|
+
data.tar.gz: 320aafe82456416eb38b45b53dae2f452118a6dd0bcbc1ef30ff13661f735125
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca3bd535aca6ace92343ec86132c78ca08a87f073b5e5a266a40522684dfb7d54297c25543f82a6216c5692cc1ca46af6ea535c08376dd7927fb86fc0b0fa611
|
|
7
|
+
data.tar.gz: bcf34c74223f1b4e6d3f5cfceab9ec4143152adf086bdb30ada1fb9d0a6b096efe7e111b6e10affd477b4c6cdfcd43cf009f8e881887278b000465a85f63b499
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,113 +1,84 @@
|
|
|
1
1
|
# pura-image
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Image processing in Ruby, without installing libvips or ImageMagick. Bundles the **pura-*** codec gems and provides a pipeline API and Rails Active Storage integration.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
Stop installing system libraries just to resize an image in Rails.
|
|
5
|
+
## Installation
|
|
8
6
|
|
|
9
|
-
```
|
|
7
|
+
```ruby
|
|
10
8
|
# Gemfile
|
|
11
|
-
|
|
12
|
-
```
|
|
9
|
+
gem "pura-image"
|
|
13
10
|
|
|
14
|
-
```ruby
|
|
15
11
|
# config/application.rb
|
|
16
12
|
config.active_storage.variant_processor = :pura
|
|
17
13
|
```
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
# Dockerfile
|
|
21
|
-
- RUN apt-get install -y libvips-dev imagemagick
|
|
22
|
-
```
|
|
15
|
+
The adapter does not require `image_processing`, `ruby-vips`, or `mini_magick`. Remove `image_processing` from your Gemfile if nothing else uses it. Requiring pura-image does not change your processor setting; select `:pura` explicitly.
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
The codecs are written in Ruby. PNG compression uses Ruby's standard `zlib` library, which must be available in your Ruby runtime. No additional image library or image-specific native extension needs to be installed.
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
- ✅ **7 formats** — JPEG, PNG, BMP, GIF, TIFF, ICO/CUR, WebP
|
|
28
|
-
- ✅ **Some operations are faster than C** (no process-spawn overhead — see [Benchmark](#benchmark))
|
|
29
|
-
- ✅ **Runs on ruby.wasm, JRuby, TruffleRuby**
|
|
19
|
+
## Supported formats and limitations
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
All decoded images currently use **8-bit RGB** pixels. Alpha, color profiles, EXIF metadata, and animation are not preserved. This matters for transparent logos and photographs whose orientation is stored in EXIF; pura-image does not yet auto-orient them.
|
|
32
22
|
|
|
33
|
-
| Format | Decode | Encode |
|
|
34
|
-
|
|
35
|
-
| JPEG |
|
|
36
|
-
| PNG |
|
|
37
|
-
| BMP |
|
|
38
|
-
| GIF |
|
|
39
|
-
| TIFF |
|
|
40
|
-
| ICO
|
|
41
|
-
|
|
|
23
|
+
| Format | Decode | Encode | Limitations |
|
|
24
|
+
|--------|--------|--------|-------------|
|
|
25
|
+
| JPEG | Baseline | Baseline | No progressive JPEG or EXIF orientation correction |
|
|
26
|
+
| PNG | Non-interlaced | RGB | No Adam7; alpha/tRNS discarded; 16-bit input reduced to 8-bit |
|
|
27
|
+
| BMP | Supported by pura-bmp | 24-bit RGB | Alpha is not preserved |
|
|
28
|
+
| GIF | First image | Single image | Animation is not preserved; transparent pixels are flattened |
|
|
29
|
+
| TIFF | 8-bit strips: uncompressed, LZW, PackBits | Uncompressed | First IFD only; alpha is discarded |
|
|
30
|
+
| ICO | BMP/PNG entries | PNG entries | Alpha is not preserved |
|
|
31
|
+
| CUR | Read through pura-ico | Not supported | Saving `.cur` raises `ArgumentError` |
|
|
32
|
+
| WebP | VP8 lossy | VP8L lossless | No VP8L/VP8X decode; **encoded WebP cannot be read back by pura-webp yet** |
|
|
42
33
|
|
|
43
|
-
Format
|
|
44
|
-
|
|
45
|
-
## Installation
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
gem install pura-image
|
|
49
|
-
```
|
|
34
|
+
See each [pura-* codec repository](https://github.com/komagata?tab=repositories&q=pura-) for its detailed restrictions. WebP also lacks the VP8 loop filter and multi-partition frame support. Format detection uses magic bytes; output format comes from the destination extension.
|
|
50
35
|
|
|
51
36
|
## Usage
|
|
52
37
|
|
|
53
38
|
```ruby
|
|
54
39
|
require "pura-image"
|
|
55
40
|
|
|
56
|
-
# Load any format (auto-detected from magic bytes)
|
|
57
41
|
image = Pura::Image.load("photo.jpg")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# Save to any format (detected from extension)
|
|
63
|
-
Pura::Image.save(image, "output.png")
|
|
64
|
-
|
|
65
|
-
# Convert between formats
|
|
66
|
-
Pura::Image.convert("input.bmp", "output.jpg", quality: 85)
|
|
67
|
-
Pura::Image.convert("photo.tiff", "photo.png")
|
|
42
|
+
thumb = image.resize_to_limit(800, 600).rotate(90).grayscale
|
|
43
|
+
Pura::Image.save(thumb, "thumb.png", compression: 6)
|
|
44
|
+
Pura::Image.convert("photo.bmp", "photo.jpg", quality: 85)
|
|
68
45
|
```
|
|
69
46
|
|
|
70
|
-
|
|
47
|
+
Operations return a new image:
|
|
71
48
|
|
|
72
|
-
|
|
49
|
+
| Operation | Behavior |
|
|
50
|
+
|-----------|----------|
|
|
51
|
+
| `resize_to_limit(w, h)` | Fit without enlarging |
|
|
52
|
+
| `resize_to_fit(w, h)` | Fit, allowing enlargement |
|
|
53
|
+
| `resize_to_fill(w, h)` | Fill, then center-crop |
|
|
54
|
+
| `resize_to_cover(w, h)` | Cover without cropping |
|
|
55
|
+
| `resize_and_pad(w, h, background: [0, 0, 0])` | Fit and center on an RGB background |
|
|
56
|
+
| `crop(x, y, w, h)` | Crop a positive integer region entirely inside the image |
|
|
57
|
+
| `rotate(degrees)` | Multiples of 90 degrees only |
|
|
58
|
+
| `grayscale` | Convert RGB to equal channels |
|
|
59
|
+
| `strip` | Return a copy; the current image model already discards metadata |
|
|
73
60
|
|
|
74
|
-
|
|
75
|
-
image = Pura::Image.load("photo.jpg")
|
|
61
|
+
`resize_to_limit` and `resize_to_fit` accept one `nil` dimension to leave that side unbounded. Supply at least one positive integer dimension. For example, `resize_to_fit(400, nil)` resizes to width 400 while preserving aspect ratio.
|
|
76
62
|
|
|
77
|
-
|
|
78
|
-
image.resize_to_limit(800, 600) # downsize only, keep aspect ratio
|
|
79
|
-
image.resize_to_fit(400, 400) # resize to fit, keep aspect ratio
|
|
80
|
-
image.resize_to_fill(400, 400) # fill exact size, center crop excess
|
|
81
|
-
image.resize_and_pad(400, 400) # fit within bounds, pad with black
|
|
82
|
-
image.resize_to_cover(400, 400) # cover bounds, no crop
|
|
83
|
-
|
|
84
|
-
# Transform
|
|
85
|
-
image.crop(10, 10, 200, 200) # crop region
|
|
86
|
-
image.rotate(90) # rotate 90/180/270 degrees
|
|
87
|
-
image.grayscale # convert to grayscale
|
|
88
|
-
|
|
89
|
-
# Chain operations
|
|
90
|
-
result = Pura::Image.load("photo.jpg")
|
|
91
|
-
.resize_to_limit(800, 600)
|
|
92
|
-
.rotate(90)
|
|
93
|
-
.grayscale
|
|
94
|
-
|
|
95
|
-
Pura::Image.save(result, "thumb.jpg", quality: 80)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Rails Active Storage Integration
|
|
63
|
+
These are a subset of the operations available in Vips/ImageMagick. Unsupported keyword options, such as `gravity:`, raise `ArgumentError`; they are not silently ignored. Saver options are passed to the selected codec: JPEG supports `quality:` and `subsampling:`, PNG supports `compression:`, and GIF supports `max_colors:`.
|
|
99
64
|
|
|
100
|
-
|
|
65
|
+
## Pipeline API
|
|
101
66
|
|
|
102
67
|
```ruby
|
|
103
|
-
|
|
104
|
-
gem "pura-image"
|
|
68
|
+
require "image_processing/pura"
|
|
105
69
|
|
|
106
|
-
|
|
107
|
-
|
|
70
|
+
pipeline = ImageProcessing::Pura.source("photo.jpg")
|
|
71
|
+
pipeline.resize_to_limit(400, nil).convert("png").call(destination: "thumb.png")
|
|
72
|
+
pipeline.resize_to_limit(100, 100).call(destination: "small.jpg")
|
|
73
|
+
|
|
74
|
+
ImageProcessing::Pura.valid_image?("photo.jpg") # true when this backend can decode it
|
|
108
75
|
```
|
|
109
76
|
|
|
110
|
-
|
|
77
|
+
The chainable API is provided by [pura-processing](https://github.com/komagata/pura-processing), adapted from `image_processing` under the MIT license. Only `loader(page: 0)` is supported; other pages and unknown loader options are rejected.
|
|
78
|
+
|
|
79
|
+
## Rails Active Storage
|
|
80
|
+
|
|
81
|
+
With the processor configuration above, use ordinary variants:
|
|
111
82
|
|
|
112
83
|
```ruby
|
|
113
84
|
class User < ApplicationRecord
|
|
@@ -121,63 +92,19 @@ end
|
|
|
121
92
|
<%= image_tag user.avatar.variant(:thumb) %>
|
|
122
93
|
```
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
### ImageProcessing::Pura API
|
|
127
|
-
|
|
128
|
-
```ruby
|
|
129
|
-
require "image_processing/pura"
|
|
130
|
-
|
|
131
|
-
# Same API as ImageProcessing::Vips
|
|
132
|
-
processed = ImageProcessing::Pura
|
|
133
|
-
.source("photo.jpg")
|
|
134
|
-
.resize_to_limit(400, 400)
|
|
135
|
-
.convert("png")
|
|
136
|
-
.call(destination: "output.png")
|
|
137
|
-
|
|
138
|
-
# Pipeline branching
|
|
139
|
-
pipeline = ImageProcessing::Pura.source("photo.jpg")
|
|
140
|
-
large = pipeline.resize_to_limit(800, 800).call(destination: "large.jpg")
|
|
141
|
-
medium = pipeline.resize_to_limit(500, 500).call(destination: "medium.jpg")
|
|
142
|
-
small = pipeline.resize_to_limit(300, 300).call(destination: "small.jpg")
|
|
143
|
-
|
|
144
|
-
# Validation
|
|
145
|
-
ImageProcessing::Pura.valid_image?("photo.jpg") #=> true
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Benchmark
|
|
149
|
-
|
|
150
|
-
400×400 image, Ruby 4.0.2 + YJIT vs ffmpeg (C + SIMD).
|
|
151
|
-
|
|
152
|
-
### Decode
|
|
95
|
+
The dedicated transformer handles supported image variants without the `image_processing` gem. This does not provide PDF/video previewers or a metadata analyzer. Image format and operation limitations above also apply to Rails variants.
|
|
153
96
|
|
|
154
|
-
|
|
155
|
-
|--------|--------|--------|-----------|
|
|
156
|
-
| TIFF | **14 ms** | 59 ms | 🚀 **4× faster** |
|
|
157
|
-
| BMP | **39 ms** | 59 ms | 🚀 **1.5× faster** |
|
|
158
|
-
| GIF | 77 ms | 65 ms | ~1× (comparable) |
|
|
159
|
-
| PNG | 111 ms | 60 ms | 1.9× slower |
|
|
160
|
-
| JPEG | 304 ms | 55 ms | 5.5× slower |
|
|
97
|
+
The integration CI exercises a real upload and variant generation on Rails 7.2, 8.0, and 8.1, with eager loading both enabled and disabled, and checks that `:disabled` is left unchanged.
|
|
161
98
|
|
|
162
|
-
|
|
99
|
+
## Browser demo
|
|
163
100
|
|
|
164
|
-
|
|
165
|
-
|--------|--------|--------|-----------|
|
|
166
|
-
| TIFF | **0.8 ms** | 58 ms | 🚀 **73× faster** |
|
|
167
|
-
| BMP | **35 ms** | 58 ms | 🚀 **1.7× faster** |
|
|
168
|
-
| PNG | **52 ms** | 61 ms | 🚀 **faster** |
|
|
169
|
-
| JPEG | 238 ms | 62 ms | 3.8× slower |
|
|
170
|
-
| GIF | 377 ms | 59 ms | 6.4× slower |
|
|
101
|
+
The [ruby.wasm demo](https://komagata.github.io/pura-image/) runs transformations in the browser. See [demo/README.md](demo/README.md). Runtime support depends on the Ruby version and available standard libraries; CRuby CI results should not be read as verification of every alternative Ruby implementation.
|
|
171
102
|
|
|
172
|
-
|
|
103
|
+
## Performance
|
|
173
104
|
|
|
174
|
-
|
|
105
|
+
The earlier 400×400 benchmark compared Ruby 4.0.2 + YJIT to a new ffmpeg process for every operation. Its measurements include ffmpeg process startup and do not establish that the codec is faster than an in-process C library such as libvips.
|
|
175
106
|
|
|
176
|
-
|
|
177
|
-
- **Works everywhere Ruby works** — CRuby, ruby.wasm, mruby, JRuby, TruffleRuby
|
|
178
|
-
- **Edge/Wasm ready** — browsers (ruby.wasm), sandboxed environments, no system libraries needed
|
|
179
|
-
- **Perfect for dev/CI** — no ImageMagick/libvips setup. `rails new` → image upload → it just works
|
|
180
|
-
- **7 formats, 1 interface** — unified API across JPEG, PNG, BMP, GIF, TIFF, ICO, WebP
|
|
107
|
+
For Rails deployment decisions, measure the complete decode–resize–encode pipeline with your own photographs, and compare output quality, file size, latency, and peak memory under the same settings. pura-image prioritizes installation convenience; native codecs are usually the appropriate comparison for throughput-sensitive workloads.
|
|
181
108
|
|
|
182
109
|
## License
|
|
183
110
|
|
|
@@ -10,10 +10,6 @@ module ImageProcessing
|
|
|
10
10
|
|
|
11
11
|
def self.valid_image?(file)
|
|
12
12
|
path = file.respond_to?(:path) ? file.path : file.to_s
|
|
13
|
-
data = File.binread(path, 8)
|
|
14
|
-
format = ::Pura::Image::Processor.detect_format(data)
|
|
15
|
-
return false unless format
|
|
16
|
-
|
|
17
13
|
::Pura::Image::Processor.load(path)
|
|
18
14
|
true
|
|
19
15
|
rescue StandardError
|
|
@@ -27,7 +23,9 @@ module ImageProcessing
|
|
|
27
23
|
false
|
|
28
24
|
end
|
|
29
25
|
|
|
30
|
-
def self.load_image(path_or_image,
|
|
26
|
+
def self.load_image(path_or_image, page: 0)
|
|
27
|
+
raise ArgumentError, "only the first image (page: 0) is supported" unless page.is_a?(Integer) && page.zero?
|
|
28
|
+
|
|
31
29
|
if path_or_image.is_a?(::Pura::Image::Wrapper)
|
|
32
30
|
path_or_image
|
|
33
31
|
elsif path_or_image.is_a?(String)
|
|
@@ -43,45 +41,36 @@ module ImageProcessing
|
|
|
43
41
|
::Pura::Image::Processor.save(wrapper, destination.to_s, **options)
|
|
44
42
|
end
|
|
45
43
|
|
|
46
|
-
def resize_to_limit(width, height
|
|
47
|
-
|
|
48
|
-
h = image.height
|
|
49
|
-
|
|
50
|
-
return image if w <= (width || w) && h <= (height || h)
|
|
51
|
-
|
|
52
|
-
width ||= w
|
|
53
|
-
height ||= h
|
|
54
|
-
image.resize_to_fit(width, height)
|
|
44
|
+
def resize_to_limit(width, height)
|
|
45
|
+
image.resize_to_limit(width, height)
|
|
55
46
|
end
|
|
56
47
|
|
|
57
|
-
def resize_to_fit(width, height
|
|
58
|
-
width ||= image.width
|
|
59
|
-
height ||= image.height
|
|
48
|
+
def resize_to_fit(width, height)
|
|
60
49
|
image.resize_to_fit(width, height)
|
|
61
50
|
end
|
|
62
51
|
|
|
63
|
-
def resize_to_fill(width, height
|
|
52
|
+
def resize_to_fill(width, height)
|
|
64
53
|
image.resize_to_fill(width, height)
|
|
65
54
|
end
|
|
66
55
|
|
|
67
|
-
def resize_and_pad(width, height, background: nil
|
|
56
|
+
def resize_and_pad(width, height, background: nil)
|
|
68
57
|
bg = background || [0, 0, 0]
|
|
69
58
|
image.resize_and_pad(width, height, background: bg)
|
|
70
59
|
end
|
|
71
60
|
|
|
72
|
-
def resize_to_cover(width, height
|
|
61
|
+
def resize_to_cover(width, height)
|
|
73
62
|
image.resize_to_cover(width, height)
|
|
74
63
|
end
|
|
75
64
|
|
|
76
|
-
def crop(left, top, width, height
|
|
65
|
+
def crop(left, top, width, height)
|
|
77
66
|
image.crop(left, top, width, height)
|
|
78
67
|
end
|
|
79
68
|
|
|
80
|
-
def rotate(degrees
|
|
69
|
+
def rotate(degrees)
|
|
81
70
|
image.rotate(degrees)
|
|
82
71
|
end
|
|
83
72
|
|
|
84
|
-
def colourspace(space
|
|
73
|
+
def colourspace(space)
|
|
85
74
|
if %w[b-w grey16].include?(space.to_s)
|
|
86
75
|
image.grayscale
|
|
87
76
|
else
|
|
@@ -89,11 +78,11 @@ module ImageProcessing
|
|
|
89
78
|
end
|
|
90
79
|
end
|
|
91
80
|
|
|
92
|
-
def strip
|
|
81
|
+
def strip
|
|
93
82
|
image.strip
|
|
94
83
|
end
|
|
95
84
|
|
|
96
|
-
def convert(format
|
|
85
|
+
def convert(format)
|
|
97
86
|
# Store desired format for save_image
|
|
98
87
|
@format = format.to_s.delete(".")
|
|
99
88
|
image
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pura
|
|
4
|
+
module Image
|
|
5
|
+
# Rails versions before variant_transformer= select the backend in Variation.
|
|
6
|
+
module LegacyVariation
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def transformer
|
|
10
|
+
return super unless ActiveStorage.variant_processor == :pura
|
|
11
|
+
|
|
12
|
+
Pura::Image::Transformer.new(transformations.except(:format))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -34,19 +34,14 @@ module Pura
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def resize_to_limit(max_width, max_height)
|
|
37
|
-
|
|
37
|
+
scale = fit_scale(max_width, max_height)
|
|
38
|
+
return dup_image if scale >= 1
|
|
38
39
|
|
|
39
|
-
scale
|
|
40
|
-
new_w = [(@width / scale).round, 1].max
|
|
41
|
-
new_h = [(@height / scale).round, 1].max
|
|
42
|
-
resize(new_w, new_h)
|
|
40
|
+
resize_scaled(scale)
|
|
43
41
|
end
|
|
44
42
|
|
|
45
43
|
def resize_to_fit(max_width, max_height)
|
|
46
|
-
|
|
47
|
-
new_w = [(@width / scale).round, 1].max
|
|
48
|
-
new_h = [(@height / scale).round, 1].max
|
|
49
|
-
resize(new_w, new_h)
|
|
44
|
+
resize_scaled(fit_scale(max_width, max_height))
|
|
50
45
|
end
|
|
51
46
|
|
|
52
47
|
def resize_to_fill(fill_width, fill_height)
|
|
@@ -91,6 +86,22 @@ module Pura
|
|
|
91
86
|
|
|
92
87
|
private
|
|
93
88
|
|
|
89
|
+
def fit_scale(max_width, max_height)
|
|
90
|
+
dimensions = [max_width, max_height].compact
|
|
91
|
+
unless dimensions.any? && dimensions.all? { |value| value.is_a?(Integer) && value.positive? }
|
|
92
|
+
raise ArgumentError, "provide at least one positive integer dimension"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
scales = []
|
|
96
|
+
scales << (max_width.to_f / @width) if max_width
|
|
97
|
+
scales << (max_height.to_f / @height) if max_height
|
|
98
|
+
scales.min
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def resize_scaled(scale)
|
|
102
|
+
resize([(@width * scale).round, 1].max, [(@height * scale).round, 1].max)
|
|
103
|
+
end
|
|
104
|
+
|
|
94
105
|
def dup_image
|
|
95
106
|
self.class.new(@width, @height, @pixels.dup)
|
|
96
107
|
end
|
data/lib/pura/image/processor.rb
CHANGED
|
@@ -22,8 +22,7 @@ module Pura
|
|
|
22
22
|
".gif" => :gif,
|
|
23
23
|
".tif" => :tiff, ".tiff" => :tiff,
|
|
24
24
|
".webp" => :webp,
|
|
25
|
-
".ico" => :ico
|
|
26
|
-
".cur" => :ico
|
|
25
|
+
".ico" => :ico
|
|
27
26
|
}.freeze
|
|
28
27
|
|
|
29
28
|
class << self
|
|
@@ -97,22 +96,22 @@ module Pura
|
|
|
97
96
|
def encode_with_format(image, path, format, **options)
|
|
98
97
|
case format
|
|
99
98
|
when :jpeg
|
|
100
|
-
Pura::Jpeg.encode(image, path,
|
|
99
|
+
Pura::Jpeg.encode(image, path, **options)
|
|
101
100
|
when :png
|
|
102
101
|
png_img = Pura::Png::Image.new(image.width, image.height, image.pixels)
|
|
103
|
-
Pura::Png.encode(png_img, path,
|
|
102
|
+
Pura::Png.encode(png_img, path, **options)
|
|
104
103
|
when :bmp
|
|
105
104
|
bmp_img = Pura::Bmp::Image.new(image.width, image.height, image.pixels)
|
|
106
|
-
Pura::Bmp.encode(bmp_img, path)
|
|
105
|
+
Pura::Bmp.encode(bmp_img, path, **options)
|
|
107
106
|
when :gif
|
|
108
107
|
gif_img = Pura::Gif::Image.new(image.width, image.height, image.pixels)
|
|
109
|
-
Pura::Gif.encode(gif_img, path)
|
|
108
|
+
Pura::Gif.encode(gif_img, path, **options)
|
|
110
109
|
when :tiff
|
|
111
110
|
tiff_img = Pura::Tiff::Image.new(image.width, image.height, image.pixels)
|
|
112
|
-
Pura::Tiff.encode(tiff_img, path)
|
|
111
|
+
Pura::Tiff.encode(tiff_img, path, **options)
|
|
113
112
|
when :ico
|
|
114
113
|
ico_img = Pura::Ico::Image.new(image.width, image.height, image.pixels)
|
|
115
|
-
Pura::Ico.encode(ico_img, path)
|
|
114
|
+
Pura::Ico.encode(ico_img, path, **options)
|
|
116
115
|
when :webp
|
|
117
116
|
raise ArgumentError, "WebP support requires the pura-webp gem" unless defined?(Pura::Webp)
|
|
118
117
|
|
data/lib/pura/image/railtie.rb
CHANGED
|
@@ -1,45 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def transformer
|
|
22
|
-
if ActiveStorage.variant_processor == :pura && ActiveStorage.variant_transformer.nil?
|
|
23
|
-
@pura_transformer ||= begin
|
|
24
|
-
klass = Class.new(ActiveStorage::Transformers::ImageProcessingTransformer) do
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def processor
|
|
28
|
-
ImageProcessing::Pura
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
klass.new(transformations.except(:format))
|
|
33
|
-
end
|
|
34
|
-
else
|
|
35
|
-
super
|
|
3
|
+
require "rails/railtie"
|
|
4
|
+
|
|
5
|
+
module Pura
|
|
6
|
+
module Image
|
|
7
|
+
class Railtie < Rails::Railtie
|
|
8
|
+
initializer "pura_image.configure_active_storage", after: "active_storage.configs" do |app|
|
|
9
|
+
app.config.after_initialize do
|
|
10
|
+
if defined?(ActiveStorage) && ActiveStorage.variant_processor == :pura
|
|
11
|
+
require_relative "transformer"
|
|
12
|
+
if ActiveStorage.respond_to?(:variant_transformer=)
|
|
13
|
+
ActiveStorage.variant_transformer = Pura::Image::Transformer
|
|
14
|
+
else
|
|
15
|
+
require_relative "legacy_variation"
|
|
16
|
+
ActiveSupport.on_load(:active_storage_blob) do
|
|
17
|
+
unless ActiveStorage::Variation < Pura::Image::LegacyVariation
|
|
18
|
+
ActiveStorage::Variation.prepend(Pura::Image::LegacyVariation)
|
|
36
19
|
end
|
|
37
20
|
end
|
|
38
21
|
end
|
|
39
|
-
|
|
40
|
-
unless ActiveStorage::Variation.ancestors.include?(variation_patch)
|
|
41
|
-
ActiveStorage::Variation.prepend(variation_patch)
|
|
42
|
-
end
|
|
43
22
|
end
|
|
44
23
|
end
|
|
45
24
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "image_processing/pura"
|
|
4
|
+
require "active_storage/transformers/transformer"
|
|
5
|
+
|
|
6
|
+
module Pura
|
|
7
|
+
module Image
|
|
8
|
+
# Uses Active Storage's backend-independent lifecycle without image_processing.
|
|
9
|
+
class Transformer < ActiveStorage::Transformers::Transformer
|
|
10
|
+
SUPPORTED_OPERATIONS = %i[
|
|
11
|
+
resize_to_limit resize_to_fit resize_to_fill resize_and_pad resize_to_cover
|
|
12
|
+
crop rotate grayscale colourspace strip
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def process(file, format:)
|
|
18
|
+
operations = transformations.each_with_object([]) do |(name, argument), result|
|
|
19
|
+
raise ArgumentError, "unsupported transformation: #{name}" unless SUPPORTED_OPERATIONS.include?(name.to_sym)
|
|
20
|
+
|
|
21
|
+
result << [name, argument] if argument.present?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ImageProcessing::Pura.source(file).convert(format).apply(operations).call
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/pura/image/version.rb
CHANGED
data/pura-image.gemspec
CHANGED
|
@@ -20,13 +20,13 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
|
|
21
21
|
spec.files = Dir["lib/**/*.rb"] + ["test/fixtures/test_64x64.jpg"] + ["pura-image.gemspec", "Gemfile", "Rakefile", "README.md", "LICENSE"]
|
|
22
22
|
|
|
23
|
-
spec.add_dependency "pura-jpeg", "~> 0.1"
|
|
24
|
-
spec.add_dependency "pura-png", "~> 0.1"
|
|
25
|
-
spec.add_dependency "pura-bmp", "~> 0.1"
|
|
26
|
-
spec.add_dependency "pura-gif", "~> 0.1"
|
|
27
|
-
spec.add_dependency "pura-tiff", "~> 0.1"
|
|
28
|
-
spec.add_dependency "pura-ico", "~> 0.1"
|
|
29
|
-
spec.add_dependency "pura-webp", "~> 0.2"
|
|
23
|
+
spec.add_dependency "pura-jpeg", "~> 0.1", ">= 0.1.1"
|
|
24
|
+
spec.add_dependency "pura-png", "~> 0.1", ">= 0.1.1"
|
|
25
|
+
spec.add_dependency "pura-bmp", "~> 0.1", ">= 0.1.1"
|
|
26
|
+
spec.add_dependency "pura-gif", "~> 0.1", ">= 0.1.1"
|
|
27
|
+
spec.add_dependency "pura-tiff", "~> 0.1", ">= 0.1.1"
|
|
28
|
+
spec.add_dependency "pura-ico", "~> 0.1", ">= 0.1.1"
|
|
29
|
+
spec.add_dependency "pura-webp", "~> 0.2", ">= 0.2.1"
|
|
30
30
|
spec.add_dependency "pura-processing", "~> 0.1"
|
|
31
31
|
spec.add_development_dependency "minitest", "~> 5.0"
|
|
32
32
|
spec.add_development_dependency "rake", "~> 13.0"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pura-image
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- komagata
|
|
@@ -16,6 +16,9 @@ dependencies:
|
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '0.1'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 0.1.1
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -23,6 +26,9 @@ dependencies:
|
|
|
23
26
|
- - "~>"
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
28
|
version: '0.1'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 0.1.1
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: pura-png
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -30,6 +36,9 @@ dependencies:
|
|
|
30
36
|
- - "~>"
|
|
31
37
|
- !ruby/object:Gem::Version
|
|
32
38
|
version: '0.1'
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 0.1.1
|
|
33
42
|
type: :runtime
|
|
34
43
|
prerelease: false
|
|
35
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -37,6 +46,9 @@ dependencies:
|
|
|
37
46
|
- - "~>"
|
|
38
47
|
- !ruby/object:Gem::Version
|
|
39
48
|
version: '0.1'
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 0.1.1
|
|
40
52
|
- !ruby/object:Gem::Dependency
|
|
41
53
|
name: pura-bmp
|
|
42
54
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,6 +56,9 @@ dependencies:
|
|
|
44
56
|
- - "~>"
|
|
45
57
|
- !ruby/object:Gem::Version
|
|
46
58
|
version: '0.1'
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 0.1.1
|
|
47
62
|
type: :runtime
|
|
48
63
|
prerelease: false
|
|
49
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +66,9 @@ dependencies:
|
|
|
51
66
|
- - "~>"
|
|
52
67
|
- !ruby/object:Gem::Version
|
|
53
68
|
version: '0.1'
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: 0.1.1
|
|
54
72
|
- !ruby/object:Gem::Dependency
|
|
55
73
|
name: pura-gif
|
|
56
74
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,6 +76,9 @@ dependencies:
|
|
|
58
76
|
- - "~>"
|
|
59
77
|
- !ruby/object:Gem::Version
|
|
60
78
|
version: '0.1'
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 0.1.1
|
|
61
82
|
type: :runtime
|
|
62
83
|
prerelease: false
|
|
63
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -65,6 +86,9 @@ dependencies:
|
|
|
65
86
|
- - "~>"
|
|
66
87
|
- !ruby/object:Gem::Version
|
|
67
88
|
version: '0.1'
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: 0.1.1
|
|
68
92
|
- !ruby/object:Gem::Dependency
|
|
69
93
|
name: pura-tiff
|
|
70
94
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +96,9 @@ dependencies:
|
|
|
72
96
|
- - "~>"
|
|
73
97
|
- !ruby/object:Gem::Version
|
|
74
98
|
version: '0.1'
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 0.1.1
|
|
75
102
|
type: :runtime
|
|
76
103
|
prerelease: false
|
|
77
104
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -79,6 +106,9 @@ dependencies:
|
|
|
79
106
|
- - "~>"
|
|
80
107
|
- !ruby/object:Gem::Version
|
|
81
108
|
version: '0.1'
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: 0.1.1
|
|
82
112
|
- !ruby/object:Gem::Dependency
|
|
83
113
|
name: pura-ico
|
|
84
114
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,6 +116,9 @@ dependencies:
|
|
|
86
116
|
- - "~>"
|
|
87
117
|
- !ruby/object:Gem::Version
|
|
88
118
|
version: '0.1'
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: 0.1.1
|
|
89
122
|
type: :runtime
|
|
90
123
|
prerelease: false
|
|
91
124
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -93,6 +126,9 @@ dependencies:
|
|
|
93
126
|
- - "~>"
|
|
94
127
|
- !ruby/object:Gem::Version
|
|
95
128
|
version: '0.1'
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: 0.1.1
|
|
96
132
|
- !ruby/object:Gem::Dependency
|
|
97
133
|
name: pura-webp
|
|
98
134
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -100,6 +136,9 @@ dependencies:
|
|
|
100
136
|
- - "~>"
|
|
101
137
|
- !ruby/object:Gem::Version
|
|
102
138
|
version: '0.2'
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: 0.2.1
|
|
103
142
|
type: :runtime
|
|
104
143
|
prerelease: false
|
|
105
144
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -107,6 +146,9 @@ dependencies:
|
|
|
107
146
|
- - "~>"
|
|
108
147
|
- !ruby/object:Gem::Version
|
|
109
148
|
version: '0.2'
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: 0.2.1
|
|
110
152
|
- !ruby/object:Gem::Dependency
|
|
111
153
|
name: pura-processing
|
|
112
154
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -163,9 +205,11 @@ files:
|
|
|
163
205
|
- Rakefile
|
|
164
206
|
- lib/image_processing/pura.rb
|
|
165
207
|
- lib/pura-image.rb
|
|
208
|
+
- lib/pura/image/legacy_variation.rb
|
|
166
209
|
- lib/pura/image/operations.rb
|
|
167
210
|
- lib/pura/image/processor.rb
|
|
168
211
|
- lib/pura/image/railtie.rb
|
|
212
|
+
- lib/pura/image/transformer.rb
|
|
169
213
|
- lib/pura/image/version.rb
|
|
170
214
|
- pura-image.gemspec
|
|
171
215
|
- test/fixtures/test_64x64.jpg
|