Closed dkam closed 5 months ago
There are two formats for JXL files, a naked stream and an ISO Bmff contained format.
The logo for the https://jpegxl.info site is a naked stream, and there is a ISOBMFF formatted image here.
With the naked stream, in the parse_type
method we can:
when "\xFF\x0A".b
:jxl
We can detect the ISOBMFF format where the \0\0
is matched - probably don't need to match both JXL
and ftypjxl
.
when "\0\0"
case @stream.peek(3).bytes.to_a.last
when 0
# http://www.ftyps.com/what.html
case @stream.peek(12)[4..-1]
when "ftypavif"
:avif
when "ftypavis"
:avif
when "ftypheic"
:heic
when "ftypmif1"
:heif
else
if @stream.peek(7)[4..-1] == 'JXL' || @stream.peek(32)[16..22] == 'ftypjxl'
:jxl
end
end
# ico has either a 1 (for ico format) or 2 (for cursor) at offset 3
when 1 then :ico
when 2 then :cur
end
A user in the JXL Discord pointed me to their Python parser.
Another user mentioned, that for the naked stream, the image dimensions come directly after the signature of ff 0a
.
The reference implementation in C++ code for extracting the dimensions is here. There was also this comment you basically have to find the jxlc box or the first jxlp box to find the actual codestream (say if you want to get the image dimensions)
The type detection works fine.
The size detection is rather complex, but at least the size appears to be present at the start of the jxlc stream. By looking at the reference decoder, I see we need to read 16 bit values and read them in bit groups starting from the least significant bits.
The bit groups that need to be read look like:
-- this is only used if xsize and ysize <= 256 and divisible by 8 -- values are stored divided by 8 with 1 subtracted, so fit in 5 bits [1] [Y bits(5)] [ratio bits(3)] [X bits(5) if nil ratio]
or
[0] [Y selector bits(2)] [Y bits(9/13/18/30)] [ratio bits(3)] [X selector bits(2) if nil ratio] [X bits(9/13/18/30) if nil ratio]
Size values can't be zero, so are stored with 1 subtracted from them.
Ratio can be 0 (x size must be given) or: x-y 1) 1-1 2) 12-10 3) 4-3 4) 3-2 5) 16-9 6) 5-4 7) 2-1
Now to implement it all!
Thanks! Any chance you could release a version with this update?
FastImage doesn't support the JPEG XL format.
Thanks very much for writing the gem!