ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
jof_png.c File Reference

Streaming PNG scanline decoder for the transcode producer (#231). More...

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "jof_internal.h"
#include "jof_png_internal.h"
#include "miniz.h"
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_log.h"
Include dependency graph for jof_png.c:

Go to the source code of this file.

Data Structures

struct  ra8_png_iter_t
 Inflate-loop cursor: compressed-input window + progress guard. More...

Functions

static uint8_t internal_png_paeth (uint8_t a, uint8_t b, uint8_t c)
 Paeth predictor (PNG sec 9.4 "Filter type 4: Paeth").
static ra8_err_t internal_png_unfilter (uint8_t filter, uint8_t *row, const uint8_t *prev, uint32_t nbytes, uint8_t bpp)
 Reconstruct one filtered scanline in place (PNG sec 9 filters).
static ra8_err_t internal_png_translate (ra8_png_state_t *st, const uint8_t *row)
 Translate one reconstructed source row into the output layout.
static ra8_err_t internal_png_consume_rows (ra8_png_state_t *st, const uint8_t *src, uint32_t avail)
 Assemble, reconstruct and emit rows from freshly inflated bytes.
static ra8_err_t internal_png_bind_geometry (ra8_png_state_t *st, jof_bump_t *bump)
 Fire the geometry hook and carve the pixel-path buffers.
static ra8_err_t internal_png_refill_input (ra8_png_state_t *st, uint32_t *out_avail)
 Refill the compressed-input buffer from the IDAT chunk stream.
static ra8_err_t internal_png_inflate_step (ra8_png_state_t *st, ra8_png_iter_t *it)
 One inflate iteration: refill, tinfl, progress guard, drain rows.
static ra8_err_t internal_png_inflate_idat (ra8_png_state_t *st, uint32_t first_len)
 Inflate the whole IDAT stream, emitting every scanline.
static ra8_err_t internal_png_run_idat (ra8_png_state_t *st, jof_bump_t *bump, uint32_t len)
 The IDAT arm: bind geometry, inflate the stream, finish the walk.
static ra8_err_t internal_png_walk_chunks (ra8_png_state_t *st, jof_bump_t *bump, uint16_t max_w, uint16_t max_h)
 Walk the chunk stream: pre-IDAT chunks, then the IDAT arm.
ra8_err_t priv_jof_png_rows (jof_pull_fn pull, void *pull_ctx, jof_bump_t *bump, uint16_t max_w, uint16_t max_h, jof_geom_fn on_geom, jof_rows_fn on_rows, void *cb_ctx)
 Streaming PNG scanline decode: pull bytes in, emit rows in order.

Detailed Description

Streaming PNG scanline decoder for the transcode producer (#231).

Implements priv_jof_png_rows(): a bounded-RAM, pull-based PNG decoder that never holds the whole image. IDAT deflate data inflates through miniz tinfl into a 64 KiB power-of-two ring (the LZ dictionary), bytes drain into a single scanline assembly buffer, each completed scanline is unfiltered against one previous row (PNG spec sec 9 "Filtering"), then translated to the producer's output layout and emitted – resident cost is the ring plus three rows, independent of the image height.

Supported: 8-bit depth, colour types 0 (gray), 2 (RGB), 3 (palette, with/without tRNS), 4 (gray+alpha), 6 (RGBA), non-interlaced. Everything else is rejected fail-closed – this decoder feeds on untrusted EPUB content. Spec citations reference the W3C PNG specification (second edition), abbreviated PNG sec N.

[Ring 4 / Domain] {World: NS}

Definition in file jof_png.c.

Function Documentation

◆ internal_png_bind_geometry()

ra8_err_t internal_png_bind_geometry ( ra8_png_state_t * st,
jof_bump_t * bump )
static

Fire the geometry hook and carve the pixel-path buffers.

Runs once, at the first IDAT: decides the output channel count (palette + tRNS promotes to RGBA), requires PLTE for palette images, and carves the inflate + row buffers from the arena.

Parameters
[in,out]stDecoder state.
[in,out]bumpWork-arena allocator.
Returns
Result code.
Return values
k_ra8_okBuffers carved; hook accepted.
k_ra8_err_validation_failedPalette image with no PLTE.
k_ra8_err_invalid_sizeArena exhausted (budget fail-closed).
otherThe geometry hook aborted the decode.
Precondition
IHDR (and any PLTE/tRNS) have been parsed.
bump has the PNG carve set available.
Postcondition
On success all pixel-path buffers are bound.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 279 of file jof_png.c.

References ra8_png_state_t::cb_ctx, ra8_png_state_t::color_type, ra8_png_state_t::dst_ch, ra8_png_state_t::h, ra8_png_state_t::has_plte, ra8_png_state_t::has_trns, ra8_png_state_t::inbuf, ra8_png_state_t::inflator, k_ra8_err_invalid_size, k_ra8_err_validation_failed, k_ra8_ok, k_ra8_png_ch_1, k_ra8_png_ch_3, k_ra8_png_ch_4, k_ra8_png_color_ga, k_ra8_png_color_gray, k_ra8_png_color_pal, k_ra8_png_inbuf_bytes, k_ra8_png_ring_bytes, memset(), ra8_png_state_t::on_geom, ra8_png_state_t::prevrow, priv_jof_bump_take(), RA8_INTERNAL, ra8_png_state_t::ring, ra8_png_state_t::rowbuf, ra8_png_state_t::rowlen, ra8_png_state_t::src_ch, ra8_png_state_t::w, and ra8_png_state_t::xlat.

Referenced by internal_png_run_idat().

◆ internal_png_consume_rows()

ra8_err_t internal_png_consume_rows ( ra8_png_state_t * st,
const uint8_t * src,
uint32_t avail )
static

Assemble, reconstruct and emit rows from freshly inflated bytes.

Drains avail produced bytes into the scanline buffer; each completed scanline is unfiltered, saved as the new previous row, translated and emitted. Producing more scanlines than IHDR declared is a hostile-stream error.

Parameters
[in,out]stDecoder state.
[in]srcFreshly produced ring bytes (contiguous).
[in]availByte count at src.
Returns
Result code.
Return values
k_ra8_okBytes consumed (rows possibly emitted).
k_ra8_err_validation_failedExtra rows / bad filter / bad index.
otherPropagated from the row sink.
Precondition
Geometry has been bound (buffers carved).
src covers avail bytes.
Postcondition
rowfill/rows_done advanced per the consumed bytes.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 217 of file jof_png.c.

References ra8_png_state_t::cb_ctx, ra8_png_state_t::dst_ch, ra8_png_state_t::h, internal_png_translate(), internal_png_unfilter(), k_ra8_err_validation_failed, k_ra8_ok, memcpy(), ra8_png_state_t::on_rows, ra8_png_state_t::prevrow, ra8_png_state_t::rowbuf, ra8_png_state_t::rowfill, ra8_png_state_t::rowlen, ra8_png_state_t::rows_done, ra8_png_state_t::src_ch, ra8_png_state_t::w, and ra8_png_state_t::xlat.

Referenced by internal_png_inflate_step().

◆ internal_png_inflate_idat()

ra8_err_t internal_png_inflate_idat ( ra8_png_state_t * st,
uint32_t first_len )
static

Inflate the whole IDAT stream, emitting every scanline.

Drives internal_png_inflate_step until the zlib stream terminates, then applies the strict termination checks: every declared row arrived, no partial scanline remains, and no compressed bytes trail the stream inside the IDAT chunks.

Parameters
[in,out]stDecoder state.
[in]first_lenPayload length of the first IDAT chunk.
Returns
Result code.
Return values
k_ra8_okAll rows emitted; stream ended clean.
k_ra8_err_protocol_errorCorrupt / truncated deflate stream.
k_ra8_err_validation_failedRow-count / trailing-byte mismatch.
otherPropagated from pull / the hooks.
Precondition
internal_png_bind_geometry() succeeded.
The source sits at the first IDAT's payload.
Postcondition
On success rows_done == h and the pending chunk is parked.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 477 of file jof_png.c.

References ra8_png_iter_t::done, ra8_png_state_t::h, ra8_png_state_t::idat_rem, ra8_png_iter_t::in_avail, ra8_png_iter_t::in_pos, internal_png_inflate_step(), k_ra8_err_validation_failed, k_ra8_ok, RA8_INTERNAL, ra8_png_state_t::rowfill, and ra8_png_state_t::rows_done.

Referenced by internal_png_run_idat().

◆ internal_png_inflate_step()

ra8_err_t internal_png_inflate_step ( ra8_png_state_t * st,
ra8_png_iter_t * it )
static

One inflate iteration: refill, tinfl, progress guard, drain rows.

The ring is tinfl's LZ dictionary; each call produces into the contiguous run [ring_wr, ring_end) which drains into the scanline assembler before the write offset wraps. Two consecutive zero-progress iterations (or any stall after the source ended) abort as a truncated/hostile stream.

Parameters
[in,out]stDecoder state.
[in,out]itLoop cursor (input window + guards).
Returns
Result code.
Return values
k_ra8_okProgress made (or clean stream end).
k_ra8_err_protocol_errorCorrupt deflate / no progress.
k_ra8_err_validation_failedRow-stream inconsistency.
otherPropagated from pull / the hooks.
Precondition
internal_png_bind_geometry() succeeded.
it was zero-initialised before the first call.
Postcondition
it->done is set once the zlib stream terminates.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 410 of file jof_png.c.

References ra8_png_iter_t::done, ra8_png_iter_t::in_avail, ra8_png_iter_t::in_pos, ra8_png_state_t::inbuf, ra8_png_state_t::inflator, internal_png_consume_rows(), internal_png_refill_input(), k_ra8_err_protocol_error, k_ra8_ok, k_ra8_png_ring_bytes, RA8_INTERNAL, ra8_png_state_t::ring, ra8_png_state_t::ring_wr, ra8_png_state_t::source_done, and ra8_png_iter_t::stalls.

Referenced by internal_png_inflate_idat().

◆ internal_png_paeth()

uint8_t internal_png_paeth ( uint8_t a,
uint8_t b,
uint8_t c )
static

Paeth predictor (PNG sec 9.4 "Filter type 4: Paeth").

Selects whichever neighbour is closest to the linear predictor a + b - c.

Parameters
[in]aLeft reconstructed byte.
[in]bAbove reconstructed byte.
[in]cAbove-left reconstructed byte.
Returns
The predictor byte.
Return values
a-cWhichever neighbour is closest to a + b - c.
Precondition
None (total over uint8_t inputs).
None.
Postcondition
No state mutated.
Return is one of the three inputs.
Note
Pure; thread-safe.
Since
0.1.0

Definition at line 65 of file jof_png.c.

References RA8_INTERNAL.

Referenced by internal_png_unfilter().

◆ internal_png_refill_input()

ra8_err_t internal_png_refill_input ( ra8_png_state_t * st,
uint32_t * out_avail )
static

Refill the compressed-input buffer from the IDAT chunk stream.

Crosses consecutive IDAT chunks transparently (skipping each CRC); a non-IDAT chunk ends the compressed stream and is parked as the pending chunk for the outer walk. Bounded by the chunk budget shared with the outer walk.

Parameters
[in,out]stDecoder state.
[out]out_availReceives the refilled byte count (0 = stream end).
Returns
Result code.
Return values
k_ra8_okBuffer refilled (or clean stream end).
k_ra8_err_protocol_errorTruncated chunk structure.
otherPropagated from the pull callback.
Precondition
The inflate phase is active (first IDAT seen).
out_avail is writable.
Postcondition
*out_avail bytes at st->inbuf are compressed data.
source_done/pending_* reflect a stream-ending chunk.
Note
Not thread-safe.
Since
0.1.0

Definition at line 335 of file jof_png.c.

References ra8_png_state_t::idat_rem, ra8_png_state_t::inbuf, k_ra8_err_protocol_error, k_ra8_ok, k_ra8_png_crc_bytes, k_ra8_png_inbuf_bytes, k_ra8_png_max_chunks, k_ra8_png_type_idat, ra8_png_state_t::pending_len, ra8_png_state_t::pending_type, ra8_png_state_t::pending_valid, priv_jof_png_chunk_hdr(), priv_jof_png_pull_exact(), priv_jof_png_skip(), RA8_INTERNAL, and ra8_png_state_t::source_done.

Referenced by internal_png_inflate_step().

◆ internal_png_run_idat()

ra8_err_t internal_png_run_idat ( ra8_png_state_t * st,
jof_bump_t * bump,
uint32_t len )
static

The IDAT arm: bind geometry, inflate the stream, finish the walk.

Runs once, at the first IDAT chunk; every subsequent IDAT is consumed inside the inflate phase.

Parameters
[in,out]stDecoder state.
[in,out]bumpWork-arena allocator for the pixel-path carves.
[in]lenPayload length of the first IDAT chunk.
Returns
Result code.
Return values
k_ra8_okEvery row emitted and the datastream consumed to IEND.
otherPropagated from geometry / inflate / the chunk walk.
Precondition
The IHDR (and any PLTE/tRNS) have been parsed.
bump has the PNG carve set available.
Postcondition
On success the whole PNG has been consumed.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 521 of file jof_png.c.

References internal_png_bind_geometry(), internal_png_inflate_idat(), k_ra8_ok, and priv_jof_png_finish().

Referenced by internal_png_walk_chunks().

◆ internal_png_translate()

ra8_err_t internal_png_translate ( ra8_png_state_t * st,
const uint8_t * row )
static

Translate one reconstructed source row into the output layout.

gray -> gray8; RGB/RGBA -> copied; gray+alpha -> RGBA (the gray value replicated); palette -> RGB or RGBA via PLTE (+ tRNS), with every index bounds-checked against the palette (hostile streams may index past a short PLTE).

Parameters
[in,out]stDecoder state (xlat written).
[in]rowReconstructed source row (w * src_ch bytes).
Returns
Result code.
Return values
k_ra8_okRow translated into st->xlat.
k_ra8_err_validation_failedA palette index is out of range.
Precondition
st->xlat covers w * dst_ch bytes.
st->dst_ch was fixed at geometry time.
Postcondition
On success st->xlat holds the packed output row.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 157 of file jof_png.c.

References ra8_png_state_t::color_type, ra8_png_state_t::dst_ch, k_ra8_err_validation_failed, k_ra8_ok, k_ra8_png_ch_2, k_ra8_png_ch_3, k_ra8_png_ch_4, k_ra8_png_color_ga, k_ra8_png_color_pal, k_ra8_png_opaque, memcpy(), ra8_png_state_t::palette, ra8_png_state_t::plte_count, RA8_INTERNAL, ra8_png_state_t::trns, ra8_png_state_t::trns_count, ra8_png_state_t::w, and ra8_png_state_t::xlat.

Referenced by internal_png_consume_rows().

◆ internal_png_unfilter()

ra8_err_t internal_png_unfilter ( uint8_t filter,
uint8_t * row,
const uint8_t * prev,
uint32_t nbytes,
uint8_t bpp )
static

Reconstruct one filtered scanline in place (PNG sec 9 filters).

row is the raw scanline (post filter-byte); prev is the previous reconstructed row (all zeroes for the first row, per spec). The filter arithmetic is modulo-256 by construction.

Parameters
[in]filterFilter-type byte (0..4; caller-validated range in).
[in,out]rowScanline bytes, reconstructed in place.
[in]prevPrevious reconstructed row.
[in]nbytesScanline byte count (w * src_ch).
[in]bppFilter delta distance (source bytes per pixel).
Returns
Result code.
Return values
k_ra8_okRow reconstructed.
k_ra8_err_validation_failedUnknown filter type (hostile stream).
Precondition
row and prev each cover nbytes bytes.
bpp is in 1..4.
Postcondition
On success row holds reconstructed bytes.
On error the row content is unspecified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 101 of file jof_png.c.

References internal_png_paeth(), k_ra8_err_validation_failed, k_ra8_ok, k_ra8_png_filter_avg, k_ra8_png_filter_none, k_ra8_png_filter_paeth, k_ra8_png_filter_sub, k_ra8_png_filter_up, and RA8_INTERNAL.

Referenced by internal_png_consume_rows().

◆ internal_png_walk_chunks()

ra8_err_t internal_png_walk_chunks ( ra8_png_state_t * st,
jof_bump_t * bump,
uint16_t max_w,
uint16_t max_h )
static

Walk the chunk stream: pre-IDAT chunks, then the IDAT arm.

Bounded by the shared chunk budget (NASA Rule 2); a stream that never reaches an IDAT within it is rejected as hostile.

Parameters
[in,out]stDecoder state (prologue already parsed).
[in,out]bumpWork-arena allocator for the pixel-path carves.
[in]max_wFail-closed width cap (for the prologue).
[in]max_hFail-closed height cap (for the prologue).
Returns
Result code.
Return values
k_ra8_okEvery row emitted; stream consumed.
k_ra8_err_protocol_errorStructure error / chunk budget spent.
otherPropagated from the chunk / IDAT arms.
Precondition
The callbacks are bound in st.
The source is positioned at byte 0 of the PNG stream.
Postcondition
On success the whole PNG has been consumed.
On error the decode aborts.
Note
Not thread-safe.
Since
0.1.0

Definition at line 555 of file jof_png.c.

References internal_png_run_idat(), k_ra8_err_protocol_error, k_ra8_ok, k_ra8_png_max_chunks, k_ra8_png_type_idat, priv_jof_png_chunk_hdr(), priv_jof_png_pre_idat(), and priv_jof_png_prologue().

Referenced by priv_jof_png_rows().

◆ priv_jof_png_rows()

ra8_err_t priv_jof_png_rows ( jof_pull_fn pull,
void * pull_ctx,
jof_bump_t * bump,
uint16_t max_w,
uint16_t max_h,
jof_geom_fn on_geom,
jof_rows_fn on_rows,
void * cb_ctx )

Streaming PNG scanline decode: pull bytes in, emit rows in order.

Bounded-RAM PNG decoder for the transcode producer: 8-bit depth, colour types 0/2/3/4/6, non-interlaced. IDAT inflates through miniz tinfl into a 64 KiB ring, scanlines are unfiltered against one previous row and translated to the output layout (gray -> 1, RGB / opaque palette -> 3, gray+alpha / RGBA / palette+tRNS -> 4), then handed to on_rows one row at a time. All working buffers are carved from bump. Interlaced, 16-bit, and out-of-spec structures are rejected fail-closed. Chunk CRCs are not verified (the ZIP layer above already integrity-checks the entry; the zlib Adler32 inside IDAT is verified).

Parameters
[in]pullSequential byte source positioned at byte 0 of the PNG stream (the producer replays sniffed bytes).
[in]pull_ctxContext for pull.
[in,out]bumpWork-arena allocator for the decoder's buffers.
[in]max_wFail-closed width cap, pixels.
[in]max_hFail-closed height cap, pixels.
[in]on_geomGeometry hook (fires once, before rows).
[in]on_rowsRow sink (fires height times, in order).
[in]cb_ctxContext for both hooks.
Returns
ra8_err_t Error code.
Return values
k_ra8_okEvery row emitted.
k_ra8_err_invalid_sizeDimensions exceed the caps or the arena is exhausted.
k_ra8_err_not_supportedInterlaced / 16-bit / unknown colour type / non-zero compression or filter method.
k_ra8_err_protocol_errorMalformed chunk structure or a corrupt / truncated deflate stream.
k_ra8_err_validation_failedPixel-stream inconsistency (filter byte, palette index, row count).
otherPropagated from pull / the hooks.
Precondition
pull delivers the PNG from its first signature byte.
bump has capacity per jof_work_bytes().
Postcondition
On success exactly height rows were emitted, in order.
On any error emission stops; the transcode aborts.
Note
Not thread-safe (module-static inflate context).
Since
0.1.0

Definition at line 579 of file jof_png.c.

References ra8_png_state_t::cb_ctx, internal_png_walk_chunks(), memset(), ra8_png_state_t::on_geom, ra8_png_state_t::on_rows, ra8_png_state_t::pull, ra8_png_state_t::pull_ctx, RA8_CHECK_NULL_PTR, RA8_PRIV, and s_tag.

Referenced by internal_dispatch().