oximg 0.18.1-aarch64-linux-gnu → 0.19.0-aarch64-linux-gnu
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/README.md +28 -8
- data/exe/oximg +0 -0
- data/lib/oximg/native/liboximg_probe.so +0 -0
- data/lib/oximg/native.rb +5 -4
- data/lib/oximg/version.rb +1 -1
- data/lib/oximg.rb +44 -13
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2f87ce359e8fc26ca90d9d0a92019f6bfeb15d6e0666a3fb5e209ac8c52393d
|
|
4
|
+
data.tar.gz: 1f9b6406f2aef0f42a99519b846cc4f9e4617e1041ab3bbebda532a09d47e514
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a36f44d6923ed1978380a9c516381730313a10c97d7b8fdb4233f7cc9f8968e5478891cfc2aa490833521d7e03ac642f6b63ece421064539da133a0f623a783
|
|
7
|
+
data.tar.gz: 8e0a0482b8860043bdc0c641243646be4159cbfa533a47837d38f9ac87ae6375d961a41d8e1e57f702ee2037e6fc9a10f574f77931dda4503d2be9871bf960b4
|
data/README.md
CHANGED
|
@@ -20,7 +20,8 @@ Oximg.resize("in.jpg", "out.jpg", width: 750)
|
|
|
20
20
|
Oximg.resize("in.jpg", "out.webp", width: 750, height: 500, quality: 80)
|
|
21
21
|
Oximg.strip("upload.jpg", "clean.jpg")
|
|
22
22
|
Oximg.probe("in.jpg")
|
|
23
|
-
#=> {content_type: "image/jpeg", format: :jpeg, width: 4000, height: 3000
|
|
23
|
+
#=> {content_type: "image/jpeg", format: :jpeg, width: 4000, height: 3000,
|
|
24
|
+
# orientation: 1, display_width: 4000, display_height: 3000}
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
A GIF source is read and probed like the others (`format: :gif`), but
|
|
@@ -66,7 +67,7 @@ cannot be read as a flag.
|
|
|
66
67
|
## Stripping metadata
|
|
67
68
|
|
|
68
69
|
```ruby
|
|
69
|
-
Oximg.strip(source, destination, icc: nil, env: nil)
|
|
70
|
+
Oximg.strip(source, destination, icc: nil, auto_rotate: nil, env: nil)
|
|
70
71
|
```
|
|
71
72
|
|
|
72
73
|
Removes EXIF (GPS, camera, timestamps), XMP, IPTC and comments from a
|
|
@@ -78,6 +79,16 @@ still displays upright. `icc: :strip` also drops the colour profile.
|
|
|
78
79
|
The default is `:keep`, or `OXIMG_ICC`. Dropping a CMYK JPEG's profile
|
|
79
80
|
raises `Oximg::ProcessingError`, as do GIF and AVIF sources.
|
|
80
81
|
|
|
82
|
+
`auto_rotate: true` turns a rotated JPEG upright instead, still without
|
|
83
|
+
re-encoding: its compressed 8x8 blocks are moved, as `jpegtran
|
|
84
|
+
-perfect` does, so the stored and displayed sizes agree for every
|
|
85
|
+
reader, not only for ones that honor EXIF. That is only exact when the
|
|
86
|
+
edges it moves are whole 8 or 16 px blocks (a phone's 4032x3024 is);
|
|
87
|
+
otherwise, and for PNG and WebP, the file is stripped as without it,
|
|
88
|
+
orientation kept. `Oximg.probe(destination)` tells which: orientation
|
|
89
|
+
1, or the one kept. It costs a full entropy decode and encode, about
|
|
90
|
+
170 ms for a 12 MP photo.
|
|
91
|
+
|
|
81
92
|
## Knobs, per process or per call
|
|
82
93
|
|
|
83
94
|
Everything the server tunes through `OXIMG_*` environment variables
|
|
@@ -106,12 +117,21 @@ Oximg.probe(Pathname("in.jpg")) # also a path
|
|
|
106
117
|
Oximg.probe(upload.tempfile) # anything with #path
|
|
107
118
|
Oximg.probe(File.open("in.jpg", "rb")) # an open File
|
|
108
119
|
Oximg.probe(StringIO.new(bytes)) # an IO: bytes in memory
|
|
109
|
-
#=> {content_type: "image/jpeg", format: :jpeg, width: 4000, height: 3000
|
|
120
|
+
#=> {content_type: "image/jpeg", format: :jpeg, width: 4000, height: 3000,
|
|
121
|
+
# orientation: 1, display_width: 4000, display_height: 3000}
|
|
110
122
|
```
|
|
111
123
|
|
|
112
|
-
`probe` reads the header only and decodes no pixels.
|
|
113
|
-
the stored ones, before any EXIF rotation.
|
|
114
|
-
|
|
124
|
+
`probe` reads the header only and decodes no pixels. `width` and
|
|
125
|
+
`height` are the stored ones, before any EXIF rotation. `orientation`
|
|
126
|
+
is the EXIF value, 1..8 (1 when there is none or it is invalid, as
|
|
127
|
+
`resize` reads it), and `display_width` / `display_height` are what a
|
|
128
|
+
viewer honoring it shows: the axes exchanged for 5..8. A portrait
|
|
129
|
+
phone photo stored 4000x3000 with orientation 6, as `strip` leaves it,
|
|
130
|
+
lays out at 3000x4000. A WebP keeps its EXIF after the image data, so
|
|
131
|
+
probing one that has it reads the whole source. An `oximg` executable
|
|
132
|
+
older than this gem reports every source as orientation 1.
|
|
133
|
+
|
|
134
|
+
A String is always a path, so bytes already in memory go in as a `StringIO`. An open `File` is
|
|
115
135
|
read from its start, its position untouched, and it is the file you
|
|
116
136
|
hold that is read, even if its name has since been replaced, renamed,
|
|
117
137
|
or was relative to a directory the process has left. A closed or
|
|
@@ -161,8 +181,8 @@ Oximg.in_process_probe? #=> true on a platform gem
|
|
|
161
181
|
|
|
162
182
|
```ruby
|
|
163
183
|
Oximg.available? #=> true
|
|
164
|
-
Oximg.executable #=> "/…/gems/oximg-0.
|
|
165
|
-
Oximg.version #=> "0.
|
|
184
|
+
Oximg.executable #=> "/…/gems/oximg-0.19.0-arm64-darwin/exe/oximg"
|
|
185
|
+
Oximg.version #=> "0.19.0" # the binary's version, not the gem's
|
|
166
186
|
```
|
|
167
187
|
|
|
168
188
|
`resize` shells out to that binary — one process per image, which is
|
data/exe/oximg
CHANGED
|
Binary file
|
|
Binary file
|
data/lib/oximg/native.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Oximg
|
|
|
29
29
|
BUNDLED = File.expand_path("native/#{LIBRARY}", __dir__)
|
|
30
30
|
|
|
31
31
|
# The library's oximg_probe_abi_version this code was written for.
|
|
32
|
-
ABI_VERSION =
|
|
32
|
+
ABI_VERSION = 2
|
|
33
33
|
|
|
34
34
|
# Statuses (ext/src/lib.rs).
|
|
35
35
|
OK = 0
|
|
@@ -110,7 +110,8 @@ module Oximg
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
def call
|
|
113
|
-
|
|
113
|
+
# Stored width, stored height, orientation (ext/src/lib.rs).
|
|
114
|
+
dims = FFI::MemoryPointer.new(:uint64, 3)
|
|
114
115
|
msg = FFI::MemoryPointer.new(:char, MESSAGE_BYTES)
|
|
115
116
|
status = yield(dims, msg)
|
|
116
117
|
return if status == READ
|
|
@@ -118,8 +119,8 @@ module Oximg
|
|
|
118
119
|
text = msg.read_string.force_encoding(Encoding::UTF_8)
|
|
119
120
|
raise ProcessingError.new(text, 1) unless status == OK
|
|
120
121
|
|
|
121
|
-
width, height = dims.read_array_of_uint64(
|
|
122
|
-
|
|
122
|
+
width, height, orientation = dims.read_array_of_uint64(3)
|
|
123
|
+
Header.build(text, width, height, orientation)
|
|
123
124
|
end
|
|
124
125
|
|
|
125
126
|
# nil when there is no library to use. Memoized either way, so a
|
data/lib/oximg/version.rb
CHANGED
data/lib/oximg.rb
CHANGED
|
@@ -102,8 +102,12 @@ module Oximg
|
|
|
102
102
|
# The line `oximg probe` prints (src/cli.rs):
|
|
103
103
|
#
|
|
104
104
|
# in.gif: image/gif 120x90 (10800 stored pixels), 3 frames, 1500ms, looping forever
|
|
105
|
+
# in.jpg: image/jpeg 3000x2000 (6000000 stored pixels), orientation 6 (displayed 2000x3000)
|
|
105
106
|
#
|
|
106
|
-
# The three fields the gem reports are pinned exactly
|
|
107
|
+
# The three fields the gem reports are pinned exactly, and so is the
|
|
108
|
+
# orientation, which a non-upright source prints first after the
|
|
109
|
+
# comma (its absence means 1; the displayed size is derived here from
|
|
110
|
+
# it, not read). Everything after
|
|
107
111
|
# the comma — the animation summary, printed for an animated source
|
|
108
112
|
# only — is information the gem does not expose, and is deliberately
|
|
109
113
|
# left unparsed rather than matched: a pattern that stopped at
|
|
@@ -113,7 +117,25 @@ module Oximg
|
|
|
113
117
|
# a field missing, a continuation without the comma, or anything
|
|
114
118
|
# printed before or after it. stderr is captured separately, so a
|
|
115
119
|
# warning never lands here.
|
|
116
|
-
PROBE_LINE = /\A[^\n]*: (\S+) (\d+)x(\d+) \(\d+ stored pixels\)(?:,[^\n]*)?\n?\z/
|
|
120
|
+
PROBE_LINE = /\A[^\n]*: (\S+) (\d+)x(\d+) \(\d+ stored pixels\)(?:, orientation ([1-8]) \(displayed \d+x\d+\))?(?:,[^\n]*)?\n?\z/
|
|
121
|
+
|
|
122
|
+
# The hash probe returns, built in one place for both of its paths
|
|
123
|
+
# (the in-process library and the spawned executable).
|
|
124
|
+
module Header # :nodoc:
|
|
125
|
+
def self.build(content_type, width, height, orientation)
|
|
126
|
+
# Orientations 5-8 exchange the axes on display.
|
|
127
|
+
swapped = orientation >= 5
|
|
128
|
+
{
|
|
129
|
+
content_type: content_type,
|
|
130
|
+
format: CONTENT_TYPE_FORMATS[content_type],
|
|
131
|
+
width: width,
|
|
132
|
+
height: height,
|
|
133
|
+
orientation: orientation,
|
|
134
|
+
display_width: swapped ? height : width,
|
|
135
|
+
display_height: swapped ? width : height
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
117
139
|
|
|
118
140
|
class << self
|
|
119
141
|
# Absolute path to the executable in use; raises
|
|
@@ -181,10 +203,18 @@ module Oximg
|
|
|
181
203
|
# This is what <tt>saver(strip: true)</tt> / <tt>-strip</tt> did
|
|
182
204
|
# under libvips or ImageMagick, minus the re-encode.
|
|
183
205
|
#
|
|
206
|
+
# <tt>auto_rotate: true</tt> turns a rotated JPEG upright instead,
|
|
207
|
+
# still without re-encoding: the compressed 8x8 blocks are moved
|
|
208
|
+
# (what <tt>jpegtran -perfect</tt> does), so its stored and displayed
|
|
209
|
+
# dimensions agree for every reader and no orientation tag is left.
|
|
210
|
+
# That is only exact when the edges it moves are whole 8 or 16 px
|
|
211
|
+
# blocks; otherwise, and for PNG and WebP, the orientation is kept
|
|
212
|
+
# as without it. Probe the destination to see which happened.
|
|
213
|
+
#
|
|
184
214
|
# +env:+ is per-call knobs, as for resize.
|
|
185
|
-
def strip(source, destination, icc: nil, env: nil)
|
|
215
|
+
def strip(source, destination, icc: nil, auto_rotate: nil, env: nil)
|
|
186
216
|
output = expand(destination, "destination")
|
|
187
|
-
argv = strip_argv(source, output, icc: icc)
|
|
217
|
+
argv = strip_argv(source, output, icc: icc, auto_rotate: auto_rotate)
|
|
188
218
|
Binary.run(*argv, env: knob_env(env))
|
|
189
219
|
output
|
|
190
220
|
end
|
|
@@ -213,15 +243,21 @@ module Oximg
|
|
|
213
243
|
|
|
214
244
|
# The argv `strip` would run, minus the executable; public for the
|
|
215
245
|
# same reason as resize_argv.
|
|
216
|
-
def strip_argv(source, destination, icc: nil)
|
|
246
|
+
def strip_argv(source, destination, icc: nil, auto_rotate: nil)
|
|
217
247
|
argv = ["strip", expand(source, "source"), expand(destination, "destination")]
|
|
218
248
|
argv.push("--icc", token(icc, ICC_MODES, "icc")) unless icc.nil?
|
|
249
|
+
# Off is strip's own default, so false adds nothing.
|
|
250
|
+
argv.push("--auto-rotate") if !auto_rotate.nil? && boolean(auto_rotate, "auto_rotate")
|
|
219
251
|
argv
|
|
220
252
|
end
|
|
221
253
|
|
|
222
254
|
# Header-only inspection — no pixels are decoded. Returns
|
|
223
|
-
# <tt>{content_type:, format:, width:, height
|
|
224
|
-
#
|
|
255
|
+
# <tt>{content_type:, format:, width:, height:, orientation:,
|
|
256
|
+
# display_width:, display_height:}</tt>. +width+ and +height+ are the
|
|
257
|
+
# stored ones; +orientation+ is the EXIF value, 1..8 (1 when there is
|
|
258
|
+
# none: the rule resize's auto-rotate applies and strip keeps), and
|
|
259
|
+
# the display size is what a viewer honoring it shows, with the axes
|
|
260
|
+
# exchanged for 5..8.
|
|
225
261
|
#
|
|
226
262
|
# +source+ is a path — a String, a Pathname, or anything with #path
|
|
227
263
|
# such as a Tempfile — an open File, or a stream read from its
|
|
@@ -363,12 +399,7 @@ module Oximg
|
|
|
363
399
|
match = output.match(PROBE_LINE)
|
|
364
400
|
raise ProcessingError, "unparsable probe output: #{output.inspect}" unless match
|
|
365
401
|
|
|
366
|
-
|
|
367
|
-
content_type: match[1],
|
|
368
|
-
format: CONTENT_TYPE_FORMATS[match[1]],
|
|
369
|
-
width: Integer(match[2]),
|
|
370
|
-
height: Integer(match[3])
|
|
371
|
-
}
|
|
402
|
+
Header.build(match[1], Integer(match[2]), Integer(match[3]), match[4] ? Integer(match[4]) : 1)
|
|
372
403
|
end
|
|
373
404
|
|
|
374
405
|
# Absolute paths throughout: a relative name beginning with "-"
|