From 037402f7f6a1fc07593be302e4f578f6c4870c6e Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Tue, 10 Feb 2026 07:50:41 -0500 Subject: [PATCH] fix: Better handle octet stream case (#5789) * Better handle octet stream case * Use data source config stack options * Use immutability helper * Use immutability helper (update) --------- Co-authored-by: Bill Wallace --- extensions/cornerstone/package.json | 2 +- extensions/cornerstone/src/index.tsx | 30 +++++++++++++++---- .../docs/configuration/configurationFiles.md | 12 ++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index df312ad35..8cd3d4471 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -63,10 +63,10 @@ "@itk-wasm/morphological-contour-interpolation": "1.1.0", "@kitware/vtk.js": "34.15.1", "html2canvas": "1.4.1", + "immutability-helper": "3.1.1", "lodash.compact": "3.0.1", "lodash.debounce": "4.0.8", "lodash.flatten": "4.4.0", - "lodash.merge": "4.6.2", "lodash.zip": "4.2.0", "shader-loader": "1.3.1", "worker-loader": "3.0.8" diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index 5111dbaf5..9ca80a5da 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -59,6 +59,7 @@ import utils from './utils'; import { useMeasurementTracking } from './hooks/useMeasurementTracking'; import { setUpSegmentationEventHandlers } from './utils/setUpSegmentationEventHandlers'; import { setUpAnnotationEventHandlers } from './utils/setUpAnnotationEventHandlers'; +import update from 'immutability-helper'; export * from './components'; const { imageRetrieveMetadataProvider } = cornerstone.utilities; @@ -75,7 +76,7 @@ const OHIFCornerstoneViewport = props => { ); }; -const stackRetrieveOptions = { +const DEFAULT_STACK_RETRIEVE_OPTIONS = { retrieveOptions: { single: { streaming: true, @@ -84,6 +85,12 @@ const stackRetrieveOptions = { }, }; +/** Normalize to immutability-helper spec: plain object → $merge, otherwise use as-is. */ +const toUpdateSpec = (obj: object) => + obj != null && typeof obj === 'object' && Object.keys(obj).some(k => k.startsWith('$')) + ? obj + : { $merge: (obj ?? {}) as object }; + const unsubscriptions = []; /** * @@ -94,7 +101,11 @@ const cornerstoneExtension: Types.Extensions.Extension = { */ id, - onModeEnter: ({ servicesManager, commandsManager }: withAppTypes): void => { + onModeEnter: ({ + servicesManager, + commandsManager, + extensionManager, + }: withAppTypes): void => { const { cornerstoneViewportService, toolbarService, segmentationService } = servicesManager.services; @@ -131,10 +142,17 @@ const cornerstoneExtension: Types.Extensions.Extension = { 'volume', cornerstone.ProgressiveRetrieveImages.interleavedRetrieveStages ); - // The default stack loading option is to progressive load HTJ2K images - // There are other possible options, but these need more thought about - // how to define them. - imageRetrieveMetadataProvider.add('stack', stackRetrieveOptions); + + /** + * Stack retrieve options: read from active data source configuration. + * Pass an immutability-helper spec (e.g. { $merge: {...} } or { $set: {...} }) in + * stackRetrieveOptions to customize. Plain object is treated as $merge for backward compat. + * Set streaming: false for uncompressed DICOM that requires full file before decode. + */ + const sourceConfig = extensionManager?.getActiveDataSource?.()?.[0]?.getConfig?.() ?? {}; + const config = sourceConfig.stackRetrieveOptions ?? {}; + const stackOptions = update(DEFAULT_STACK_RETRIEVE_OPTIONS, toUpdateSpec(config)) as typeof DEFAULT_STACK_RETRIEVE_OPTIONS; + imageRetrieveMetadataProvider.add('stack', stackOptions); }, getPanelModule, onModeExit: ({ servicesManager }: withAppTypes): void => { diff --git a/platform/docs/docs/configuration/configurationFiles.md b/platform/docs/docs/configuration/configurationFiles.md index 2af892bde..8df9cb26c 100644 --- a/platform/docs/docs/configuration/configurationFiles.md +++ b/platform/docs/docs/configuration/configurationFiles.md @@ -312,6 +312,18 @@ reasons: However, if you would like to get compressed data in a specific transfer syntax, you can modify the `acceptHeader` configuration or `requestTransferSyntaxUID` configuration. +### Data Source: stackRetrieveOptions +At the data source configuration level, you can set `stackRetrieveOptions` to customize Cornerstone stack image retrieval. Merged with defaults; only specify overrides. For example, set `streaming: false` when the data source returns uncompressed DICOM (e.g. `application/octet-stream` only) to avoid black image on load: + +```js +configuration: { + acceptHeader: ['application/octet-stream'], + stackRetrieveOptions: { + retrieveOptions: { single: { streaming: false } }, + }, +} +``` + ## Environment Variables We use environment variables at build and dev time to change the Viewer's