Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

A pure Swift 6.2 implementation of JPEG 2000 (ISO/IEC 15444) encoding and decoding with strict concurrency support.

**Current Version**: 10.18.0
**Status**: Apple Silicon-first JPEG 2000 / HTJ2K (Part-15) implementation. v10.18.0 ships **JP3D AsyncSequence progress reporting** — `progressStream()` extensions on `JP3DDecoder` / `JP3DROIDecoder` / `JP3DEncoder` / `JP3DStreamWriter`, modern Swift-concurrency progress API alongside the existing `setProgressCallback(_:)` closure surface. Closes the deferred Phase 3 from v10.17.0's plan with a focused MINOR scope so the AsyncStream lifecycle (race-free setup via `async` method, documented overwrite semantics) is implemented cleanly. Consumers gain idiomatic `for await progress in decoder.progressStream() { … }` iteration without hand-rolling a `setProgressCallback` → `AsyncStream.makeStream` adapter. MINOR / additive — pure surface, no existing API change, codestream bytes byte-identical to v10.17.0.
**Previous Release**: 10.17.0 (J2KDICOMHelpers Phase 1 — new SwiftPM product)
**Current Version**: 10.19.0
**Status**: Apple Silicon-first JPEG 2000 / HTJ2K (Part-15) implementation. v10.19.0 ships **`J2KDICOMHelpers` Phase 2 — DICOM Pixel Data encapsulation helpers**. Wrap J2K codestreams into DICOM Pixel Data Item bytes (PS3.5 §A.4) and round-trip them back: `J2KDICOMPixelDataEncapsulator.encapsulateItem(_:)` for single frames + `.encapsulateFrames(_:includeBOT:)` for multi-frame sequences with optional Basic Offset Table + `J2KDICOMPixelDataDecapsulator.extractFrames(_:)` for the reverse direction (with EOC-then-pad detection). Builds directly on v10.17.0's Phase 1 product. ADR-004 compliant — no DICOM library dependency added anywhere; the byte-layout rules are codified directly from PS3.5 §A.4 / §6.4. MINOR / additive — pure surface, codestream bytes byte-identical to v10.18.0.
**Previous Release**: 10.18.0 (JP3D AsyncSequence progress reporting)
**Release process**: see [RELEASING.md](RELEASING.md). Every release MUST update this README (Current Version line + new Release Status paragraph) — see the Release artefacts checklist for the full requirements.

## 📦 Release Status

**v10.19.0** ships **`J2KDICOMHelpers` Phase 2 — DICOM Pixel Data encapsulation helpers**. Builds directly on v10.17.0's Phase 1 product (UID-and-config bridge) with the wire-format layer that lets consumers turn J2K codestreams into DICOM Pixel Data bytes and back. New public API on the `J2KDICOMHelpers` library: **`J2KDICOMPixelDataEncapsulator.encapsulateItem(_:)`** wraps one J2K codestream into a single DICOM Pixel Data Item (8-byte header `FFFE,E000` + LE u32 length + payload + optional 0x00 pad to maintain even Item length per PS3.5 §6.4); **`J2KDICOMPixelDataEncapsulator.encapsulateFrames(_:includeBOT:)`** wraps multiple frames into a complete Item sequence with optional Basic Offset Table prefix + Sequence Delimitation Item suffix (BOT entries are little-endian u32 offsets measured from the start of the first frame Item, per current DICOM Standard); **`J2KDICOMPixelDataDecapsulator.extractFrames(_:)`** parses an encapsulated sequence back into per-frame J2K codestreams, stripping trailing 0x00 pad bytes when detected via the EOC-then-pad pattern (`0xFF 0xD9 0x00`); **`J2KDICOMPixelDataError`** `Sendable, Equatable` error type with cases for `truncated`, `itemTagExpected`, `itemLengthOverrun`, `malformedSequenceDelimitation`. Write-side use case: encode an image via `J2KSwift`, wrap as Pixel Data, hand off to your DICOM writer for insertion at `(7FE0,0010)` with VR `OB` and undefined length. Read-side: extract codestreams from your DICOM library's Pixel Data bytes, decode via `J2KDecoder`. Phase 2 scope is **narrower than the original v10.17 plan** (which contemplated full DICOM file parsing extraction); refined to the wire-format layer specifically because (a) consumers should use their own DICOM library for file parsing (pydicom, DICOMKit, dcm4che), (b) the J2K-specific wire format wasn't previously available — consumers had to hand-roll the byte layout (the pattern was test-scaffolding in `J2KStrictCrossCodecValidationTests.swift:170-194`). ADR-004 compliant: no DICOM library dependency added anywhere; rules codified directly from PS3.5 §A.4 / §6.4. **Validation**: `V10_31_PixelDataEncapsulationTests` **12/12 PASS** release mode (encapsulateItem even+odd length + padding correctness; encapsulateFrames single+multi frame with empty and populated BOT; round-trip byte-exact single + multi frame; pad stripping via EOC-then-pad detection; truncated/invalid input rejection; error type Equatable); full `swift test --filter J2KDICOMHelpers` regression **38/38 PASS** (26 V10_29 Phase 1 + 12 V10_31 Phase 2); `swift test --filter JP3D` regression **532/532 PASS**; mandatory commit gate 7/7 PASS. Also bumps `getVersion()` 10.18.0 → 10.19.0. Codestream bytes byte-identical to v10.18.0. See [RELEASE_NOTES_v10.19.0.md](RELEASE_NOTES_v10.19.0.md).

