oxideav-sub-image
Pure-Rust bitmap-subtitle codecs and containers:
- PGS (HDMV / Blu-ray
.sup) — decode + encode, standalone.supcontainer. - DVB subtitles (ETSI EN 300 743) — decode only, no standalone container (DVB subs ride inside MPEG-TS).
- VobSub / DVD SPU (
.idx+.sub) — decode only, container reads the.idxtext index + matched.subpayload (MPEG-PS pack + PES private_stream_1 or raw SPU-length-prefixed form).
All three decoders emit RGBA oxideav_core::VideoFrame values — one
frame per display-set. The stream's media kind is Subtitle even though
the frame kind is Video — the dual tagging surfaces the
bitmap-subtitle nature of these codecs to downstream consumers (player,
mixer, file writer) without losing the fact that what arrives is a
pre-rendered RGBA picture.
When a display-set stacks overlapping objects (multiple PGS composition
objects, multiple DVB regions/objects) and the topmost pixel is only
partially transparent, the painted source is alpha-composited over the
existing canvas content using the Porter–Duff source-over operator
(composite::over / composite::blit_indexed), rather than hard-copying
and discarding what's underneath. Fully-opaque and fully-transparent
pixels short-circuit to a plain copy / no-op.
Part of the oxideav framework but usable standalone. Zero C dependencies.
Installation
[]
= "0.1"
= "0.1"
= "0.1"
= "0.0"
Quick use
Decoding a .sup file
use ;
let mut ctx = new;
register;
let codecs = &ctx.codecs;
let containers = &ctx.containers;
let input: = Boxnew;
let mut dmx = containers.open?;
let stream = &dmx.streams;
let mut dec = codecs.make_decoder?;
loop
# Ok::
Encoding PGS
use ;
let mut params = video;
params.media_type = Subtitle;
params.pixel_format = Some;
let mut enc = codecs.make_encoder?;
enc.send_frame?;
let packet = enc.receive_packet?;
// packet.data is one complete PGS display-set (PCS + WDS + PDS + ODS + END).
Each send_frame call produces one packet. The encoder first finds the
tight bounding box of the frame's non-transparent pixels and emits one
composition object covering just that sub-rectangle at the bbox's
(x, y); the surrounding transparent area is not encoded. Colour is
then quantised into a ≤ 255-entry palette (index 0 is reserved for
fully-transparent). When the bbox has more than 255 distinct RGBA
colours the encoder falls back to a 3/3/2/2 (R/G/B/A) bucketed
reduction; otherwise the quantisation is lossless up to the BT.601
RGB↔YCbCr round-trip PGS does in the palette. A fully-transparent
input frame emits an erase display-set (PCS with zero composition
objects + empty WDS), which the decoder maps to a fully-transparent
canvas — the canonical way to clear whatever was on screen before.
Codec / container IDs
| Codec id | Container id | Extensions | Encode |
|---|---|---|---|
pgs |
pgs |
.sup |
yes |
dvbsub |
(none) | (TS only) | no |
vobsub |
vobsub |
.idx+.sub |
no |
Why PGS encode only?
DVB subs are carried inside MPEG-TS PES packets, so emitting a valid
standalone stream requires a TS-aware muxer upstream. VobSub similarly
requires the .idx text index and .sub binary to be written in
lock-step, which this crate doesn't yet expose through the encoder
trait. Both decoders read the preformatted payloads the container/TS
demuxer delivers; adding encoders waits on the container layer growing
the muxer counterparts.
License
MIT — see LICENSE.