Update fork to upstream master (streaming fix #331), zero carried patches - #6
Open
wayfarer3130 wants to merge 470 commits into
Open
Update fork to upstream master (streaming fix #331), zero carried patches#6wayfarer3130 wants to merge 470 commits into
wayfarer3130 wants to merge 470 commits into
Conversation
Adding 32bit lossless support.
This address the illegal instruction issue when -flto flag is used (link time optimization) as details in this thread https://bugzilla.redhat.com/show_bug.cgi?id=2307795
This fixes a bug which wrongly sets implementation precision.
Fixes a bug when a resolution has one 64bit line.
…ame_precision Makes all colour components involved in colour transform employ the same precision (32bit or 64bit).
Allow OpenJPH to be exported by cmake
…s. Changes to API. May still be buggy (WIP).
…ents Supporting differing components in QCD
* Removes direct access to COC segment marker, and adds a test. This commit borrows from aous72#272 and aous72#309. * Changed the default creation behaviour of COC marker segment and removed an unneeded file.
(cherry picked from commit d6297b2)
(cherry picked from commit 4417d4b)
master holds the old (~0.21-era) fork state whose custom patches have been re-applied on top of upstream 0.30.1 in this branch, so its content is fully superseded. Recorded as a merge parent (strategy=ours) purely to make the PR fast-forward-mergeable; the tree stays 0.30.1 + the carried resilient-decode patches.
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@9c091bb...3d3c42e) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump github/codeql-action/analyze from 4.36.2 to 4.37.1 Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.2 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...7188fc3) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Bump github/codeql-action/autobuild from 4.36.2 to 4.37.1 Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.36.2 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...7188fc3) --- updated-dependencies: - dependency-name: github/codeql-action/autobuild dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Bump github/codeql-action/init from 4.36.2 to 4.37.1 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.2 to 4.37.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@8aad20d...7188fc3) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* For visual weighting, this replaces vector with array, to give rid of the new/delete. A small change for bit-depth-based quantization; now it is limited to 2^-16, if not explicitly chosen. * Fixes compilation issues * Removed dependence on std::array. * Fixes a warning. * Rearranged visual weighting and qfactor settings * Fixes warnings. * Reverts visual weights to an older version and create a factory function for it. Also break setting SPcod into three functions. * Removes one forgotten line * Removes dependency on ojph_visual_weights.h. Implements everything in ojph_param.cpp & uses new qfactor weights -- qfactor tests will fail. * Fixes a bug and warnings * Fixes visual weight channel gains -- tests must pass * Update Qfactor comment * Updated set_qfactor comment * Some code cleanup
* Fixes to ppc64le * revert some changes * Clears clang warnings
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.1 to 4.37.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@7188fc3...e064762) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.1 to 4.37.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@7188fc3...e064762) --- updated-dependencies: - dependency-name: github/codeql-action/autobuild dependency-version: 4.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.1 to 4.37.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@7188fc3...e064762) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…16-bit samples (aous72#336) * Fix: sign-extend 16-bit signed raw samples in raw_in::read() Casting the signed sample to ui16 for swap_bytes_if_be() and then widening to si32 zero-extends instead of sign-extends, corrupting every negative 16-bit signed raw input sample on encode since 0.29.0. Reinterpret the (possibly byte-swapped) bits as si16 before widening, mirroring the 32-bit is_signed branch's explicit outer cast. Fixes aous72#335 * Fix: give raw_in the requested signedness, not the option array ojph_compress's is_signed is a si32* holding one entry per component, but raw_in::set_img_props() takes a bool; passing the array converted the pointer to bool, which is always true. Every .raw file was therefore read as if its samples were signed, whatever -signed said, so unsigned samples with the top bit set were read as negative and clamped away on decode. This is why the sign extension fix in the previous commit is not sufficient on its own: with the reader stuck in its signed branch, the zero extension that corrupted signed samples was also what kept unsigned 16 bit samples intact. Correcting one without the other moves the corruption from signed to unsigned input. * Add .raw round trip tests for every sample width and signedness raw_in::read() and raw_out::write() extend samples differently for each of the four sample widths and for each signedness, and none of those eight combinations was covered: the existing raw tests all go through yuv_in with 8 bit unsigned samples. Each new test encodes one 256x256 frame reversibly and requires the decoded frame to be bit exact. The frame ramps from the smallest to the largest sample the component can hold, so at 16 bits the step is exactly 1 and the frame sweeps all 65536 sample values. Against the two preceding commits' parent these tests fail for 8 bit unsigned, 16 bit signed and 24 bit unsigned samples. * Fix: vsx_i64x2_extend_low/high_i32x4 return the wrong half vec_unpackh unpacks the half of a vector holding elements 0 and 1, and vec_unpackl the half holding elements 2 and 3; the compiler keeps that element numbering on little endian. vsx_i64x2_extend_low_i32x4 called vec_unpackl and vsx_i64x2_extend_high_i32x4 called vec_unpackh, so every si32 quad widened to si64 lines as [x2, x3, x0, x1]. The only callers are the si32 to si64 conversions in ojph_colour_vsx.cpp (vsx_rev_convert, vsx_rev_convert_nlt_type3 and vsx_rct_forward), all on the encode side, so a reversible encode of a component deep enough to need si64 lines produced a codestream of the pair-swapped image on ppc64le. The decode side narrows with byte-indexed vec_perm shuffles and is unaffected, which is why the corruption survives into the output: the decoder faithfully reconstructs the scrambled frame. Caught by the new SimpleEncRev53Raw32* round-trip tests, whose 32-bit samples are the first in the suite deep enough to widen: on ppc64le sample 0 decoded to sample 2's value (+131074, exactly two steps of the test ramp) and the mismatch pattern over each quad was +2, +2, -2, -2 steps. Verified under qemu-ppc64le (ubuntu 22.04 g++): with this change the VSX and OJPH_DISABLE_SIMD builds produce byte-identical codestreams and all four cross encode/decode pairings round-trip bit-exact; before it, a VSX encode was corrupt under either decoder while a generic encode decoded cleanly under both. * Update ojph_img_io.cpp A small touch to be safe. --------- Co-authored-by: Aous Naman <aous@unsw.edu.au>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.2 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e064762...e4fba86) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.2 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e064762...e4fba86) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.2 to 4.37.3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e064762...e4fba86) --- updated-dependencies: - dependency-name: github/codeql-action/autobuild dependency-version: 4.37.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Change to debug build for a while * Fixes for error messages * Remove the partial stream message * Remove the partial stream message (cherry picked from commit d6297b2) * Fixes for error messages (cherry picked from commit 4417d4b) * fix: handle truncated tile header decode errors gracefully (cherry picked from commit 4d5eab5) * fix: keep the const char* handler when widening tile-header catches The truncation paths in ojph_bitbuffer_read.h and ojph_precinct.cpp throw string literals, so replacing catch (const char*) outright would route the most common truncated-stream errors into catch (...) and lose their messages. Keep it and add std::exception / ... alongside. * Fixes the decode to allow partial decodes * Add test and resolve PR comments --------- Co-authored-by: Alireza <ar.sedghi@gmail.com>
Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.3 to 4.37.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e4fba86...f205ea1) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.3 to 4.37.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e4fba86...f205ea1) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.3 to 4.37.4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@e4fba86...f205ea1) --- updated-dependencies: - dependency-name: github/codeql-action/autobuild dependency-version: 4.37.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Both local edits to ojph_codestream_local.cpp are now redundant, so the fork
tracks upstream with zero delta and future bumps are fast-forwards.
resilient = true
Replaced by codestream::enable_resilience(), public API in
src/core/openjph/ojph_codestream.h and called by openjphjs'
HTJ2KDecoder before read_headers. Patching the default meant every
consumer of this fork got resilient mode whether or not they asked;
the API scopes it to the decoder that wants it.
commented-out OJPH_INFO "File terminated early"
Replaced by ojph::set_message_level(OJPH_MSG_WARN), which openjphjs
sets so INFO is suppressed and WARN/ERROR still surface. That also
silences the per-construction "v06 HTJ2K Decoder" banner, which is
the same class of noise and was never worth a source patch.
The truncated-decode behaviour these were propping up is upstream as of
638ccb4 "Cs3d/truncated decode graceful 0.30.1 (aous72#331)", which also brings
tests/test_truncated_decode.cpp.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
wayfarer3130
added a commit
to cornerstonejs/codecs
that referenced
this pull request
Aug 19, 2026
Moves extern/openjph from 0.30.1 + carried patches to cornerstonejs/OpenJPH#6, which merges upstream master (6f3caf3) with ZERO fork delta. Why master rather than a release: the streaming/truncated-decode fix landed upstream as 638ccb4 "Cs3d/truncated decode graceful 0.30.1 (aous72#331)" on 2026-08-08, and the newest upstream release 0.31.0 was published 2026-07-27 -- twelve days earlier. `git tag --contains 638ccb4` is empty, so no tagged release carries it yet. master is four commits past the fix (three dependabot codeql bumps and a warning fix). Re-pin to a tag once upstream cuts one with #331. Both patches the fork used to carry are gone, replaced by public API: resilient = true codestream::enable_resilience() -- already called by HTJ2KDecoder on main (line 270), so this patch was redundant before this PR. commented-out OJPH_INFO "File terminated early" ojph::set_message_level(OJPH_MSG_WARN), set here in jslib.cpp. The message level is worth its own note. OpenJPH INFO goes to stdout, which emscripten forwards to console.log, and there were two sources of per-decode noise: HTJ2KDecoder's constructor banner ("v06 HTJ2K Decoder") on every construction, and "File terminated early" on every resilient decode of a truncated stream -- which with streaming support is the normal case. Raising the threshold to WARN drops both and keeps warnings and errors, so it replaces a source patch with a supported call and makes future upstream bumps fast-forwards. Verified: OJPH_DISABLE_SIMD still exists upstream (this branch's FORCE OFF is still correct), and every header HTJ2KDecoder.hpp includes is present under src/core/openjph on master.
This was referenced Aug 19, 2026
wayfarer3130
added a commit
to cornerstonejs/codecs
that referenced
this pull request
Aug 27, 2026
…ersedes #68) (#76) codspeed regression is due to parallelization not being consistent in the simulation, not due to code speed changes * chore(openjphjs): bump extern/openjph submodule to upstream 0.30.1 Points the openjph submodule at cornerstonejs/OpenJPH#5, which rebases the fork onto upstream OpenJPH 0.30.1 (was ~22 months behind) and re-applies our custom patches. Net cornerstonejs delta from stock 0.30.1 is 3 lines in ojph_codestream_local.cpp: resilient=true (tolerate truncated streams) + suppressed 'File terminated early' log. Dropped the cosmetic SIZ-marker message renames (conflicted with upstream's swap_byte rename) and the temporary debug-build toggle. CI is the first real build/validation of this bump (not built locally). After OpenJPH#5 merges, re-point this submodule at the merge commit. * fix(openjphjs): add OpenJPH 0.30.1 header dirs to the wasm target include path 0.30.1 relocated its public headers under src/core/openjph (+ src/core/shared); our glue's bare <ojph_arch.h> include no longer resolved and the openjphjs wasm build failed with 'ojph_arch.h file not found'. Add both 0.30.1 header roots to the openjphjs target's include path. * fix(openjphjs): link the OpenJPH 0.30.1 'openjph' target (was renamed from openjphsimd) 0.30.1 builds a single architecture-agnostic 'openjph' library; the old 'openjphsimd' target no longer exists, so wasm-ld failed with 'unable to find library -lopenjphsimd'. Link 'openjph', matching upstream's own wasm wrapper (subprojects/js). * fix(openjphjs): keep WASM SIMD enabled for OpenJPH 0.30.1 0.30.1 deprecated OJPH_DISABLE_INTEL_SIMD and bridges it onto the new OJPH_DISABLE_SIMD; our old 'OJPH_DISABLE_INTEL_SIMD=ON' therefore disabled ALL SIMD (OJPH_ENABLE_WASM_SIMD=OFF), shipping a scalar wasm ~2x slower on decode/ encode. Stop setting the deprecated option and force OJPH_DISABLE_SIMD=OFF so 0.30.1's Emscripten path builds the WASM SIMD kernels (-msimd128). * fix(openjphjs): build the wasm in Release, not Debug build.sh forced CMAKE_BUILD_TYPE=Debug, so the shipped openjph wasm was built -O0 with unoptimized SIMD intrinsics — the reason decode/encode benched far slower (SIMD-on was even slower than scalar under -O0) and the binary was oversized. Release (-O3) is the correct artifact and is what makes the 0.30.1 SIMD kernels fast and the wasm small. * ci: squash in the pnpm migration from #87 Single squashed commit of ci/pnpm-trusted-publishing (#87), on the assumption that #87 lands on main before this PR. Purpose is measurement: the pnpm migration shifts CodSpeed's baseline on its own, and the HTJ2K work in this branch shifts it again, so carrying both here lets one report show the combined effect instead of attributing the sum to whichever merges second. Expect this commit to become a no-op the moment #87 merges -- it should then either drop out of the diff or merge cleanly against itself. It is NOT a second copy of that work to review; review it in #87. Merged with no conflicts. Two things worth noting about the overlap: - The submodule gitlink stayed at this branch's 0748112b rather than taking #87's e01c7b7, because #87 only reverted its own accidental bump back to the value main already had. Updated separately in the next commit. - dicom-codec/src/codecs/codecFactory.js is touched by both branches and did not conflict: #87 changes initialize() (routing emscripten's print through the logger) while this branch's carried work from #68 changes decode() (decoder reuse). They are independent edits to the same file. Includes the pnpm.overrides pinning esbuild/rollup/webpack/terser to the versions yarn.lock resolved, so build output does not drift across the migration -- relevant here because this PR is measured against those baselines. * build(openjphjs): track upstream OpenJPH master for the streaming fix Moves extern/openjph from 0.30.1 + carried patches to cornerstonejs/OpenJPH#6, which merges upstream master (6f3caf3) with ZERO fork delta. Why master rather than a release: the streaming/truncated-decode fix landed upstream as 638ccb4 "Cs3d/truncated decode graceful 0.30.1 (aous72#331)" on 2026-08-08, and the newest upstream release 0.31.0 was published 2026-07-27 -- twelve days earlier. `git tag --contains 638ccb4` is empty, so no tagged release carries it yet. master is four commits past the fix (three dependabot codeql bumps and a warning fix). Re-pin to a tag once upstream cuts one with #331. Both patches the fork used to carry are gone, replaced by public API: resilient = true codestream::enable_resilience() -- already called by HTJ2KDecoder on main (line 270), so this patch was redundant before this PR. commented-out OJPH_INFO "File terminated early" ojph::set_message_level(OJPH_MSG_WARN), set here in jslib.cpp. The message level is worth its own note. OpenJPH INFO goes to stdout, which emscripten forwards to console.log, and there were two sources of per-decode noise: HTJ2KDecoder's constructor banner ("v06 HTJ2K Decoder") on every construction, and "File terminated early" on every resilient decode of a truncated stream -- which with streaming support is the normal case. Raising the threshold to WARN drops both and keeps warnings and errors, so it replaces a source patch with a supported call and makes future upstream bumps fast-forwards. Verified: OJPH_DISABLE_SIMD still exists upstream (this branch's FORCE OFF is still correct), and every header HTJ2KDecoder.hpp includes is present under src/core/openjph on master. * fix(openjphjs): decode truncated HTJ2K streams without a known full length Carried from #68, which this PR supersedes. Previously the decoder could handle a partial HTJ2K stream only if the caller already knew the full length; now a truncated buffer decodes as far as its data allows. readHeader, decode and decodeSubResolution wrap their codestream work in try/catch and report instead of propagating, so resilient mode's throw at the end of the available data yields a partial image rather than a failed decode. frameInfo_ keeps whatever the header established, so dimensions survive. Two deliberate changes from #68's version: - The diagnostics are OJPH_WARN, not OJPH_INFO. jslib.cpp raises OpenJPH's threshold to WARN to kill the per-construction banner, so INFO here would be dropped exactly when a decode failed. - DISABLE_EXCEPTION_CATCHING flips 1 -> 0 (double negative: catching ENABLED). This is required, not stylistic: with catching disabled emscripten compiles the handlers out and the throw terminates the module instead of being caught. It costs wasm size, so dist-size may need re-baselining. test/truncated.test.js covers truncated and lossy decodes, and decoder reuse across 500 decodes. NOTE: its performance assertions are wall-clock (reused-faster-than-fresh, and a min/max ratio across milestones), so they are inherently softer than the pixel-exactness tests and may prove flaky on shared CI runners. Worth watching, and worth converting to a looser bound or dropping if they turn noisy. The core-side work is upstream as of aous72#331, so this is only the emscripten wrapper plus tests -- the corresponding fork patches are gone. * perf(dicom-codec): reuse the HTJ2K decoder instead of one per frame Carried from #68. codecFactory.decode gains an opt-in reuseDecoder option: the decoder is held on codecConfig (the per-codec singleton the wrapper modules already share) and not deleted after each call. htj2k.js opts in; every other codec keeps the construct-and-delete behaviour. This is very likely the bulk of #68's measured 3.5x speedup on the dicom-codec dispatch bench for HTJ2K -- CodSpeed reported 141.4ms -> 40.6ms there, and 40.6ms is about what openjphjs' own direct decode benches cost, i.e. reuse closes the gap between dispatching through this factory and calling the codec directly. Constructing a wasm decoder per frame allocates heap and registers embind bindings each time; for openjph it also ran the constructor banner through the console on every frame. Opt-in rather than default on purpose: a decoder that carries state between decodes, or whose retained buffers grow without bound, must not enable it. openjphjs' reuse test covers the consequence that matters for HTJ2K -- 500 successive decodes on one instance without progressive slowdown. Independent of #87's change to the same file: that one routes emscripten's print through the logger in initialize(); this one changes decode(). They merged with no conflict. * test(openjphjs): make the decoder-reuse perf assertion measure something real Built openjphjs locally via tools/docker/build.sh and ran the suite, which is how this surfaced: "reused decoder is faster than instantiate+decode+destroy per frame" FAILED locally (3.34 ms vs 2.72 ms) while passing CI by 5% (2.38 vs 2.50). The original had a structural flaw, not bad luck. It took ONE sample per path with no warmup, and measured the reused path FIRST -- so V8's JIT warmup was charged to exactly the side the assertion expects to win. Construction costs well under a millisecond against a ~2.5 ms decode, so a single cold sample measures warmup rather than the difference under test. Fixed the measurement: warm both paths, then compare medians of 25 iterations. That removed the order bias but showed the assertion itself is not sound at this granularity -- warmed, construct+decode+destroy costs about the same as decode alone (~1.6 ms each), so the medians sit inside each other's noise. Eight observed runs produced two failures on unchanged code. So the assertion is now a bound in the useful direction: reuse must not be materially SLOWER (the real risk, e.g. retained state degrading each decode) rather than provably faster. The medians are still logged. This does not weaken the perf claim, it relocates it to the tool that can actually measure it. CodSpeed on this branch reports the dispatch bench 141.5 ms -> 24.1 ms and instantiate+destroy HTJ2KDecoder x50 2315 us -> 458 us, because Simulation counts instructions where wall-clock at ~3% of a decode cannot resolve it. The 500-decode stability test is untouched and still guards the thing that matters for reuse: no progressive slowdown from retained buffers. Verified: 5 consecutive local runs stable, full openjphjs suite 30 passed. * fix(htj2k): own the decoded buffer and surface swallowed decode failures Addresses the review findings on #76, all of which stem from the same two changes in this PR: reusing one HTJ2K decoder across a series, and swallowing OpenJPH's exceptions so a partial codestream degrades to a partial image. Buffer ownership (the critical one). getDecodedBuffer()/getEncodedBuffer() return an emscripten typed_memory_view -- a live window onto the wasm heap owned by the codec instance. Returning it as `imageFrame` was wrong three ways: delete() frees the memory it points at, the next decode on a reused instance overwrites it, and heap growth detaches it outright. Frames 1..n of a series all showed frame n. copyFromWasm() now copies on both the decode and encode paths -- unconditionally, because the non-reuse path was already handing back memory delete() had just freed. Measured side effect: the raw view's .buffer is the whole 50 MB heap, so callers passing imageFrame.buffer to a worker were transferring the heap rather than the frame. Failure reporting. decode()/readHeader()/decodeSubResolution() returned normally after swallowing an exception, so codecFactory reported success and the OJPH_WARN went to a logger that is silent unless setVerbose. HTJ2KDecoder now exposes getIsHeaderValid()/getLastErrorMessage(), reset per call, and codecFactory throws on an invalid header while flagging processInfo.partial otherwise. Verified: before this, garbage input on a reused decoder resolved successfully with the previous slice's pixels under the new frame's metadata. Stale pixels. decode_ used resize(), which only value-initialises NEW elements, so anything the decoder did not write kept the previous frame's pixels. Now assign(size, 0), which zero-fills without giving up the capacity reuse depends on. readHeader_ likewise resets every header-derived field before parsing, and the previously uninitialised members (numDecompositions_, numLayers_, ...) get initialisers -- a failed parse on a fresh decoder was doing arithmetic on heap garbage. Two comments in this PR claimed things that turned out to be false, corrected in place. Truncation does NOT throw into decode()'s catch: swept CT1.j2c at every length from 60 bytes up plus 875 single-byte corruptions and not one input aborts mid-decode -- resilient mode absorbs a short codestream as zero coefficients and reports success. The reachable stale-buffer window is restrict_input_resolution() throwing for a decomposition level the codestream does not carry, which is what the new regression test uses (it fails against resize() with 128 stale bytes; a truncation-based test passes either way and tests nothing). Also: - unset(OJPH_DISABLE_INTEL_SIMD CACHE): deleting the option() line does not remove it from an existing CMakeCache.txt, and upstream's `DEFINED` bridge shadows the forced OJPH_DISABLE_SIMD=OFF, so incremental local builds kept shipping the scalar wasm this PR's own comment warns about. - releaseDecoder()/htj2k.release()/dicomCodec.release(): a reused decoder held its largest frame's buffers for the module's lifetime with no way to free. - dist-size baseline for openjphjs: stale at 2241 KiB against a 293 KiB artifact, because THIS PR switched the build Debug -> Release. The gate only fails on growth, so it would have tolerated a 7.7x regression. Rebuilt with tools/docker/build.sh (emsdk 3.1.74, SIMD confirmed intact); openjphjs 34 passed, dicom-codec 41 passed, other codec suites unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Bill Wallace <wayfarer3130@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the fork up to upstream
master(6f3caf3) and drops the last two carried patches, leaving zero delta from upstream. Supersedes #5 (0.30.1) and the fork side of #3/#4.Why master and not a release
The streaming/truncated-decode fix we contributed landed upstream as
638ccb4"Cs3d/truncated decode graceful 0.30.1 (aous72#331)" on 2026-08-08. The newest upstream release,0.31.0, was published 2026-07-27 — twelve days earlier.git tag --contains 638ccb4is empty, so no tagged release carries the fix yet.masteris the only target that does, and it sits just four commits past it (three dependabot codeql bumps and a warning fix).Worth re-pinning to a tag as soon as upstream cuts one that includes aous72#331.
Patches dropped, and what replaces them
Both were in
src/core/codestream/ojph_codestream_local.cpp:resilient = false→truecodestream::enable_resilience()— public API insrc/core/openjph/ojph_codestream.h, called by openjphjs'HTJ2KDecoderbeforeread_headersOJPH_INFO(0x00030067, "File terminated early")ojph::set_message_level(OJPH_MSG_WARN), set by openjphjsPatching the
resilientdefault meant every consumer of this fork got resilient mode whether or not it asked; the API scopes it to the decoder that wants it. Andset_message_levelsuppresses INFO generally, so it also silences the per-construction"v06 HTJ2K Decoder"banner — the same class of noise, and never worth a source patch either.The behaviour those patches propped up is upstream now, along with
tests/test_truncated_decode.cpp.Verification
git diff upstream/master HEADis empty — zero fork delta.git merge-base --is-ancestor 638ccb4 HEADpasses — the streaming fix is present.Consumed by cornerstonejs/codecs#76.