**v10.18.0** ships **JP3D AsyncSequence progress reporting** — `progressStream()` extensions on `JP3DDecoder`, `JP3DROIDecoder`, `JP3DEncoder`, and `JP3DStreamWriter`. Modern Swift concurrency progress API alongside the existing `setProgressCallback(_:)` closure surface (both remain supported indefinitely). Consumers using structured concurrency previously had to hand-roll a `setProgressCallback` → `AsyncStream.makeStream` adapter themselves; v10.18.0 ships that adapter inside the JP3D module so it composes cleanly with the existing async-await encode/decode methods. The `progressStream()` method is `async` so the relay closure is installed on the actor BEFORE the stream is returned — no race window where early progress events get missed. Lifecycle behaviour is explicitly codified: calling `progressStream()` twice overwrites the first stream's writer (the first stream's continuation receives no further events but isn't explicitly finished); the stream doesn't auto-finish at operation completion (consumers `break` based on observed progress or rely on `Task` cancellation); `setProgressCallback(_:)` after `progressStream()` overwrites the relay (use one or the other, not both). **Validation**: `V10_30_ProgressStreamParityTests` **4/4 PASS** release mode — JP3DDecoder + JP3DEncoder both deliver progress events during real operations; JP3DROIDecoder stream surface is available + safe to consume (its decoder body doesn't currently fire `progressCallback` events — pre-existing upstream gap, documented in the test header); `setProgressCallback` overwrites of `progressStream`'s relay still fire correctly. Full `swift test --filter JP3D` regression sweep **532/532 PASS** (528 pre-existing + 4 new V10_30 + 1 pre-existing skip); mandatory commit gate 7/7 PASS. Closes the deferred Phase 3 from v10.17.0's plan with focused MINOR scope. Also bumps `getVersion()` 10.17.0 → 10.18.0. Codestream bytes byte-identical to v10.17.0. See [RELEASE_NOTES_v10.18.0.md](RELEASE_NOTES_v10.18.0.md).

**v10.17.0** ships **`J2KDICOMHelpers` (Phase 1)** — a new public SwiftPM library closing a long-standing product-layer gap. Per [ADR-004](Documentation/ADR/ADR-004-no-dicom-dependency.md), J2KSwift core libraries don't depend on any DICOM library. The CLI ships a `DICOMSupport.swift` for `.dcm` file loading (uses a Python fallback for compressed transfer syntaxes), but the `DICOMTransferSyntax` / `DICOMTransferSyntaxInfo` types there are CLI-private — not surfaced to library consumers. Library consumers using their own DICOM parser (DICOMKit, pydicom-via-XPC, etc.) extract `(Transfer Syntax UID, Pixel Data bytes)` and previously had to hand-roll the UID-to-`J2KEncodingConfiguration` mapping. v10.17.0 lifts the UID-bridge layer into a new public `J2KDICOMHelpers` SwiftPM library: **`J2KDICOMTransferSyntax`** enum (`CaseIterable` over all seven DICOM Part 5 Annex A JPEG 2000 / HTJ2K UIDs — `j2kLossless`, `j2kLossy`, `j2kPart2MulticompLossless`, `j2kPart2Multicomp`, `htj2kLossless`, `htj2kLossyConstant`, `htj2kLossy`) with `init?(uid:)` / `var uid` round-trip (whitespace + trailing-NUL tolerant per PS3.5 §6.2), `isLossless` / `isHTJ2K` / `isPart2` classification flags, and `encodingConfiguration(bitDepth:psnrTarget:)` returning a matching `J2KEncodingConfiguration` (with `htj2kBlockFormat: .conformant` for DICOM interop); **`J2KDICOMCodestreamDetector.detect(_:)`** sniffs SOC + (optional) CAP markers in the first ~256 bytes to classify raw codestreams as `.j2kLossless` (Part 1, no CAP) or `.htj2kLossless` (Part 15, CAP present), returning nil for non-J2K bytes (JPEG SOI, PNG signature, random bytes all correctly reject); and **`J2KDICOMPhotometricInterpretation`** enum mirror (`MONOCHROME1/2`, `RGB`, `YBR_FULL/422`, `YBR_RCT/ICT`) with bidirectional `J2KColorSpace` mapping (greyscale ↔ MONOCHROME2, sRGB ↔ RGB, yCbCr ↔ YBR_FULL; `hdr`/`hdrLinear`/`iccProfile`/`unknown` return nil reverse). ADR-004 compliant: **no DICOM library dependency anywhere in J2KSwift** — the new product depends only on J2KCore + J2KCodec; consumers not importing `J2KDICOMHelpers` are completely unaffected. **Validation**: `V10_29_*` test suites **26/26 PASS** release mode (9 TransferSyntaxRoundTrip + 5 EncodingConfigurationParity + 6 CodestreamDetection + 6 PhotometricInterpretation); end-to-end bit-exact lossless round-trip confirmed for `.j2kLossless` + `.htj2kLossless` via the helpers-built configurations. Full `swift test --filter JP3D` regression sweep **528/528 PASS**; mandatory commit gate 7/7 PASS. Also bumps `getVersion()` 10.16.0 → 10.17.0. Phase 1 deliberately ships the smallest viable shape — UID-and-config bridge only, no DICOM file parsing (Phase 2 territory: extract more of `J2KCLI/DICOMSupport.swift` or ship a DICOMKit adapter as a sibling opt-in product). AsyncSequence progress streams (originally Phase 3) deferred to a dedicated MINOR. See [RELEASE_NOTES_v10.17.0.md](RELEASE_NOTES_v10.17.0.md).
Expand Down
Loading
Loading