Module Qoic

QOI image codec.

Encodes and decodes QOI images.

See the limitations and an example.

Image metadata

type color_space = [
| `Srgb
| `Linear
]

The type for color spaces.

type channels = [
| `Rgb
| `Rgba
]

The type for image channels.

val channel_count : channels -> int

channel_count c is the number of channels in c.

module Meta : sig ... end

Image metadata.

Bytes and pixel data

type uint32 = int32

The type for unsigned 32-bit integers.

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.

Encoding

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.

Decoding

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.

Limitations

Example

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