Qoic
QOI image codec.
Encodes and decodes QOI images.
See the limitations and an example.
val channel_count : channels -> int
channel_count c
is the number of channels in c
.
module Meta : sig ... end
Image metadata.
module Bigbytes : sig ... end
Bigarrays of bytes.
type pixels = Bigbytes.t
The type for pixel data.
Pixels are stored in a linear buffer, line-by-line, from left to right and top to bottom in RGB or RGBA order with one byte per component.
val encode : Meta.t -> pixels -> Bigbytes.t
encode m p
is the encoding of pixels p
described by m
.
Raises Invalid_argument
if Meta.pixels_byte_length m
does not match p
's length or if Meta.encodable m
is false
.
module Error : sig ... end
Decoding errors.
val decode :
?channels:channels ->
Bigbytes.t ->
( Meta.t * pixels, Error.t ) Stdlib.result
decode ~channels b
decodes a QOI image from b
.
If channels
is Some c
, forces the channels of the resulting metadata and pixels to be c
. An `Rgb
image forced to `Rgba
gets a constant 0xFF
alpha channel and an `Rgba
image forced to `Rgb
drops the alpha channel.
val decode' :
?channels:channels ->
Bigbytes.t ->
( Meta.t * pixels, string ) Stdlib.result
decode' b
is Result.map_error Error.to_string (decode b)
.
val decode_meta : Bigbytes.t -> ( Meta.t, Error.t ) Stdlib.result
decode_meta b
decodes QOI image metadata from b
.
int
s are used for the image size and indexing pixel components in bigarrays of bytes. On 32-bit platforms, this limits images to around 536 millions pixels (23k x 23k) for RGBA.The Stdlib
(at least until 4.14) does not provide a simple interface to interface bigarrays with files. Assume we have a module with the following interface (see below for an implementation):
module Bigfile : sig
type fpath = string
type bigbytes =
(int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
val read : fpath -> (bigbytes, string) result
val write : fpath -> bigbytes -> (unit, string) result
end
The following function recodes a QOI file with Qoic
:
let recode ?channels file ~dst =
let ( let* ) = Result.bind in
let* bytes = Bigfile.read file in
let* meta, pixels = Qoic.decode' ?channels bytes in
Bigfile.write dst (Qoic.encode meta pixels)
The Bigfile
module can be implemented by:
module Bigfile = struct
type fpath = string
type bigbytes =
(int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t
let map_bytes ?(len = -1) ~write fd =
let t = Bigarray.int8_unsigned and l = Bigarray.C_layout in
Bigarray.array1_of_genarray (Unix.map_file fd t l write [|len|])
let error f e = Error (Printf.sprintf "%s: %s" f (Unix.error_message e))
let read file =
try
let fd = Unix.openfile file Unix.[O_RDONLY] 0x600 in
let finally () = try Unix.close fd with Unix.Unix_error _ -> () in
Fun.protect ~finally @@ fun () -> Ok (map_bytes ~write:false fd)
with
| Unix.Unix_error (e, _, _) -> error file e
let write file bytes =
try
let fd = Unix.openfile file Unix.[O_CREAT; O_RDWR; O_TRUNC] 0o644 in
let finally () = try Unix.close fd with Unix.Unix_error _ -> () in
Fun.protect ~finally @@ fun () ->
let dst = map_bytes ~len:(Bigarray.Array1.dim bytes) ~write:true fd in
Ok (Bigarray.Array1.blit bytes dst)
with
| Unix.Unix_error (e, _, _) -> error file e
end