Sign in

    codec_image

    The streaming contract of image decoders: step through input chunks, get whole rows into bounded output slices, with bounded memory.

    codec
    image
    streaming
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    12 hours ago
    Downloads
    12

    #gaato/codec_image

    The contract of streaming image decoders in codecs.mbt: a decoder is driven step by step, takes input in chunks of any size, writes whole rows into output slices the caller provides, and keeps its memory within the limits it was given. A step stops to report the image's header, the start and the end of each frame, the rows it wrote, and metadata it was asked for. Code written against Decode works with every image decoder that implements it, such as gaato/png.

    #Install

    moon add gaato/codec_image

    #Usage

    Write the loop that drives an image decoder once, against Decode. Each step consumes a prefix of the input and says why it stopped; a step that writes, writes whole rows of one pass of the current frame and answers Rows, and the accessors say where they lie. A slice must hold at least one row, which room_needed() measures.

    ///|
    /// Hands each row of the image in `chunks` to `row`, with its pass and its
    /// row in the pass.
    fn[D : @codec_image.Decode] each_row(
    decoder : D,
    chunks : Array[Bytes],
    row : (Int32, Int32, BytesView) -> Unit,
    ) -> Unit raise {
    let mut out = FixedArray::make(0, b'\x00')
    for i, chunk in chunks {
    let mut input = chunk[:]
    for ;; {
    if out.length() < decoder.room_needed() {
    out = FixedArray::make(decoder.room_needed() * 16, b'\x00')
    }
    let status = decoder.step(
    input,
    out.mut_view(),
    end=i == chunks.length() - 1,
    )
    input = input[decoder.last_consumed():]
    match status {
    Rows => {
    let bytes = Bytes::from_array(out[:decoder.last_produced()])
    for r in 0..<decoder.rows_count() {
    let at = r * decoder.row_bytes()
    row(
    decoder.pass(),
    decoder.rows_first() + r,
    bytes[at:at + decoder.row_bytes()],
    )
    }
    }
    Done => return
    NeedMoreInput => break
    _ => ()
    }
    }
    }
    }

    ///|
    test "the loop takes any image decoder, a trait object too" {
    let drive : (
    &@codec_image.Decode,
    Array[Bytes],
    (Int32, Int32, BytesView) -> Unit,
    ) -> Unit raise = each_row
    ignore(drive)
    }

    Rows come out in the format's own layout (for PNG, packed samples, palette indices, 16-bit samples big-endian), and interlaced images pass by pass: row r of a pass lies at row pass_y() + r * pass_dy() of the frame, and its pixel i at column pass_x() + i * pass_dx(). Converting pixels, applying palettes, and assembling passes and frames into a picture are left to functions outside the decoders, which each format module provides for its own layout.

    #Errors

    A step raises gaato/codec's CodecError(kind, message). This package re-exports it with ErrorKind, Limit and Resource, so it is the only import a consumer needs to name them. In a catch on a decoder's step, CodecError(kind, message) and the kinds match unqualified; elsewhere, qualify a constructor by its type, as in @codec_image.CodecError::CodecError(kind, _).

    A step that raises writes nothing: the rows and events before the error came out in the steps before it. Errors stick: later steps raise the same kind until reset.

    #License

    Apache-2.0.

    CodecError

    using @gaato/codec { type CodecError }

    The error every codec raises, its kinds, and the limits a stream can exceed, from gaato/codec, so that a caller of an image decoder can name them with this package as its only import. A constructor is matched unqualified, as in CodecError(Corrupt, _), where the type is known (a catch on a decoder's step), or qualified by its type, as in @codec_image.CodecError::CodecError(kind, _).

    ErrorKind

    using @gaato/codec { type ErrorKind }

    The error every codec raises, its kinds, and the limits a stream can exceed, from gaato/codec, so that a caller of an image decoder can name them with this package as its only import. A constructor is matched unqualified, as in CodecError(Corrupt, _), where the type is known (a catch on a decoder's step), or qualified by its type, as in @codec_image.CodecError::CodecError(kind, _).

    Limit

    using @gaato/codec { type Limit }

    The error every codec raises, its kinds, and the limits a stream can exceed, from gaato/codec, so that a caller of an image decoder can name them with this package as its only import. A constructor is matched unqualified, as in CodecError(Corrupt, _), where the type is known (a catch on a decoder's step), or qualified by its type, as in @codec_image.CodecError::CodecError(kind, _).

    Resource

    using @gaato/codec { type Resource }

    The error every codec raises, its kinds, and the limits a stream can exceed, from gaato/codec, so that a caller of an image decoder can name them with this package as its only import. A constructor is matched unqualified, as in CodecError(Corrupt, _), where the type is known (a catch on a decoder's step), or qualified by its type, as in @codec_image.CodecError::CodecError(kind, _).

    Decode

    pub(open) trait Decode {
    fn step(Self, BytesView, MutArrayView[Byte], end~ : Bool) -> Status raise
    CodecError

    fn last_consumed(Self) -> Int
    fn last_produced(Self) -> Int
    fn reset(Self) -> Unit
    fn memory_usage(Self) -> Int
    fn room_needed(Self) -> Int
    fn width(Self) -> Int
    fn height(Self) -> Int
    fn frame_x(Self) -> Int
    fn frame_y(Self) -> Int
    fn frame_width(Self) -> Int
    fn frame_height(Self) -> Int
    fn pass(Self) -> Int
    fn pass_x(Self) -> Int
    fn pass_y(Self) -> Int
    fn pass_dx(Self) -> Int
    fn pass_dy(Self) -> Int
    fn rows_first(Self) -> Int
    fn rows_count(Self) -> Int
    fn row_bytes(Self) -> Int
    }

    A decoder of an image read from a byte stream, driven step by step.

    A step consumes a prefix of input and writes a prefix of output (last_consumed and last_produced say how much), and returns why it stopped. end says whether input reaches the end of the data. The rules every decoder keeps are in the repository's CONTRACT.md: among them, a decoder writes whole rows only, and all rows of one step belong to one pass of one frame; it reports every event and writes every row the input determines before it raises; errors stick until reset; and the rows, the metadata, the events and the error do not depend on how input and output are split.

    The numbers that describe what a step reported are read from the decoder, never returned as a tuple or a struct. Each says when it is valid; outside that, its value is unspecified.

    Status

    pub(all) enum Status {
    Header
    Frame
    Rows
    Metadata
    FrameEnd
    Done
    NeedMoreInput
    NeedMoreOutput
    } derive(Eq,
    Debug
    )

    Why a step stopped without an error. Every step that writes output says what it wrote (Rows, Metadata); every other answer comes from a step that wrote nothing.

    Status::equal

    fn Status::equal(Status, Status) -> Bool

    Status::equal and Status::not_equal, the derived comparison.

    Status::not_equal

    fn Status::not_equal(x : Status, y : Status) -> Bool

    Status::equal and Status::not_equal, the derived comparison.

    Status::to_repr

    Status::to_repr, the derived debug representation.

    Until

    pub(all) enum Until {
    EndOfInput
    EndOfMember
    } derive(Eq,
    Debug
    )

    Where a decoder stops when bytes follow the image.

    Until::equal

    fn Until::equal(Until, Until) -> Bool

    Until::equal and Until::not_equal, the derived comparison.

    Until::not_equal

    fn Until::not_equal(x : Until, y : Until) -> Bool

    Until::equal and Until::not_equal, the derived comparison.

    Until::to_repr

    Until::to_repr, the derived debug representation.

    Source Files

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io