Skip to content
Open
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
13 changes: 12 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,22 @@ executors:

commands:
install-build-tools:
description: Install yarn + cmake + C++ build deps on top of the emsdk image
description: Install node + yarn + cmake + C++ build deps on top of the emsdk image
steps:
- run:
name: Install build tooling
command: |
# The emsdk 3.1.74 image ships node 20.18.0, but vite 7 (pulled in
# transitively by vitest 3) requires "^20.19.0 || >=22.12.0", so
# `yarn install` fails its engine check. Install node 22 and put it
# first on PATH for all later steps (mirrors the setup-node@v4 step
# the GitHub Actions build job already uses). emcc keeps using its
# own configured node, so the wasm build is unaffected.
wget -qO- "https://nodejs.org/dist/v22.12.0/node-v22.12.0-linux-x64.tar.gz" \
| tar --strip-components=1 -xz -C /usr/local
echo 'export PATH=/usr/local/bin:$PATH' >> "$BASH_ENV"
export PATH=/usr/local/bin:$PATH
node --version
npm install --global yarn@1.22.22
apt-get update
apt-get -y install build-essential
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ jobs:
# measured. A change here (e.g. an emsdk bump in this workflow, or
# a vitest upgrade in the root lockfile) must run the full
# pipeline with a full bench sweep — previously such PRs matched
# no package path and skipped CI entirely.
# no package path and skipped CI entirely. tools/ entries count
# too: browser-smoke drives the browser-smoke job, and the test
# suites import reference derivations from
# tools/fixture-verification/gen/ — a change to either must not
# land with CI skipped.
TOOLCHAIN_PATHS=(
".github/workflows/"
"package.json"
Expand All @@ -106,6 +110,8 @@ jobs:
"babel.config.json"
"lerna.json"
"tools/dist-size/"
"tools/browser-smoke/"
"tools/fixture-verification/"
)
for p in "${TOOLCHAIN_PATHS[@]}"; do
if ! git diff --quiet "$BASE"..HEAD -- "$p"; then
Expand Down
8 changes: 8 additions & 0 deletions packages/big-endian/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [0.1.1](https://github.com/cornerstonejs/codecs/compare/@cornerstonejs/codec-big-endian@0.1.0...@cornerstonejs/codec-big-endian@0.1.1) (2026-07-09)

**Note:** Version bump only for package @cornerstonejs/codec-big-endian





# [0.1.0](https://github.com/cornerstonejs/codecs/compare/@cornerstonejs/codec-big-endian@0.0.5...@cornerstonejs/codec-big-endian@0.1.0) (2022-02-09)


Expand Down
2 changes: 1 addition & 1 deletion packages/big-endian/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cornerstonejs/codec-big-endian",
"version": "0.1.0",
"version": "0.1.1",
"description": "",
"main": "dist/index.js",
"publishConfig": {
Expand Down
61 changes: 53 additions & 8 deletions packages/big-endian/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@
function swap16(val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}


function swap32(val) {
return (
((val & 0xff) << 24) |
((val & 0xff00) << 8) |
((val >> 8) & 0xff00) |
((val >> 24) & 0xff)
);
}

/**
* Decodes the provided pixelData and sets the `pixelData` property
* of the imageFrame object to the decoded representation.
*
* Set pixelData will be `Uint16Array` if `pixelRepresentation` is 0,
* otherwise it will be an `Int16Array`
*
*
* 16-bit and 32-bit data are byte-swapped and become unsigned
* (`pixelRepresentation` 0) or signed (`pixelRepresentation` 1) integer
* arrays. 32-bit data with no `pixelRepresentation` is treated as float
* (e.g. FloatPixelData), mirroring the little-endian package.
*
* @param {object} imageFrame
* @param {number} imageFrame.bitsAllocated - 16 or 8
* @param {number} imageFrame.bitsAllocated - 32, 16, 8 or 1
* @param {number} imageFrame.pixelRepresentation - 0 or 1
* @param {*} pixelData
* @param {*} pixelData
*/
function decode(imageFrame, pixelData) {
if (imageFrame.bitsAllocated === 16) {
Expand All @@ -38,8 +49,42 @@ function decode(imageFrame, pixelData) {
for (let i = 0; i < imageFrame.pixelData.length; i++) {
imageFrame.pixelData[i] = swap16(imageFrame.pixelData[i]);
}
} else if (imageFrame.bitsAllocated === 8) {
} else if (imageFrame.bitsAllocated === 8 || imageFrame.bitsAllocated === 1) {
// 1-bit data must already be extracted per frame by the caller:
// multi-frame 1-bit pixel data is bit-packed across frame boundaries,
// so frame extraction cannot happen at this level
imageFrame.pixelData = pixelData;
} else if (imageFrame.bitsAllocated === 32) {
let arrayBuffer = pixelData.buffer;

let offset = pixelData.byteOffset;
const length = pixelData.length;
// pixelData is typically a view into the full DICOM P10 buffer, so its
// byteOffset is even (DICOM guarantees even lengths) but not necessarily
// 4-byte aligned; 32-bit typed-array views require 4-byte alignment,
// so copy the bytes to a fresh, aligned buffer when needed
if (offset % 4) {
arrayBuffer = arrayBuffer.slice(offset);
offset = 0;
}

// The swap is a pure byte permutation, so it is done through a
// Uint32Array view regardless of how the result is interpreted below
const swapView = new Uint32Array(arrayBuffer, offset, length / 4);
for (let i = 0; i < swapView.length; i++) {
swapView[i] = swap32(swapView[i]);
}

// 32-bit PixelData is integer data (signed per pixelRepresentation);
// it is only float when pixelRepresentation is absent (e.g. the
// FloatPixelData element), matching cornerstone3D's decodeLittleEndian
if (imageFrame.pixelRepresentation === 0) {
imageFrame.pixelData = swapView;
} else if (imageFrame.pixelRepresentation === 1) {
imageFrame.pixelData = new Int32Array(arrayBuffer, offset, length / 4);
} else {
imageFrame.pixelData = new Float32Array(arrayBuffer, offset, length / 4);
}
}

return imageFrame;
Expand Down
63 changes: 63 additions & 0 deletions packages/big-endian/test/decode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,69 @@ describe("big-endian decode", () => {
expect(Array.from(imageFrame.pixelData)).toEqual([1, 2])
})

it("passes 1-bit pixel data through unchanged", () => {
const pixelData = new Uint8Array([0b10101010])
const imageFrame = { bitsAllocated: 1 }

decode(imageFrame, pixelData)

expect(imageFrame.pixelData).toBe(pixelData)
})

it("byte-swaps 32-bit unsigned pixel data into Uint32Array", () => {
const source = [1, 2, 0xdeadbeef]
// Build the big-endian byte stream for those values
const bigEndianBytes = new Uint8Array(source.length * 4)
const view = new DataView(bigEndianBytes.buffer)
source.forEach((value, i) => view.setUint32(i * 4, value, false))
const imageFrame = { bitsAllocated: 32, pixelRepresentation: 0 }

decode(imageFrame, bigEndianBytes)

expect(imageFrame.pixelData).toBeInstanceOf(Uint32Array)
expect(Array.from(imageFrame.pixelData)).toEqual([1, 2, 0xdeadbeef])
})

it("byte-swaps 32-bit signed pixel data into Int32Array", () => {
const source = [-1, 2, -100000]
const bigEndianBytes = new Uint8Array(source.length * 4)
const view = new DataView(bigEndianBytes.buffer)
source.forEach((value, i) => view.setInt32(i * 4, value, false))
const imageFrame = { bitsAllocated: 32, pixelRepresentation: 1 }

decode(imageFrame, bigEndianBytes)

expect(imageFrame.pixelData).toBeInstanceOf(Int32Array)
expect(Array.from(imageFrame.pixelData)).toEqual([-1, 2, -100000])
})

it("byte-swaps 32-bit pixel data into Float32Array when pixelRepresentation is absent", () => {
const source = new Float32Array([1.5, -2.25, 3.75])
const bigEndianBytes = new Uint8Array(source.length * 4)
const view = new DataView(bigEndianBytes.buffer)
source.forEach((value, i) => view.setFloat32(i * 4, value, false))
const imageFrame = { bitsAllocated: 32 }

decode(imageFrame, bigEndianBytes)

expect(imageFrame.pixelData).toBeInstanceOf(Float32Array)
expect(Array.from(imageFrame.pixelData)).toEqual([1.5, -2.25, 3.75])
})

it("realigns 32-bit pixel data when byteOffset is not 4-byte aligned", () => {
const source = new Float32Array([1.5, -2.25])
const padded = new Uint8Array(2 + source.length * 4)
const view = new DataView(padded.buffer)
source.forEach((value, i) => view.setFloat32(2 + i * 4, value, false))
const pixelData = new Uint8Array(padded.buffer, 2, source.length * 4)
const imageFrame = { bitsAllocated: 32 }

decode(imageFrame, pixelData)

expect(imageFrame.pixelData).toBeInstanceOf(Float32Array)
expect(Array.from(imageFrame.pixelData)).toEqual([1.5, -2.25])
})

it("returns the same imageFrame object", () => {
const imageFrame = { bitsAllocated: 8 }
const result = decode(imageFrame, new Uint8Array([0]))
Expand Down
12 changes: 12 additions & 0 deletions packages/charls/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [1.2.4](https://github.com/chafey/charls-js/compare/@cornerstonejs/codec-charls@1.2.3...@cornerstonejs/codec-charls@1.2.4) (2026-07-09)


### Bug Fixes

* **build:** try to build again ([#31](https://github.com/chafey/charls-js/issues/31)) ([a9bc1a9](https://github.com/chafey/charls-js/commit/a9bc1a918da268e88c550a44c626859450cdb7ae))
* **build:** try to build again ([#32](https://github.com/chafey/charls-js/issues/32)) ([b81e87e](https://github.com/chafey/charls-js/commit/b81e87e64c792648ae267c2a090f888896bb5823))





## [1.2.3](https://github.com/chafey/charls-js/compare/@cornerstonejs/codec-charls@1.2.2...@cornerstonejs/codec-charls@1.2.3) (2023-02-13)


Expand Down
4 changes: 2 additions & 2 deletions packages/charls/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/charls/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cornerstonejs/codec-charls",
"version": "1.2.3",
"version": "1.2.4",
"description": "WASM Build of CharLS",
"main": "dist/charlsjs.js",
"publishConfig": {
Expand Down
14 changes: 14 additions & 0 deletions packages/dicom-codec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [1.0.10](https://github.com/cornerstonejs/codecs/compare/@cornerstonejs/dicom-codec@1.0.9...@cornerstonejs/dicom-codec@1.0.10) (2026-07-09)


### Bug Fixes

* npm publish ([#56](https://github.com/cornerstonejs/codecs/issues/56)) ([1b03a10](https://github.com/cornerstonejs/codecs/commit/1b03a10d9f4ffa06fcd533208aa0ae5d31ce4428))
* npm publish ([#57](https://github.com/cornerstonejs/codecs/issues/57)) ([30e9d4d](https://github.com/cornerstonejs/codecs/commit/30e9d4db0f94f9711fe541850ccaceaedffd0fed))
* npm publish ([#59](https://github.com/cornerstonejs/codecs/issues/59)) ([d066ef2](https://github.com/cornerstonejs/codecs/commit/d066ef28dac987ca9c41a449e9330694fef474b4))
* trigger ci ([#54](https://github.com/cornerstonejs/codecs/issues/54)) ([5afcb5d](https://github.com/cornerstonejs/codecs/commit/5afcb5d895a365a49fc986be0ec1b60ee69cf809))





## [1.0.9](https://github.com/cornerstonejs/codecs/compare/@cornerstonejs/dicom-codec@1.0.8...@cornerstonejs/dicom-codec@1.0.9) (2025-10-22)

**Note:** Version bump only for package @cornerstonejs/dicom-codec
Expand Down
14 changes: 7 additions & 7 deletions packages/dicom-codec/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cornerstonejs/dicom-codec",
"version": "1.0.9",
"version": "1.0.10",
"description": "",
"main": "src/index.js",
"publishConfig": {
Expand All @@ -26,12 +26,12 @@
"webpack-merge": "^5.7.3"
},
"dependencies": {
"@cornerstonejs/codec-big-endian": ">=0.1.0",
"@cornerstonejs/codec-charls": ">=1.2.3",
"@cornerstonejs/codec-libjpeg-turbo-8bit": ">=1.2.2",
"@cornerstonejs/codec-little-endian": ">=0.0.6",
"@cornerstonejs/codec-openjpeg": "^1.3.0",
"@cornerstonejs/codec-openjph": "^2.4.7",
"@cornerstonejs/codec-big-endian": "^0.1.1",
"@cornerstonejs/codec-charls": "^1.2.4",
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.3",
"@cornerstonejs/codec-little-endian": "^0.0.7",
"@cornerstonejs/codec-openjpeg": "^1.3.1",
"@cornerstonejs/codec-openjph": "^2.4.8",
"browser-or-node": "^2.0.0",
"jpeg-lossless-decoder-js": "^2.0.4"
}
Expand Down
Loading
Loading