Skip to content

v0.7.0 — VobSub .sub bitmaps + caption merge / split / frame snap

Choose a tag to compare

@SteliyanH SteliyanH released this 02 Jun 14:42
· 1 commit to main since this release

Reopened cycle. Closes the bitmap-decode gap v0.6 explicitly deferred and bundles three caption-utility helpers consumers were reimplementing as one-liners. Pure additive; every v0.6 call site compiles unchanged. Kadr floor stays at ≥ 0.9.2.

Added — VobSub .sub SPU bitmap extraction (Tier 1)

  • CaptionParser.extractVobSubBitmaps(idx:sub:) async — decodes the SPU packets referenced by each VobSubCue.fileOffset in the paired .sub blob and returns [VobSubBitmap]. Each entry pairs the cue's start with the SPU's stop-display end (the .idx alone can't tell you duration — that's encoded inside the SPU control sequence) and the rendered RGBA CGImage.
  • VobSubBitmap value type — start: CMTime, end: CMTime, image: CGImage.
  • Handles both bare-SPU .sub layouts and MPEG-2 PS / PES-wrapped .sub files. Bad cues drop silently — dropping one beats throwing the whole batch.
  • Free per the locked premium scope; AI / OCR stays in kadr-pro.

Added — Caption-utility helpers (Tier 2)

  • Caption.split(at: CMTime) -> (Caption, Caption) — cleave a cue at an absolute timestamp.
  • Caption.snappedToFrameRate(_: Double) + array sugar — round start / end to the nearest frame at the given rate (23.976 / 24 / 25 / 29.97 / 30 / 60). Non-positive / non-finite rates no-op.
  • Array<Caption>.merged(within: CMTime) — collapse adjacent / overlapping cues whose gap is ≤ threshold. Text joins on \n.

Tests

27 new tests across VobSubBitmapTests (13) and CaptionTransformsTests (14). Full suite green.

Notes

  • extractVobSubBitmaps is async only because real call sites read the .sub off disk; the decode itself is synchronous.
  • The renderer interleaves top / bottom RLE fields per the SPU spec; alpha is scaled from the SPU's 4-bit field to 8-bit (×17) so palette index 0 with alpha 0 is fully transparent.
  • merged(within:) treats overlapping cues (negative gap) as merge candidates — they share screen time anyway.

Full changelog: CHANGELOG.md.