@cornerstonejs/core 5.2.2 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/esm/RenderingEngine/ContextPoolRenderingEngine.js +1 -1
  2. package/dist/esm/RenderingEngine/GenericViewport/GenericViewport.d.ts +1 -0
  3. package/dist/esm/RenderingEngine/GenericViewport/GenericViewport.js +3 -5
  4. package/dist/esm/RenderingEngine/GenericViewport/Planar/CpuImageSliceRenderPath.js +7 -3
  5. package/dist/esm/RenderingEngine/GenericViewport/Planar/PlanarRenderPathDecisionService.d.ts +4 -10
  6. package/dist/esm/RenderingEngine/GenericViewport/Planar/PlanarRenderPathDecisionService.js +8 -52
  7. package/dist/esm/RenderingEngine/GenericViewport/Planar/PlanarViewport.d.ts +7 -0
  8. package/dist/esm/RenderingEngine/GenericViewport/Planar/PlanarViewport.js +120 -9
  9. package/dist/esm/RenderingEngine/GenericViewport/Planar/PlanarViewportTypes.d.ts +2 -6
  10. package/dist/esm/RenderingEngine/GenericViewport/Planar/planarRenderPathSelector.d.ts +1 -1
  11. package/dist/esm/RenderingEngine/GenericViewport/Planar/planarRenderPathSelector.js +1 -1
  12. package/dist/esm/RenderingEngine/TiledRenderingEngine.js +2 -0
  13. package/dist/esm/RenderingEngine/WebGLContextPool.d.ts +1 -1
  14. package/dist/esm/RenderingEngine/WebGLContextPool.js +3 -1
  15. package/dist/esm/RenderingEngine/helpers/attachWebGLContextEvents.d.ts +2 -0
  16. package/dist/esm/RenderingEngine/helpers/attachWebGLContextEvents.js +22 -0
  17. package/dist/esm/enums/Events.d.ts +6 -1
  18. package/dist/esm/enums/Events.js +5 -0
  19. package/dist/esm/enums/RenderBackend.d.ts +7 -0
  20. package/dist/esm/enums/RenderBackend.js +7 -0
  21. package/dist/esm/enums/index.d.ts +2 -1
  22. package/dist/esm/enums/index.js +2 -1
  23. package/dist/esm/index.d.ts +3 -2
  24. package/dist/esm/index.js +3 -2
  25. package/dist/esm/init.d.ts +7 -1
  26. package/dist/esm/init.js +75 -32
  27. package/dist/esm/types/Cornerstone3DConfig.d.ts +2 -7
  28. package/dist/esm/utilities/renderingCapabilities.d.ts +16 -0
  29. package/dist/esm/utilities/renderingCapabilities.js +123 -0
  30. package/dist/esm/utilities/textureSupport.d.ts +7 -2
  31. package/dist/esm/utilities/textureSupport.js +68 -24
  32. package/dist/esm/version.d.ts +1 -1
  33. package/dist/esm/version.js +1 -1
  34. package/package.json +6 -6
@@ -43,7 +43,7 @@ class ContextPoolRenderingEngine extends BaseRenderingEngine {
43
43
  if (!this.contextPool) {
44
44
  const { rendering } = getConfiguration();
45
45
  const { webGlContextCount } = rendering;
46
- this.contextPool = new WebGLContextPool(webGlContextCount);
46
+ this.contextPool = new WebGLContextPool(webGlContextCount, this.id);
47
47
  }
48
48
  return this.contextPool;
49
49
  }
@@ -60,6 +60,7 @@ declare abstract class GenericViewport<TViewState extends object, TDataPresentat
60
60
  setRendered(): void;
61
61
  setNeedsRender(): void;
62
62
  abstract render(): void;
63
+ updateRenderingPipeline(): void;
63
64
  resize(): void;
64
65
  resetViewState(_options?: unknown): boolean;
65
66
  resizeForRenderingEngine({ keepCamera, }?: RenderingEngineResizeOptions): void;
@@ -161,6 +161,8 @@ class GenericViewport {
161
161
  setNeedsRender() {
162
162
  this.viewportStatus = ViewportStatus.NEEDS_RENDER;
163
163
  }
164
+ updateRenderingPipeline() {
165
+ }
164
166
  resize() {
165
167
  if (this.isDestroyed) {
166
168
  return;
@@ -190,10 +192,6 @@ class GenericViewport {
190
192
  const path = this.renderPathResolver.resolve(this.type, data, options);
191
193
  const renderPath = path.createRenderPath();
192
194
  const ctx = path.selectContext?.(this.renderContext) ?? this.renderContext;
193
- const existing = this.bindings.get(displaySetId);
194
- if (existing) {
195
- existing.removeData();
196
- }
197
195
  const attachment = await renderPath.addData(ctx, data, options);
198
196
  if (shouldIgnore?.()) {
199
197
  attachment.removeData();
@@ -204,7 +202,7 @@ class GenericViewport {
204
202
  throw new Error('Viewport has been destroyed');
205
203
  }
206
204
  const current = this.bindings.get(displaySetId);
207
- if (current && current !== existing) {
205
+ if (current) {
208
206
  current.removeData();
209
207
  }
210
208
  const role = options.role ?? 'overlay';
@@ -33,7 +33,7 @@ export class CpuImageSliceRenderPath {
33
33
  getImageData: () => this.getImageData(rendering),
34
34
  }, payload.image);
35
35
  compatibilityActor.setVisibility(true);
36
- const defaultViewport = getDefaultViewport(ctx.cpu.canvas, payload.image);
36
+ const defaultViewport = getDefaultViewport(ctx.cpu.canvas, payload.image, getCPUFallbackViewportModality(payload.image));
37
37
  defaultViewport.displayedArea = resolvePlanarCpuImageDisplayedArea(payload.image);
38
38
  const enabledElement = {
39
39
  canvas: ctx.cpu.canvas,
@@ -196,7 +196,7 @@ export class CpuImageSliceRenderPath {
196
196
  rendering,
197
197
  viewState: camera,
198
198
  });
199
- rendering.fitScale = getCPUFallbackScalarScale(getDefaultViewport(rendering.enabledElement.canvas, image).scale);
199
+ rendering.fitScale = getCPUFallbackScalarScale(getDefaultViewport(rendering.enabledElement.canvas, image, getCPUFallbackViewportModality(image)).scale);
200
200
  rendering.renderingInvalidated = true;
201
201
  if (projection) {
202
202
  applyPresentationState(rendering, projection.presentation, projection.resolvedICamera);
@@ -359,6 +359,10 @@ function worldToCPUImageDrawPoint(enabledElement, worldPos) {
359
359
  function markCpuImagePreScaled(image) {
360
360
  image.isPreScaled = image.preScale?.scaled;
361
361
  }
362
+ function getCPUFallbackViewportModality(image) {
363
+ return (image.preScale?.scalingParameters?.modality ||
364
+ metaData.get(MetadataModules.GENERAL_SERIES, image.imageId)?.modality);
365
+ }
362
366
  export function buildPlanarImageData(image, frameOfReferenceUID) {
363
367
  const metadata = getImageDataMetadata(image);
364
368
  const { dimensions, direction, modality, origin, spacing } = metadata;
@@ -428,7 +432,7 @@ async function updateRenderedImage(args) {
428
432
  const enabledElement = rendering.enabledElement;
429
433
  markCpuImagePreScaled(image);
430
434
  const camera = ctx.viewport.getViewState();
431
- const defaultViewport = getDefaultViewport(ctx.cpu.canvas, image);
435
+ const defaultViewport = getDefaultViewport(ctx.cpu.canvas, image, getCPUFallbackViewportModality(image));
432
436
  const previousViewport = enabledElement.viewport;
433
437
  defaultViewport.displayedArea = resolvePlanarCpuImageDisplayedArea(image);
434
438
  enabledElement.image = image;
@@ -1,6 +1,6 @@
1
+ import { RenderBackend } from '../../../enums/index.js';
2
+ import type { RenderBackendValue } from '../../../enums/index.js';
1
3
  import type { PlanarViewState, PlanarEffectiveRenderMode, PlanarOrientation, PlanarRegisteredDataSet } from './PlanarViewportTypes.js';
2
- export declare const DEFAULT_PLANAR_CPU_IMAGE_THRESHOLD: number;
3
- export declare const DEFAULT_PLANAR_CPU_VOLUME_THRESHOLD: number;
4
4
  export interface SelectedPlanarRenderPath {
5
5
  acquisitionOrientation?: PlanarViewState['orientation'];
6
6
  renderMode: PlanarEffectiveRenderMode;
@@ -8,22 +8,16 @@ export interface SelectedPlanarRenderPath {
8
8
  }
9
9
  export interface PlanarRenderPathDecisionOptions {
10
10
  orientation?: PlanarOrientation;
11
- cpuThresholds?: {
12
- image?: number;
13
- volume?: number;
14
- };
15
11
  useSliceRendering?: boolean;
16
- webGLAvailable?: boolean;
12
+ renderBackend?: RenderBackend | RenderBackendValue;
17
13
  }
18
14
  export declare class PlanarRenderPathDecisionService {
19
15
  select(dataSet: PlanarRegisteredDataSet, options?: PlanarRenderPathDecisionOptions): SelectedPlanarRenderPath;
20
16
  canRender(dataSet: PlanarRegisteredDataSet, options?: PlanarRenderPathDecisionOptions): boolean;
21
17
  private selectImageRenderMode;
22
18
  private selectVolumeRenderMode;
23
- private shouldUseCPUForImage;
24
- private shouldUseCPUForVolume;
19
+ private resolveBackend;
25
20
  }
26
21
  export declare const defaultPlanarRenderPathDecisionService: PlanarRenderPathDecisionService;
27
22
  export declare function selectPlanarRenderPath(dataSet: PlanarRegisteredDataSet, options?: PlanarRenderPathDecisionOptions): SelectedPlanarRenderPath;
28
23
  export declare function getPlanarAcquisitionOrientation(imageIds: string[]): PlanarViewState['orientation'] | undefined;
29
- export declare function shouldUseCPU(imageIds: string[], threshold?: number): boolean;
@@ -1,13 +1,11 @@
1
1
  import { vec3 } from 'gl-matrix';
2
2
  import cache from '../../../cache/cache.js';
3
- import { OrientationAxis } from '../../../enums/index.js';
4
- import { getConfiguration, getShouldUseCPURendering } from '../../../init.js';
3
+ import { OrientationAxis, RenderBackend } from '../../../enums/index.js';
4
+ import { getEffectiveRenderBackend } from '../../../init.js';
5
5
  import * as metaData from '../../../metaData.js';
6
6
  import { ActorRenderMode } from '../../../types/index.js';
7
7
  import { isValidVolume } from '../../../utilities/isValidVolume.js';
8
8
  import { getOrientationFromScanAxisNormal } from '../../helpers/getCameraVectors.js';
9
- export const DEFAULT_PLANAR_CPU_IMAGE_THRESHOLD = 64 * 1024 * 1024;
10
- export const DEFAULT_PLANAR_CPU_VOLUME_THRESHOLD = Number.POSITIVE_INFINITY;
11
9
  export class PlanarRenderPathDecisionService {
12
10
  select(dataSet, options = {}) {
13
11
  if (!dataSet.imageIds.length) {
@@ -31,7 +29,7 @@ export class PlanarRenderPathDecisionService {
31
29
  return {
32
30
  acquisitionOrientation,
33
31
  renderMode: useVolumePath
34
- ? this.selectVolumeRenderMode(dataSet, options)
32
+ ? this.selectVolumeRenderMode(options)
35
33
  : this.selectImageRenderMode(options),
36
34
  volumeId,
37
35
  };
@@ -57,29 +55,17 @@ export class PlanarRenderPathDecisionService {
57
55
  return true;
58
56
  }
59
57
  selectImageRenderMode(options) {
60
- return this.shouldUseCPUForImage(options)
58
+ return this.resolveBackend(options) === RenderBackend.CPU
61
59
  ? ActorRenderMode.CPU_IMAGE
62
60
  : ActorRenderMode.VTK_IMAGE;
63
61
  }
64
- selectVolumeRenderMode(dataSet, options) {
65
- return this.shouldUseCPUForVolume(dataSet, options)
62
+ selectVolumeRenderMode(options) {
63
+ return this.resolveBackend(options) === RenderBackend.CPU
66
64
  ? ActorRenderMode.CPU_VOLUME
67
65
  : ActorRenderMode.VTK_VOLUME_SLICE;
68
66
  }
69
- shouldUseCPUForImage(options) {
70
- if (options.webGLAvailable === false) {
71
- return true;
72
- }
73
- return getShouldUseCPURendering();
74
- }
75
- shouldUseCPUForVolume(dataSet, options) {
76
- if (options.webGLAvailable === false) {
77
- return true;
78
- }
79
- const configuredCpuThresholds = getConfiguredPlanarCpuThresholds();
80
- return shouldUseCPU(dataSet.imageIds, options.cpuThresholds?.volume ??
81
- configuredCpuThresholds?.volume ??
82
- DEFAULT_PLANAR_CPU_VOLUME_THRESHOLD);
67
+ resolveBackend(options) {
68
+ return getEffectiveRenderBackend(options.renderBackend);
83
69
  }
84
70
  }
85
71
  export const defaultPlanarRenderPathDecisionService = new PlanarRenderPathDecisionService();
@@ -112,30 +98,6 @@ export function getPlanarAcquisitionOrientation(imageIds) {
112
98
  }
113
99
  return orientation;
114
100
  }
115
- export function shouldUseCPU(imageIds, threshold = DEFAULT_PLANAR_CPU_IMAGE_THRESHOLD) {
116
- if (getShouldUseCPURendering()) {
117
- return true;
118
- }
119
- const imageId = imageIds[0];
120
- if (!imageId) {
121
- return false;
122
- }
123
- const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
124
- const rows = imagePlaneModule?.rows;
125
- const columns = imagePlaneModule?.columns;
126
- if (!isPositiveSafeInteger(rows) || !isPositiveSafeInteger(columns)) {
127
- return false;
128
- }
129
- if (!Number.isFinite(threshold)) {
130
- return false;
131
- }
132
- const normalizedThreshold = Math.trunc(threshold);
133
- if (!Number.isSafeInteger(normalizedThreshold) || normalizedThreshold < 0) {
134
- return false;
135
- }
136
- return (BigInt(rows) * BigInt(columns) * BigInt(imageIds.length) >=
137
- BigInt(normalizedThreshold));
138
- }
139
101
  function getVolumeId(dataSet) {
140
102
  return dataSet.volumeId || cache.generateVolumeId(dataSet.imageIds);
141
103
  }
@@ -154,9 +116,3 @@ function isVolumeBackedDataSet(dataSet, isAcquisitionPath) {
154
116
  }
155
117
  return !isAcquisitionPath && supportsVolumeRendering(dataSet);
156
118
  }
157
- function isPositiveSafeInteger(value) {
158
- return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
159
- }
160
- function getConfiguredPlanarCpuThresholds() {
161
- return getConfiguration().rendering?.planar?.cpuThresholds;
162
- }
@@ -35,6 +35,9 @@ declare class PlanarViewport extends GenericViewport<PlanarViewState, PlanarData
35
35
  private resolvedViewCacheRevision;
36
36
  private cachedCanvasDimensions?;
37
37
  private setDataRequestId;
38
+ private renderPipelineSwapId;
39
+ private readonly lastRenderPathErrorByDataId;
40
+ private readonly mountOptionsByDataId;
38
41
  static get useCustomRenderingPipeline(): boolean;
39
42
  constructor(args: PlanarViewportInput);
40
43
  setDisplaySets(...entries: Array<{
@@ -130,6 +133,9 @@ declare class PlanarViewport extends GenericViewport<PlanarViewState, PlanarData
130
133
  protected onDestroy(): void;
131
134
  protected modified(previousCamera?: ICamera): void;
132
135
  protected renderBindings(): boolean;
136
+ updateRenderingPipeline(): void;
137
+ private applyRenderingPipelineUpdate;
138
+ private reportRenderPathError;
133
139
  private requestRenderingEngineRender;
134
140
  private setRenderModeVisibility;
135
141
  protected createLegacyCompatibilityHost(): {
@@ -168,6 +174,7 @@ declare class PlanarViewport extends GenericViewport<PlanarViewState, PlanarData
168
174
  private isSegmentationOverlayBinding;
169
175
  private getDataSet;
170
176
  private loadPlanarData;
177
+ private getInheritedOverlayRenderBackend;
171
178
  private applyLoadedPlanarViewState;
172
179
  private createInitialVolumeSliceState;
173
180
  private resolveRequestedOrientation;
@@ -1,4 +1,4 @@
1
- import { Events, OrientationAxis, ViewportStatus, ViewportType, VOILUTFunctionType, } from '../../../enums/index.js';
1
+ import { Events, OrientationAxis, RenderBackend, ViewportStatus, ViewportType, VOILUTFunctionType, } from '../../../enums/index.js';
2
2
  import { ActorRenderMode } from '../../../types/index.js';
3
3
  import { isImageActor } from '../../../utilities/actorCheck.js';
4
4
  import cache from '../../../cache/cache.js';
@@ -7,6 +7,7 @@ import imageIdToURI from '../../../utilities/imageIdToURI.js';
7
7
  import { getImageDataMetadata } from '../../../utilities/getImageDataMetadata.js';
8
8
  import genericViewportDisplaySetMetadataProvider from '../../../utilities/genericViewportDisplaySetMetadataProvider.js';
9
9
  import triggerEvent from '../../../utilities/triggerEvent.js';
10
+ import eventTarget from '../../../eventTarget.js';
10
11
  import getMinMax from '../../../utilities/getMinMax.js';
11
12
  import renderingEngineCache from '../../renderingEngineCache.js';
12
13
  import { getCameraVectors } from '../../helpers/getCameraVectors.js';
@@ -35,6 +36,9 @@ class PlanarViewport extends GenericViewport {
35
36
  this.suppressEvents = false;
36
37
  this.resolvedViewCacheRevision = 0;
37
38
  this.setDataRequestId = 0;
39
+ this.renderPipelineSwapId = 0;
40
+ this.lastRenderPathErrorByDataId = new Map();
41
+ this.mountOptionsByDataId = new Map();
38
42
  this.renderingEngineId = args.renderingEngineId;
39
43
  this.canvas = args.canvas;
40
44
  this.sWidth = args.sWidth;
@@ -200,6 +204,7 @@ class PlanarViewport extends GenericViewport {
200
204
  if (!added) {
201
205
  return;
202
206
  }
207
+ this.mountOptionsByDataId.set(dataId, resolvedOptions);
203
208
  this.clearResolvedViewCache();
204
209
  this.setDefaultDataPresentation(dataId, {
205
210
  visible: true,
@@ -215,6 +220,8 @@ class PlanarViewport extends GenericViewport {
215
220
  }
216
221
  removeData(dataId) {
217
222
  this.clearResolvedViewCache();
223
+ this.mountOptionsByDataId.delete(dataId);
224
+ this.lastRenderPathErrorByDataId.delete(dataId);
218
225
  super.removeData(dataId);
219
226
  this.mountedData.handleRemovedData(dataId);
220
227
  if (!this.isDestroyed && this.getCurrentBinding()) {
@@ -764,17 +771,106 @@ class PlanarViewport extends GenericViewport {
764
771
  }
765
772
  let renderedByAdapter = false;
766
773
  const sourceBinding = this.getCurrentBinding();
767
- sourceBinding?.render?.();
768
- renderedByAdapter = renderedByAdapter || Boolean(sourceBinding?.render);
769
- for (const binding of this.bindings.values()) {
774
+ const renderBinding = (binding, dataId) => {
775
+ if (!binding.render) {
776
+ return;
777
+ }
778
+ renderedByAdapter = true;
779
+ try {
780
+ binding.render();
781
+ this.lastRenderPathErrorByDataId.delete(dataId);
782
+ }
783
+ catch (error) {
784
+ this.reportRenderPathError(error, dataId);
785
+ }
786
+ };
787
+ for (const [dataId, binding] of this.bindings.entries()) {
770
788
  if (binding === sourceBinding) {
771
- continue;
789
+ renderBinding(binding, dataId);
790
+ }
791
+ }
792
+ for (const [dataId, binding] of this.bindings.entries()) {
793
+ if (binding !== sourceBinding) {
794
+ renderBinding(binding, dataId);
772
795
  }
773
- binding.render?.();
774
- renderedByAdapter = renderedByAdapter || Boolean(binding.render);
775
796
  }
776
797
  return renderedByAdapter;
777
798
  }
799
+ updateRenderingPipeline() {
800
+ void this.applyRenderingPipelineUpdate();
801
+ }
802
+ async applyRenderingPipelineUpdate() {
803
+ if (this.isDestroyed) {
804
+ return;
805
+ }
806
+ const swapId = ++this.renderPipelineSwapId;
807
+ const isStale = () => swapId !== this.renderPipelineSwapId || this.isDestroyed;
808
+ let changed = false;
809
+ const sourceBinding = this.getCurrentBinding();
810
+ const bindingEntries = Array.from(this.bindings.entries());
811
+ const orderedEntries = [
812
+ ...bindingEntries.filter(([, binding]) => binding === sourceBinding),
813
+ ...bindingEntries.filter(([, binding]) => binding !== sourceBinding),
814
+ ];
815
+ for (const [dataId, binding] of orderedEntries) {
816
+ if (isStale()) {
817
+ return;
818
+ }
819
+ if (this.bindings.get(dataId) !== binding) {
820
+ continue;
821
+ }
822
+ const options = this.mountOptionsByDataId.get(dataId) ?? {};
823
+ try {
824
+ const { data, selectedPath } = await this.loadPlanarData(dataId, {
825
+ ...options,
826
+ role: binding.role,
827
+ });
828
+ if (isStale() || this.bindings.get(dataId) !== binding) {
829
+ continue;
830
+ }
831
+ if (selectedPath.renderMode === binding.rendering.renderMode) {
832
+ continue;
833
+ }
834
+ const added = await this.addLoadedData(dataId, data, {
835
+ renderMode: selectedPath.renderMode,
836
+ role: binding.role,
837
+ }, () => isStale() || this.bindings.get(dataId) !== binding);
838
+ changed = changed || added;
839
+ }
840
+ catch (error) {
841
+ this.reportRenderPathError(error, dataId);
842
+ }
843
+ }
844
+ if (isStale() || !changed) {
845
+ return;
846
+ }
847
+ const sourceRenderMode = this.getCurrentPlanarRendering()?.renderMode;
848
+ if (sourceRenderMode) {
849
+ this.renderContext.display.activateRenderMode(sourceRenderMode);
850
+ }
851
+ this.clearResolvedViewCache();
852
+ this.updateBindingsCameraState();
853
+ this.render();
854
+ triggerEvent(eventTarget, Events.RENDERING_PIPELINE_CHANGED, {
855
+ renderingEngineId: this.renderingEngineId,
856
+ viewportId: this.id,
857
+ });
858
+ }
859
+ reportRenderPathError(error, dataId) {
860
+ const message = error instanceof Error ? error.message : String(error);
861
+ const errorKey = dataId ?? '';
862
+ if (this.lastRenderPathErrorByDataId.get(errorKey) === message) {
863
+ return;
864
+ }
865
+ this.lastRenderPathErrorByDataId.set(errorKey, message);
866
+ console.error('[PlanarViewport] Render path error', dataId ?? '', error);
867
+ triggerEvent(eventTarget, Events.RENDER_PATH_ERROR, {
868
+ renderingEngineId: this.renderingEngineId,
869
+ viewportId: this.id,
870
+ dataId,
871
+ error,
872
+ });
873
+ }
778
874
  requestRenderingEngineRender() {
779
875
  const renderingEngine = renderingEngineCache.get(this.renderingEngineId);
780
876
  if (renderingEngine) {
@@ -927,8 +1023,8 @@ class PlanarViewport extends GenericViewport {
927
1023
  const resolvedOrientation = this.resolveRequestedOrientation(options.orientation);
928
1024
  const selectedPath = selectPlanarRenderPath(dataSet, {
929
1025
  orientation: resolvedOrientation,
930
- cpuThresholds: options.cpuThresholds,
931
- webGLAvailable: options.forceCPU ? false : undefined,
1026
+ renderBackend: options.renderBackend ??
1027
+ this.getInheritedOverlayRenderBackend(options.role),
932
1028
  });
933
1029
  const data = await this.dataProvider.load(dataId, {
934
1030
  acquisitionOrientation: selectedPath.acquisitionOrientation,
@@ -942,6 +1038,21 @@ class PlanarViewport extends GenericViewport {
942
1038
  resolvedOrientation,
943
1039
  };
944
1040
  }
1041
+ getInheritedOverlayRenderBackend(role) {
1042
+ if (role === 'source') {
1043
+ return undefined;
1044
+ }
1045
+ switch (this.getCurrentPlanarRendering()?.renderMode) {
1046
+ case ActorRenderMode.CPU_IMAGE:
1047
+ case ActorRenderMode.CPU_VOLUME:
1048
+ return RenderBackend.CPU;
1049
+ case ActorRenderMode.VTK_IMAGE:
1050
+ case ActorRenderMode.VTK_VOLUME_SLICE:
1051
+ return RenderBackend.GPU;
1052
+ default:
1053
+ return undefined;
1054
+ }
1055
+ }
945
1056
  applyLoadedPlanarViewState(resolvedOrientation, planarData, selectedPath) {
946
1057
  const isVolumePath = selectedPath.renderMode === ActorRenderMode.CPU_VOLUME ||
947
1058
  selectedPath.renderMode === ActorRenderMode.VTK_VOLUME_SLICE;
@@ -1,6 +1,6 @@
1
1
  import type vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
2
2
  import type vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3
- import type { BlendModes, InterpolationType, OrientationAxis, VOILUTFunctionType } from '../../../enums/index.js';
3
+ import type { BlendModes, InterpolationType, OrientationAxis, RenderBackend, RenderBackendValue, VOILUTFunctionType } from '../../../enums/index.js';
4
4
  import type { ActorEntry, ActorRenderMode, ColormapPublic, IImage, IImageVolume, OrientationVectors, Point2, Point3, VOIRange } from '../../../types/index.js';
5
5
  import type { ViewportInput } from '../../../types/IViewport.js';
6
6
  import type ViewportInputOptions from '../../../types/ViewportInputOptions.js';
@@ -24,11 +24,7 @@ export interface PlanarRegisteredDataSet {
24
24
  }
25
25
  export interface PlanarSetDataOptions {
26
26
  orientation?: PlanarOrientation;
27
- cpuThresholds?: {
28
- image?: number;
29
- volume?: number;
30
- };
31
- forceCPU?: boolean;
27
+ renderBackend?: RenderBackend | RenderBackendValue;
32
28
  role?: BindingRole;
33
29
  }
34
30
  export interface PlanarDataLoadOptions {
@@ -1,4 +1,4 @@
1
1
  import type { PlanarViewState, PlanarOrientation } from './PlanarViewportTypes.js';
2
- export { DEFAULT_PLANAR_CPU_IMAGE_THRESHOLD, DEFAULT_PLANAR_CPU_VOLUME_THRESHOLD, PlanarRenderPathDecisionService, defaultPlanarRenderPathDecisionService, getPlanarAcquisitionOrientation, selectPlanarRenderPath, shouldUseCPU, } from './PlanarRenderPathDecisionService.js';
2
+ export { PlanarRenderPathDecisionService, defaultPlanarRenderPathDecisionService, getPlanarAcquisitionOrientation, selectPlanarRenderPath, } from './PlanarRenderPathDecisionService.js';
3
3
  export type { PlanarRenderPathDecisionOptions, SelectedPlanarRenderPath, } from './PlanarRenderPathDecisionService.js';
4
4
  export declare function normalizePlanarOrientation(orientation: PlanarOrientation | undefined, acquisitionOrientation?: PlanarViewState['orientation']): PlanarViewState['orientation'];
@@ -1,6 +1,6 @@
1
1
  import { OrientationAxis } from '../../../enums/index.js';
2
2
  import { clonePlanarOrientation } from './planarLegacyCompatibility.js';
3
- export { DEFAULT_PLANAR_CPU_IMAGE_THRESHOLD, DEFAULT_PLANAR_CPU_VOLUME_THRESHOLD, PlanarRenderPathDecisionService, defaultPlanarRenderPathDecisionService, getPlanarAcquisitionOrientation, selectPlanarRenderPath, shouldUseCPU, } from './PlanarRenderPathDecisionService.js';
3
+ export { PlanarRenderPathDecisionService, defaultPlanarRenderPathDecisionService, getPlanarAcquisitionOrientation, selectPlanarRenderPath, } from './PlanarRenderPathDecisionService.js';
4
4
  export function normalizePlanarOrientation(orientation, acquisitionOrientation) {
5
5
  if (!orientation || orientation === OrientationAxis.ACQUISITION) {
6
6
  return OrientationAxis.ACQUISITION;
@@ -5,6 +5,7 @@ import triggerEvent from '../utilities/triggerEvent.js';
5
5
  import viewportTypeUsesCustomRenderingPipeline from './helpers/viewportTypeUsesCustomRenderingPipeline.js';
6
6
  import getOrCreateCanvas from './helpers/getOrCreateCanvas.js';
7
7
  import { vtkOffscreenMultiRenderWindow } from './vtkClasses/index.js';
8
+ import { attachWebGLContextEvents } from './helpers/attachWebGLContextEvents.js';
8
9
  import { getViewportClassForInput } from './helpers/viewportTypeToViewportClass.js';
9
10
  class TiledRenderingEngine extends BaseRenderingEngine {
10
11
  constructor(id) {
@@ -47,6 +48,7 @@ class TiledRenderingEngine extends BaseRenderingEngine {
47
48
  vtkOffscreenMultiRenderWindow.newInstance();
48
49
  this.offScreenCanvasContainer = document.createElement('div');
49
50
  this.offscreenMultiRenderWindow.setContainer(this.offScreenCanvasContainer);
51
+ attachWebGLContextEvents(this.offscreenMultiRenderWindow, this.id);
50
52
  }
51
53
  }
52
54
  enableVTKjsDrivenViewport(viewportInputEntry) {
@@ -5,7 +5,7 @@ declare class WebGLContextPool {
5
5
  private viewportToContext;
6
6
  private viewportSizes;
7
7
  private contextMaxSizes;
8
- constructor(count: number);
8
+ constructor(count: number, renderingEngineId?: string);
9
9
  getContextByIndex(index: number): {
10
10
  context: VtkOffscreenMultiRenderWindow;
11
11
  container: HTMLDivElement;
@@ -1,6 +1,7 @@
1
1
  import { vtkOffscreenMultiRenderWindow } from './vtkClasses/index.js';
2
+ import { attachWebGLContextEvents } from './helpers/attachWebGLContextEvents.js';
2
3
  class WebGLContextPool {
3
- constructor(count) {
4
+ constructor(count, renderingEngineId = '') {
4
5
  this.contexts = [];
5
6
  this.offScreenCanvasContainers = [];
6
7
  this.viewportToContext = new Map();
@@ -10,6 +11,7 @@ class WebGLContextPool {
10
11
  const offscreenMultiRenderWindow = vtkOffscreenMultiRenderWindow.newInstance();
11
12
  const container = document.createElement('div');
12
13
  offscreenMultiRenderWindow.setContainer(container);
14
+ attachWebGLContextEvents(offscreenMultiRenderWindow, renderingEngineId, i);
13
15
  this.contexts.push(offscreenMultiRenderWindow);
14
16
  this.offScreenCanvasContainers.push(container);
15
17
  }
@@ -0,0 +1,2 @@
1
+ import type { VtkOffscreenMultiRenderWindow } from '../../types/index.js';
2
+ export declare function attachWebGLContextEvents(offscreenMultiRenderWindow: VtkOffscreenMultiRenderWindow, renderingEngineId: string, contextIndex?: number): void;
@@ -0,0 +1,22 @@
1
+ import eventTarget from '../../eventTarget.js';
2
+ import { Events } from '../../enums/index.js';
3
+ import triggerEvent from '../../utilities/triggerEvent.js';
4
+ export function attachWebGLContextEvents(offscreenMultiRenderWindow, renderingEngineId, contextIndex = 0) {
5
+ const canvas = offscreenMultiRenderWindow.getOpenGLRenderWindow?.()?.getCanvas?.();
6
+ if (!canvas) {
7
+ return;
8
+ }
9
+ canvas.addEventListener('webglcontextlost', () => {
10
+ console.warn(`CornerstoneRender: WebGL context lost (renderingEngine=${renderingEngineId}, context=${contextIndex})`);
11
+ triggerEvent(eventTarget, Events.WEBGL_CONTEXT_LOST, {
12
+ renderingEngineId,
13
+ contextIndex,
14
+ });
15
+ });
16
+ canvas.addEventListener('webglcontextrestored', () => {
17
+ triggerEvent(eventTarget, Events.WEBGL_CONTEXT_RESTORED, {
18
+ renderingEngineId,
19
+ contextIndex,
20
+ });
21
+ });
22
+ }
@@ -43,6 +43,11 @@ declare enum Events {
43
43
  GEOMETRY_LOADED = "GEOMETRY_LOADED",
44
44
  GEOMETRY_LOAD_PROGRESS = "GEOMETRY_LOAD_PROGRESS",
45
45
  GEOMETRY_LOADED_FAILED = "GEOMETRY_LOADED_FAILED",
46
- ACTORS_CHANGED = "CORNERSTONE_ACTORS_CHANGED"
46
+ ACTORS_CHANGED = "CORNERSTONE_ACTORS_CHANGED",
47
+ RENDER_BACKEND_CHANGED = "CORNERSTONE_RENDER_BACKEND_CHANGED",
48
+ WEBGL_CONTEXT_LOST = "CORNERSTONE_WEBGL_CONTEXT_LOST",
49
+ WEBGL_CONTEXT_RESTORED = "CORNERSTONE_WEBGL_CONTEXT_RESTORED",
50
+ RENDER_PATH_ERROR = "CORNERSTONE_RENDER_PATH_ERROR",
51
+ RENDERING_PIPELINE_CHANGED = "CORNERSTONE_RENDERING_PIPELINE_CHANGED"
47
52
  }
48
53
  export default Events;
@@ -45,5 +45,10 @@ var Events;
45
45
  Events["GEOMETRY_LOAD_PROGRESS"] = "GEOMETRY_LOAD_PROGRESS";
46
46
  Events["GEOMETRY_LOADED_FAILED"] = "GEOMETRY_LOADED_FAILED";
47
47
  Events["ACTORS_CHANGED"] = "CORNERSTONE_ACTORS_CHANGED";
48
+ Events["RENDER_BACKEND_CHANGED"] = "CORNERSTONE_RENDER_BACKEND_CHANGED";
49
+ Events["WEBGL_CONTEXT_LOST"] = "CORNERSTONE_WEBGL_CONTEXT_LOST";
50
+ Events["WEBGL_CONTEXT_RESTORED"] = "CORNERSTONE_WEBGL_CONTEXT_RESTORED";
51
+ Events["RENDER_PATH_ERROR"] = "CORNERSTONE_RENDER_PATH_ERROR";
52
+ Events["RENDERING_PIPELINE_CHANGED"] = "CORNERSTONE_RENDERING_PIPELINE_CHANGED";
48
53
  })(Events || (Events = {}));
49
54
  export default Events;
@@ -0,0 +1,7 @@
1
+ declare enum RenderBackend {
2
+ Auto = "auto",
3
+ GPU = "gpu",
4
+ CPU = "cpu"
5
+ }
6
+ export type RenderBackendValue = `${RenderBackend}`;
7
+ export default RenderBackend;
@@ -0,0 +1,7 @@
1
+ var RenderBackend;
2
+ (function (RenderBackend) {
3
+ RenderBackend["Auto"] = "auto";
4
+ RenderBackend["GPU"] = "gpu";
5
+ RenderBackend["CPU"] = "cpu";
6
+ })(RenderBackend || (RenderBackend = {}));
7
+ export default RenderBackend;
@@ -18,4 +18,5 @@ import MetadataModules from './MetadataModules.js';
18
18
  import { GenerateImageType } from './GenerateImageType.js';
19
19
  import VoxelManagerEnum from './VoxelManagerEnum.js';
20
20
  import RenderingEngineModeEnum from './RenderingEngineModeEnum.js';
21
- export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, ViewportTypes, type ViewportTypesMap, OrientationAxis, GeometryType, ContourType, MeshType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, RenderingEngineModeEnum, };
21
+ import RenderBackend, { type RenderBackendValue } from './RenderBackend.js';
22
+ export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, ViewportTypes, type ViewportTypesMap, OrientationAxis, GeometryType, ContourType, MeshType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, RenderingEngineModeEnum, RenderBackend, type RenderBackendValue, };
@@ -18,4 +18,5 @@ import MetadataModules from './MetadataModules.js';
18
18
  import { GenerateImageType } from './GenerateImageType.js';
19
19
  import VoxelManagerEnum from './VoxelManagerEnum.js';
20
20
  import RenderingEngineModeEnum from './RenderingEngineModeEnum.js';
21
- export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, ViewportTypes, OrientationAxis, GeometryType, ContourType, MeshType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, RenderingEngineModeEnum, };
21
+ import RenderBackend, {} from './RenderBackend.js';
22
+ export { Events, BlendModes, CalibrationTypes, InterpolationType, RequestType, ViewportType, ViewportTypes, OrientationAxis, GeometryType, ContourType, MeshType, VOILUTFunctionType, DynamicOperatorType, ViewportStatus, VideoEnums, MetadataModules, ImageQualityStatus, VoxelManagerEnum, GenerateImageType, RenderingEngineModeEnum, RenderBackend, };
@@ -34,7 +34,8 @@ import imageRetrievalPoolManager from './requestPool/imageRetrievalPoolManager.j
34
34
  import imageLoadPoolManager from './requestPool/imageLoadPoolManager.js';
35
35
  import getEnabledElement, { getEnabledElementByIds, getEnabledElementByViewportId, getEnabledElements } from './getEnabledElement.js';
36
36
  import * as metaData from './metaData.js';
37
- import { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization } from './init.js';
37
+ import { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization } from './init.js';
38
+ import { getRenderingCapabilities, detectRenderingCapabilities } from './utilities/renderingCapabilities.js';
38
39
  import Settings from './Settings.js';
39
40
  import * as volumeLoader from './loaders/volumeLoader.js';
40
41
  import * as imageLoader from './loaders/imageLoader.js';
@@ -111,4 +112,4 @@ declare const renderingEngineExportsV2: {
111
112
  };
112
113
  declare const LegacyVolumeViewport3D: typeof VolumeViewport3D;
113
114
  export type { Types, IRetrieveConfiguration, RetrieveOptions, RetrieveStage, ImageLoadListener, IImagesLoader, };
114
- export { init, isCornerstoneInitialized, peerImport, resetInitialization, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, Enums, CONSTANTS, Events as EVENTS, Settings, BaseVolumeViewport, VolumeViewport, LegacyVolumeViewport3D, VolumeViewport3D, GenericVolumeViewport3D, Viewport, StackViewport, VideoViewport, WSIViewport, ECGViewport, ECGGenericViewport, GenericViewport, ViewportProjectionService, viewportProjection, viewportHasCanvasWorldTransform, viewportHasFrameOfReferenceUID, viewportHasPan, viewportHasZoom, RenderingEngine, BaseRenderingEngine, TiledRenderingEngine, ContextPoolRenderingEngine, ImageVolume, Surface, getRenderingEngine, getRenderingEngines, getEnabledElement, getEnabledElementByIds, getEnabledElements, getEnabledElementByViewportId, createVolumeActor, createVolumeMapper, cache, eventTarget, triggerEvent, convertColorArrayToRgbString, imageLoader, registerImageLoader, volumeLoader, metaData, utilities, setVolumesForViewports, addVolumesToViewports, addImageSlicesToViewports, DefaultRenderPathResolver, defaultRenderPathResolver, createDefaultECGRenderPaths, createECGRenderPathResolver, DefaultECGDataProvider, ecgProjection, VideoGenericViewport, createDefaultVideoRenderPaths, createVideoRenderPathResolver, DefaultVideoDataProvider, videoProjection, PlanarViewport, createDefaultPlanarRenderPaths, createPlanarRenderPathResolver, DefaultPlanarDataProvider, planarProjection, createDefaultVolume3DRenderPaths, createVolume3DRenderPathResolver, DefaultVolume3DDataProvider, volume3DProjection, WSIGenericViewport, createDefaultWSIRenderPaths, createWSIRenderPathResolver, DefaultWSIDataProvider, wsiProjection, renderingEngineExportsV2, getProjectionScaleMatrix, registerViewportType, isRegisteredViewportType, imageLoadPoolManager as requestPoolManager, imageRetrievalPoolManager, imageLoadPoolManager, getShouldUseCPURendering, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getUseGenericViewport, geometryLoader, cornerstoneMeshLoader, ProgressiveRetrieveImages, decimatedVolumeLoader, ActorRenderMode, cornerstoneStreamingImageVolumeLoader, cornerstoneStreamingDynamicImageVolumeLoader, StreamingDynamicImageVolume, StreamingImageVolume, convertMapperToNotSharedMapper, version, };
115
+ export { init, isCornerstoneInitialized, peerImport, resetInitialization, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, Enums, CONSTANTS, Events as EVENTS, Settings, BaseVolumeViewport, VolumeViewport, LegacyVolumeViewport3D, VolumeViewport3D, GenericVolumeViewport3D, Viewport, StackViewport, VideoViewport, WSIViewport, ECGViewport, ECGGenericViewport, GenericViewport, ViewportProjectionService, viewportProjection, viewportHasCanvasWorldTransform, viewportHasFrameOfReferenceUID, viewportHasPan, viewportHasZoom, RenderingEngine, BaseRenderingEngine, TiledRenderingEngine, ContextPoolRenderingEngine, ImageVolume, Surface, getRenderingEngine, getRenderingEngines, getEnabledElement, getEnabledElementByIds, getEnabledElements, getEnabledElementByViewportId, createVolumeActor, createVolumeMapper, cache, eventTarget, triggerEvent, convertColorArrayToRgbString, imageLoader, registerImageLoader, volumeLoader, metaData, utilities, setVolumesForViewports, addVolumesToViewports, addImageSlicesToViewports, DefaultRenderPathResolver, defaultRenderPathResolver, createDefaultECGRenderPaths, createECGRenderPathResolver, DefaultECGDataProvider, ecgProjection, VideoGenericViewport, createDefaultVideoRenderPaths, createVideoRenderPathResolver, DefaultVideoDataProvider, videoProjection, PlanarViewport, createDefaultPlanarRenderPaths, createPlanarRenderPathResolver, DefaultPlanarDataProvider, planarProjection, createDefaultVolume3DRenderPaths, createVolume3DRenderPathResolver, DefaultVolume3DDataProvider, volume3DProjection, WSIGenericViewport, createDefaultWSIRenderPaths, createWSIRenderPathResolver, DefaultWSIDataProvider, wsiProjection, renderingEngineExportsV2, getProjectionScaleMatrix, registerViewportType, isRegisteredViewportType, imageLoadPoolManager as requestPoolManager, imageRetrievalPoolManager, imageLoadPoolManager, getShouldUseCPURendering, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, getRenderingCapabilities, detectRenderingCapabilities, getUseGenericViewport, geometryLoader, cornerstoneMeshLoader, ProgressiveRetrieveImages, decimatedVolumeLoader, ActorRenderMode, cornerstoneStreamingImageVolumeLoader, cornerstoneStreamingDynamicImageVolumeLoader, StreamingDynamicImageVolume, StreamingImageVolume, convertMapperToNotSharedMapper, version, };
package/dist/esm/index.js CHANGED
@@ -28,7 +28,8 @@ import imageRetrievalPoolManager from './requestPool/imageRetrievalPoolManager.j
28
28
  import imageLoadPoolManager from './requestPool/imageLoadPoolManager.js';
29
29
  import getEnabledElement, { getEnabledElementByIds, getEnabledElementByViewportId, getEnabledElements, } from './getEnabledElement.js';
30
30
  import * as metaData from './metaData.js';
31
- import { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, } from './init.js';
31
+ import { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, } from './init.js';
32
+ import { getRenderingCapabilities, detectRenderingCapabilities, } from './utilities/renderingCapabilities.js';
32
33
  import Settings from './Settings.js';
33
34
  import * as volumeLoader from './loaders/volumeLoader.js';
34
35
  import * as imageLoader from './loaders/imageLoader.js';
@@ -77,4 +78,4 @@ const renderingEngineExportsV2 = {
77
78
  wsiProjection,
78
79
  };
79
80
  const LegacyVolumeViewport3D = VolumeViewport3D;
80
- export { init, isCornerstoneInitialized, peerImport, resetInitialization, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, Enums, CONSTANTS, Events as EVENTS, Settings, BaseVolumeViewport, VolumeViewport, LegacyVolumeViewport3D, VolumeViewport3D, GenericVolumeViewport3D, Viewport, StackViewport, VideoViewport, WSIViewport, ECGViewport, ECGGenericViewport, GenericViewport, ViewportProjectionService, viewportProjection, viewportHasCanvasWorldTransform, viewportHasFrameOfReferenceUID, viewportHasPan, viewportHasZoom, RenderingEngine, BaseRenderingEngine, TiledRenderingEngine, ContextPoolRenderingEngine, ImageVolume, Surface, getRenderingEngine, getRenderingEngines, getEnabledElement, getEnabledElementByIds, getEnabledElements, getEnabledElementByViewportId, createVolumeActor, createVolumeMapper, cache, eventTarget, triggerEvent, convertColorArrayToRgbString, imageLoader, registerImageLoader, volumeLoader, metaData, utilities, setVolumesForViewports, addVolumesToViewports, addImageSlicesToViewports, DefaultRenderPathResolver, defaultRenderPathResolver, createDefaultECGRenderPaths, createECGRenderPathResolver, DefaultECGDataProvider, ecgProjection, VideoGenericViewport, createDefaultVideoRenderPaths, createVideoRenderPathResolver, DefaultVideoDataProvider, videoProjection, PlanarViewport, createDefaultPlanarRenderPaths, createPlanarRenderPathResolver, DefaultPlanarDataProvider, planarProjection, createDefaultVolume3DRenderPaths, createVolume3DRenderPathResolver, DefaultVolume3DDataProvider, volume3DProjection, WSIGenericViewport, createDefaultWSIRenderPaths, createWSIRenderPathResolver, DefaultWSIDataProvider, wsiProjection, renderingEngineExportsV2, getProjectionScaleMatrix, registerViewportType, isRegisteredViewportType, imageLoadPoolManager as requestPoolManager, imageRetrievalPoolManager, imageLoadPoolManager, getShouldUseCPURendering, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getUseGenericViewport, geometryLoader, cornerstoneMeshLoader, ProgressiveRetrieveImages, decimatedVolumeLoader, ActorRenderMode, cornerstoneStreamingImageVolumeLoader, cornerstoneStreamingDynamicImageVolumeLoader, StreamingDynamicImageVolume, StreamingImageVolume, convertMapperToNotSharedMapper, version, };
81
+ export { init, isCornerstoneInitialized, peerImport, resetInitialization, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, Enums, CONSTANTS, Events as EVENTS, Settings, BaseVolumeViewport, VolumeViewport, LegacyVolumeViewport3D, VolumeViewport3D, GenericVolumeViewport3D, Viewport, StackViewport, VideoViewport, WSIViewport, ECGViewport, ECGGenericViewport, GenericViewport, ViewportProjectionService, viewportProjection, viewportHasCanvasWorldTransform, viewportHasFrameOfReferenceUID, viewportHasPan, viewportHasZoom, RenderingEngine, BaseRenderingEngine, TiledRenderingEngine, ContextPoolRenderingEngine, ImageVolume, Surface, getRenderingEngine, getRenderingEngines, getEnabledElement, getEnabledElementByIds, getEnabledElements, getEnabledElementByViewportId, createVolumeActor, createVolumeMapper, cache, eventTarget, triggerEvent, convertColorArrayToRgbString, imageLoader, registerImageLoader, volumeLoader, metaData, utilities, setVolumesForViewports, addVolumesToViewports, addImageSlicesToViewports, DefaultRenderPathResolver, defaultRenderPathResolver, createDefaultECGRenderPaths, createECGRenderPathResolver, DefaultECGDataProvider, ecgProjection, VideoGenericViewport, createDefaultVideoRenderPaths, createVideoRenderPathResolver, DefaultVideoDataProvider, videoProjection, PlanarViewport, createDefaultPlanarRenderPaths, createPlanarRenderPathResolver, DefaultPlanarDataProvider, planarProjection, createDefaultVolume3DRenderPaths, createVolume3DRenderPathResolver, DefaultVolume3DDataProvider, volume3DProjection, WSIGenericViewport, createDefaultWSIRenderPaths, createWSIRenderPathResolver, DefaultWSIDataProvider, wsiProjection, renderingEngineExportsV2, getProjectionScaleMatrix, registerViewportType, isRegisteredViewportType, imageLoadPoolManager as requestPoolManager, imageRetrievalPoolManager, imageLoadPoolManager, getShouldUseCPURendering, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, getRenderingCapabilities, detectRenderingCapabilities, getUseGenericViewport, geometryLoader, cornerstoneMeshLoader, ProgressiveRetrieveImages, decimatedVolumeLoader, ActorRenderMode, cornerstoneStreamingImageVolumeLoader, cornerstoneStreamingDynamicImageVolumeLoader, StreamingDynamicImageVolume, StreamingImageVolume, convertMapperToNotSharedMapper, version, };
@@ -1,7 +1,13 @@
1
1
  import type { Cornerstone3DConfig } from './types/index.js';
2
2
  import CentralizedWebWorkerManager from './webWorkerManager/webWorkerManager.js';
3
+ import { RenderBackend } from './enums/index.js';
4
+ import type { RenderBackendValue } from './enums/index.js';
3
5
  declare function init(configuration?: Cornerstone3DConfig): boolean;
4
6
  declare function getCanUseNorm16Texture(): boolean;
7
+ declare function getRenderBackend(): RenderBackend;
8
+ declare function resolveAutoRenderBackend(): RenderBackend.GPU | RenderBackend.CPU;
9
+ declare function getEffectiveRenderBackend(override?: RenderBackend | RenderBackendValue): RenderBackend.GPU | RenderBackend.CPU;
10
+ declare function setRenderBackend(backend: RenderBackend | RenderBackendValue, reason?: string): void;
5
11
  declare function setUseCPURendering(status: boolean, updateViewports?: boolean): void;
6
12
  declare function setPreferSizeOverAccuracy(status: boolean): void;
7
13
  declare function canRenderFloatTextures(): boolean;
@@ -14,4 +20,4 @@ declare function getConfiguration(): Cornerstone3DConfig;
14
20
  declare function setConfiguration(c: Cornerstone3DConfig): void;
15
21
  declare function getWebWorkerManager(): CentralizedWebWorkerManager;
16
22
  declare function peerImport(moduleId: string): Promise<any>;
17
- export { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, getCanUseNorm16Texture, };
23
+ export { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, resolveAutoRenderBackend, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, getCanUseNorm16Texture, };
package/dist/esm/init.js CHANGED
@@ -2,10 +2,11 @@ import { getRenderingEngines } from './RenderingEngine/getRenderingEngine.js';
2
2
  let csRenderInitialized = false;
3
3
  import deepMerge from './utilities/deepMerge.js';
4
4
  import CentralizedWebWorkerManager from './webWorkerManager/webWorkerManager.js';
5
- import { getSupportedTextureFormats } from './utilities/textureSupport.js';
6
- import { RenderingEngineModeEnum } from './enums/index.js';
5
+ import { getRenderingCapabilities } from './utilities/renderingCapabilities.js';
6
+ import triggerEvent from './utilities/triggerEvent.js';
7
+ import eventTarget from './eventTarget.js';
8
+ import { Events, RenderBackend, RenderingEngineModeEnum } from './enums/index.js';
7
9
  const defaultConfig = {
8
- gpuTier: { tier: 2 },
9
10
  isMobile: false,
10
11
  rendering: {
11
12
  useCPURendering: false,
@@ -15,10 +16,7 @@ const defaultConfig = {
15
16
  renderingEngineMode: RenderingEngineModeEnum.ContextPool,
16
17
  webGlContextCount: 7,
17
18
  planar: {
18
- cpuThresholds: {
19
- image: 64 * 1024 * 1024,
20
- volume: Infinity,
21
- },
19
+ renderBackend: RenderBackend.Auto,
22
20
  cpuVolume: {
23
21
  useViewportSamplingForLinear: true,
24
22
  volumeModifiedThrottleMs: 5000,
@@ -38,21 +36,9 @@ let config = {
38
36
  rendering: { ...defaultConfig.rendering },
39
37
  };
40
38
  let webWorkerManager = null;
41
- let canUseNorm16Texture = false;
42
- function _getGLContext() {
43
- const canvas = document.createElement('canvas');
44
- const gl = canvas.getContext('webgl2') ||
45
- canvas.getContext('webgl') ||
46
- canvas.getContext('experimental-webgl');
47
- return gl;
48
- }
49
- function _hasActiveWebGLContext() {
50
- const gl = _getGLContext();
51
- return (gl instanceof WebGLRenderingContext || gl instanceof WebGL2RenderingContext);
52
- }
53
39
  function _hasNorm16TextureSupport() {
54
- const supportedTextureFormats = getSupportedTextureFormats();
55
- return supportedTextureFormats.norm16 && supportedTextureFormats.norm16Linear;
40
+ const capabilities = getRenderingCapabilities();
41
+ return capabilities.norm16 && capabilities.norm16Linear;
56
42
  }
57
43
  function isIOS() {
58
44
  if (/iPad|iPhone|iPod/.test(navigator.platform)) {
@@ -68,7 +54,6 @@ function init(configuration = config) {
68
54
  if (csRenderInitialized) {
69
55
  return csRenderInitialized;
70
56
  }
71
- canUseNorm16Texture = _hasNorm16TextureSupport();
72
57
  config = deepMerge(defaultConfig, configuration);
73
58
  if (config.isMobile) {
74
59
  config.rendering.webGlContextCount = 1;
@@ -77,17 +62,20 @@ function init(configuration = config) {
77
62
  if (configuration.rendering?.preferSizeOverAccuracy) {
78
63
  config.rendering.preferSizeOverAccuracy = true;
79
64
  }
80
- else {
65
+ else if (!_hasNorm16TextureSupport()) {
81
66
  console.log('norm16 texture not supported, you can turn on the preferSizeOverAccuracy flag to use native data type, but be aware of the inaccuracy of the rendering in high bits');
82
67
  }
83
68
  }
84
- const hasWebGLContext = _hasActiveWebGLContext();
85
- if (!hasWebGLContext) {
69
+ const capabilities = getRenderingCapabilities();
70
+ if (!capabilities.webgl) {
86
71
  console.log('CornerstoneRender: GPU not detected, using CPU rendering');
87
72
  config.rendering.useCPURendering = true;
88
73
  }
89
74
  else {
90
75
  console.log('CornerstoneRender: using GPU rendering');
76
+ if (capabilities.softwareRasterizer) {
77
+ console.log(`CornerstoneRender: software rasterizer detected (${capabilities.renderer}), GPU rendering may be slow`);
78
+ }
91
79
  }
92
80
  csRenderInitialized = true;
93
81
  if (!webWorkerManager) {
@@ -96,14 +84,54 @@ function init(configuration = config) {
96
84
  return csRenderInitialized;
97
85
  }
98
86
  function getCanUseNorm16Texture() {
99
- return canUseNorm16Texture;
87
+ return _hasNorm16TextureSupport();
88
+ }
89
+ function getRenderBackend() {
90
+ return (config.rendering.planar?.renderBackend ??
91
+ RenderBackend.Auto);
92
+ }
93
+ function resolveAutoRenderBackend() {
94
+ return config.rendering.useCPURendering
95
+ ? RenderBackend.CPU
96
+ : RenderBackend.GPU;
97
+ }
98
+ function getEffectiveRenderBackend(override) {
99
+ const backend = (override ?? getRenderBackend());
100
+ if (backend === RenderBackend.GPU || backend === RenderBackend.CPU) {
101
+ return backend;
102
+ }
103
+ return resolveAutoRenderBackend();
104
+ }
105
+ function setRenderBackend(backend, reason) {
106
+ if (backend !== RenderBackend.Auto &&
107
+ backend !== RenderBackend.GPU &&
108
+ backend !== RenderBackend.CPU) {
109
+ throw new Error(`[setRenderBackend] Invalid render backend: ${String(backend)}`);
110
+ }
111
+ const previous = getRenderBackend();
112
+ if (previous === backend) {
113
+ return;
114
+ }
115
+ config.rendering.planar = {
116
+ ...config.rendering.planar,
117
+ renderBackend: backend,
118
+ };
119
+ _updateRenderingPipelinesForAllViewports();
120
+ triggerEvent(eventTarget, Events.RENDER_BACKEND_CHANGED, {
121
+ previous,
122
+ current: backend,
123
+ effectiveBackend: getEffectiveRenderBackend(),
124
+ reason,
125
+ });
100
126
  }
101
127
  function setUseCPURendering(status, updateViewports = true) {
128
+ const previousEffective = getEffectiveRenderBackend();
102
129
  config.rendering.useCPURendering = status;
103
130
  csRenderInitialized = true;
104
131
  if (updateViewports) {
105
132
  _updateRenderingPipelinesForAllViewports();
106
133
  }
134
+ _notifyEffectiveBackendChange(previousEffective, 'setUseCPURendering');
107
135
  }
108
136
  function setPreferSizeOverAccuracy(status) {
109
137
  config.rendering.preferSizeOverAccuracy = status;
@@ -111,14 +139,17 @@ function setPreferSizeOverAccuracy(status) {
111
139
  _updateRenderingPipelinesForAllViewports();
112
140
  }
113
141
  function canRenderFloatTextures() {
114
- if (!isIOS()) {
115
- return true;
142
+ const capabilities = getRenderingCapabilities();
143
+ if (capabilities.webgl2) {
144
+ return capabilities.floatLinear;
116
145
  }
117
- return false;
146
+ return !isIOS();
118
147
  }
119
148
  function resetUseCPURendering() {
120
- config.rendering.useCPURendering = !_hasActiveWebGLContext();
149
+ const previousEffective = getEffectiveRenderBackend();
150
+ config.rendering.useCPURendering = !getRenderingCapabilities().webgl;
121
151
  _updateRenderingPipelinesForAllViewports();
152
+ _notifyEffectiveBackendChange(previousEffective, 'resetUseCPURendering');
122
153
  }
123
154
  function getShouldUseCPURendering() {
124
155
  return config.rendering.useCPURendering;
@@ -142,10 +173,22 @@ function setConfiguration(c) {
142
173
  function _updateRenderingPipelinesForAllViewports() {
143
174
  getRenderingEngines().forEach((engine) => {
144
175
  engine.getViewports().forEach((viewport) => {
145
- viewport.updateRenderingPipeline();
176
+ viewport.updateRenderingPipeline?.();
146
177
  });
147
178
  });
148
179
  }
180
+ function _notifyEffectiveBackendChange(previousEffective, reason) {
181
+ const effectiveBackend = getEffectiveRenderBackend();
182
+ if (previousEffective === effectiveBackend) {
183
+ return;
184
+ }
185
+ triggerEvent(eventTarget, Events.RENDER_BACKEND_CHANGED, {
186
+ previous: previousEffective,
187
+ current: effectiveBackend,
188
+ effectiveBackend,
189
+ reason,
190
+ });
191
+ }
149
192
  function getWebWorkerManager() {
150
193
  if (!webWorkerManager) {
151
194
  webWorkerManager = new CentralizedWebWorkerManager();
@@ -155,4 +198,4 @@ function getWebWorkerManager() {
155
198
  async function peerImport(moduleId) {
156
199
  return config.peerImport(moduleId);
157
200
  }
158
- export { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, getCanUseNorm16Texture, };
201
+ export { init, getShouldUseCPURendering, getUseGenericViewport, isCornerstoneInitialized, setUseCPURendering, setPreferSizeOverAccuracy, resetUseCPURendering, getRenderBackend, setRenderBackend, getEffectiveRenderBackend, resolveAutoRenderBackend, getConfiguration, setConfiguration, getWebWorkerManager, canRenderFloatTextures, peerImport, resetInitialization, getCanUseNorm16Texture, };
@@ -1,8 +1,6 @@
1
1
  import type { RenderingEngineModeType } from '../types/index.js';
2
+ import type { RenderBackend, RenderBackendValue } from '../enums/index.js';
2
3
  interface Cornerstone3DConfig {
3
- gpuTier?: {
4
- tier?: number;
5
- };
6
4
  isMobile?: boolean;
7
5
  rendering?: {
8
6
  preferSizeOverAccuracy?: boolean;
@@ -12,10 +10,7 @@ interface Cornerstone3DConfig {
12
10
  renderingEngineMode?: RenderingEngineModeType;
13
11
  webGlContextCount?: number;
14
12
  planar?: {
15
- cpuThresholds?: {
16
- image?: number;
17
- volume?: number;
18
- };
13
+ renderBackend?: RenderBackend | RenderBackendValue;
19
14
  cpuVolume?: {
20
15
  useViewportSamplingForLinear?: boolean;
21
16
  volumeModifiedThrottleMs?: number;
@@ -0,0 +1,16 @@
1
+ import type { TextureFormatSupport } from './textureSupport.js';
2
+ export declare const RENDERING_CAPABILITIES_PROBE_VERSION = 1;
3
+ export interface RenderingCapabilities extends TextureFormatSupport {
4
+ webgl: boolean;
5
+ webgl2: boolean;
6
+ maxTextureSize: number;
7
+ renderer: string;
8
+ softwareRasterizer: boolean;
9
+ }
10
+ export declare function detectRenderingCapabilities({ useCache, }?: {
11
+ useCache?: boolean;
12
+ }): RenderingCapabilities;
13
+ export declare function getRenderingCapabilities(): RenderingCapabilities;
14
+ export declare function resetRenderingCapabilities({ clearStorage, }?: {
15
+ clearStorage?: boolean;
16
+ }): void;
@@ -0,0 +1,123 @@
1
+ import { getSupportedTextureFormats } from './textureSupport.js';
2
+ export const RENDERING_CAPABILITIES_PROBE_VERSION = 1;
3
+ const STORAGE_KEY = 'cornerstone3D.renderingCapabilities';
4
+ const SOFTWARE_RASTERIZER_PATTERN = /swiftshader|llvmpipe|softpipe|software|microsoft basic render/i;
5
+ const NO_GPU_FORMATS = {
6
+ norm16: false,
7
+ norm16Linear: false,
8
+ float: false,
9
+ floatLinear: false,
10
+ halfFloat: false,
11
+ halfFloatLinear: false,
12
+ };
13
+ let cachedCapabilities = null;
14
+ function getWebGLContextInfo() {
15
+ const info = {
16
+ webgl: false,
17
+ webgl2: false,
18
+ maxTextureSize: 0,
19
+ renderer: '',
20
+ };
21
+ if (typeof document === 'undefined') {
22
+ return info;
23
+ }
24
+ try {
25
+ const canvas = document.createElement('canvas');
26
+ const gl2 = canvas.getContext('webgl2');
27
+ const gl = gl2 ||
28
+ canvas.getContext('webgl') ||
29
+ canvas.getContext('experimental-webgl');
30
+ if (!gl) {
31
+ return info;
32
+ }
33
+ info.webgl = true;
34
+ info.webgl2 = !!gl2;
35
+ info.maxTextureSize = Number(gl.getParameter(gl.MAX_TEXTURE_SIZE)) || 0;
36
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
37
+ const renderer = debugInfo
38
+ ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
39
+ : gl.getParameter(gl.RENDERER);
40
+ info.renderer = typeof renderer === 'string' ? renderer : '';
41
+ const loseContext = gl.getExtension('WEBGL_lose_context');
42
+ if (loseContext) {
43
+ loseContext.loseContext();
44
+ }
45
+ }
46
+ catch {
47
+ }
48
+ return info;
49
+ }
50
+ function readCachedFormats(renderer, webgl2) {
51
+ try {
52
+ const raw = window.localStorage?.getItem(STORAGE_KEY);
53
+ if (!raw) {
54
+ return null;
55
+ }
56
+ const parsed = JSON.parse(raw);
57
+ if (parsed?.probeVersion !== RENDERING_CAPABILITIES_PROBE_VERSION ||
58
+ parsed?.renderer !== renderer ||
59
+ parsed?.webgl2 !== webgl2 ||
60
+ typeof parsed?.formats !== 'object' ||
61
+ parsed?.formats === null) {
62
+ return null;
63
+ }
64
+ return { ...NO_GPU_FORMATS, ...parsed.formats };
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ function writeCachedFormats(renderer, webgl2, formats) {
71
+ try {
72
+ const payload = {
73
+ probeVersion: RENDERING_CAPABILITIES_PROBE_VERSION,
74
+ renderer,
75
+ webgl2,
76
+ formats,
77
+ };
78
+ window.localStorage?.setItem(STORAGE_KEY, JSON.stringify(payload));
79
+ }
80
+ catch {
81
+ }
82
+ }
83
+ export function detectRenderingCapabilities({ useCache = true, } = {}) {
84
+ const contextInfo = getWebGLContextInfo();
85
+ if (!contextInfo.webgl) {
86
+ return {
87
+ ...contextInfo,
88
+ ...NO_GPU_FORMATS,
89
+ softwareRasterizer: false,
90
+ };
91
+ }
92
+ let formats = useCache
93
+ ? readCachedFormats(contextInfo.renderer, contextInfo.webgl2)
94
+ : null;
95
+ if (!formats) {
96
+ const probed = getSupportedTextureFormats();
97
+ if (probed) {
98
+ writeCachedFormats(contextInfo.renderer, contextInfo.webgl2, probed);
99
+ }
100
+ formats = probed ?? { ...NO_GPU_FORMATS };
101
+ }
102
+ return {
103
+ ...contextInfo,
104
+ ...formats,
105
+ softwareRasterizer: SOFTWARE_RASTERIZER_PATTERN.test(contextInfo.renderer),
106
+ };
107
+ }
108
+ export function getRenderingCapabilities() {
109
+ if (!cachedCapabilities) {
110
+ cachedCapabilities = detectRenderingCapabilities();
111
+ }
112
+ return cachedCapabilities;
113
+ }
114
+ export function resetRenderingCapabilities({ clearStorage = false, } = {}) {
115
+ cachedCapabilities = null;
116
+ if (clearStorage) {
117
+ try {
118
+ window.localStorage?.removeItem(STORAGE_KEY);
119
+ }
120
+ catch {
121
+ }
122
+ }
123
+ }
@@ -1,4 +1,9 @@
1
- export declare function getSupportedTextureFormats(): {
1
+ export interface TextureFormatSupport {
2
2
  norm16: boolean;
3
3
  norm16Linear: boolean;
4
- };
4
+ float: boolean;
5
+ floatLinear: boolean;
6
+ halfFloat: boolean;
7
+ halfFloatLinear: boolean;
8
+ }
9
+ export declare function getSupportedTextureFormats(): TextureFormatSupport | null;
@@ -2,14 +2,14 @@ const canvasSize = 4;
2
2
  const texWidth = 5;
3
3
  const texHeight = 1;
4
4
  const pixelToCheck = [1, 1];
5
- function main({ ext, filterType, texData, internalFormat, glDataType }) {
5
+ function createProbeContext() {
6
6
  try {
7
7
  const canvas = document.createElement('canvas');
8
8
  canvas.width = canvasSize;
9
9
  canvas.height = canvasSize;
10
10
  const gl = canvas.getContext('webgl2');
11
11
  if (!gl) {
12
- return false;
12
+ return null;
13
13
  }
14
14
  const vs = `#version 300 es
15
15
  void main() {
@@ -31,31 +31,40 @@ function main({ ext, filterType, texData, internalFormat, glDataType }) {
31
31
  color = vec4(vec3(intColor.rrr), 1);
32
32
  }
33
33
  `;
34
- let extToUse;
35
- if (ext) {
36
- extToUse = gl.getExtension(ext);
37
- if (!extToUse) {
38
- return false;
39
- }
40
- }
41
34
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
42
35
  gl.shaderSource(vertexShader, vs);
43
36
  gl.compileShader(vertexShader);
44
37
  if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
45
- return false;
38
+ return null;
46
39
  }
47
40
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
48
41
  gl.shaderSource(fragmentShader, fs);
49
42
  gl.compileShader(fragmentShader);
50
43
  if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
51
- return false;
44
+ return null;
52
45
  }
53
46
  const program = gl.createProgram();
54
47
  gl.attachShader(program, vertexShader);
55
48
  gl.attachShader(program, fragmentShader);
56
49
  gl.linkProgram(program);
57
50
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
58
- return false;
51
+ return null;
52
+ }
53
+ gl.useProgram(program);
54
+ return gl;
55
+ }
56
+ catch (e) {
57
+ return null;
58
+ }
59
+ }
60
+ function probeFormat(gl, { ext, filterType, texData, internalFormat, glDataType }) {
61
+ try {
62
+ let extToUse;
63
+ if (ext) {
64
+ extToUse = gl.getExtension(ext);
65
+ if (!extToUse) {
66
+ return false;
67
+ }
59
68
  }
60
69
  const tex = gl.createTexture();
61
70
  gl.bindTexture(gl.TEXTURE_2D, tex);
@@ -63,15 +72,13 @@ function main({ ext, filterType, texData, internalFormat, glDataType }) {
63
72
  const filter = filterType === 'LINEAR' ? gl.LINEAR : gl.NEAREST;
64
73
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
65
74
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
66
- gl.useProgram(program);
75
+ gl.clearColor(0, 0, 0, 0);
76
+ gl.clear(gl.COLOR_BUFFER_BIT);
67
77
  gl.drawArrays(gl.POINTS, 0, 1);
68
78
  const pixel = new Uint8Array(4);
69
79
  gl.readPixels(pixelToCheck[0], pixelToCheck[1], 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
70
80
  const [r, g, b] = pixel;
71
- const webglLoseContext = gl.getExtension('WEBGL_lose_context');
72
- if (webglLoseContext) {
73
- webglLoseContext.loseContext();
74
- }
81
+ gl.deleteTexture(tex);
75
82
  return r === g && g === b && r !== 0;
76
83
  }
77
84
  catch (e) {
@@ -79,23 +86,60 @@ function main({ ext, filterType, texData, internalFormat, glDataType }) {
79
86
  }
80
87
  }
81
88
  export function getSupportedTextureFormats() {
89
+ const gl = createProbeContext();
90
+ if (!gl) {
91
+ return null;
92
+ }
82
93
  const norm16TexData = new Int16Array([
83
94
  32767, 2000, 3000, 4000, 5000, 16784, 7000, 8000, 9000, 32767,
84
95
  ]);
85
- return {
86
- norm16: main({
96
+ const floatTexData = new Float32Array([0.3, 0.2, 0.3, 0.4, 0.5]);
97
+ const halfFloatTexData = new Uint16Array([13517, 12902, 13517, 13926, 14336]);
98
+ const result = {
99
+ norm16: probeFormat(gl, {
87
100
  ext: 'EXT_texture_norm16',
88
101
  filterType: 'NEAREST',
89
102
  texData: norm16TexData,
90
- internalFormat: (gl, ext) => ext.R16_SNORM_EXT,
91
- glDataType: (gl) => gl.SHORT,
103
+ internalFormat: (_gl, ext) => ext.R16_SNORM_EXT,
104
+ glDataType: (_gl) => _gl.SHORT,
92
105
  }),
93
- norm16Linear: main({
106
+ norm16Linear: probeFormat(gl, {
94
107
  ext: 'EXT_texture_norm16',
95
108
  filterType: 'LINEAR',
96
109
  texData: norm16TexData,
97
- internalFormat: (gl, ext) => ext.R16_SNORM_EXT,
98
- glDataType: (gl) => gl.SHORT,
110
+ internalFormat: (_gl, ext) => ext.R16_SNORM_EXT,
111
+ glDataType: (_gl) => _gl.SHORT,
112
+ }),
113
+ float: probeFormat(gl, {
114
+ filterType: 'NEAREST',
115
+ texData: floatTexData,
116
+ internalFormat: (_gl) => _gl.R32F,
117
+ glDataType: (_gl) => _gl.FLOAT,
118
+ }),
119
+ floatLinear: probeFormat(gl, {
120
+ ext: 'OES_texture_float_linear',
121
+ filterType: 'LINEAR',
122
+ texData: floatTexData,
123
+ internalFormat: (_gl) => _gl.R32F,
124
+ glDataType: (_gl) => _gl.FLOAT,
125
+ }),
126
+ halfFloat: probeFormat(gl, {
127
+ filterType: 'NEAREST',
128
+ texData: halfFloatTexData,
129
+ internalFormat: (_gl) => _gl.R16F,
130
+ glDataType: (_gl) => _gl.HALF_FLOAT,
131
+ }),
132
+ halfFloatLinear: probeFormat(gl, {
133
+ filterType: 'LINEAR',
134
+ texData: halfFloatTexData,
135
+ internalFormat: (_gl) => _gl.R16F,
136
+ glDataType: (_gl) => _gl.HALF_FLOAT,
99
137
  }),
100
138
  };
139
+ const contextLost = gl.isContextLost();
140
+ const webglLoseContext = gl.getExtension('WEBGL_lose_context');
141
+ if (webglLoseContext) {
142
+ webglLoseContext.loseContext();
143
+ }
144
+ return contextLost ? null : result;
101
145
  }
@@ -1 +1 @@
1
- export declare const version = "5.2.2";
1
+ export declare const version = "5.4.0";
@@ -1 +1 @@
1
- export const version = '5.2.2';
1
+ export const version = '5.4.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "5.2.2",
3
+ "version": "5.4.0",
4
4
  "description": "Cornerstone3D Core",
5
5
  "module": "./dist/esm/index.js",
6
6
  "types": "./dist/esm/index.d.ts",
@@ -85,18 +85,18 @@
85
85
  "prepublishOnly": "pnpm run build"
86
86
  },
87
87
  "dependencies": {
88
- "@cornerstonejs/utils": "5.2.2",
88
+ "@cornerstonejs/utils": "5.4.0",
89
89
  "@kitware/vtk.js": "35.5.3",
90
90
  "comlink": "4.4.2",
91
91
  "gl-matrix": "3.4.3",
92
92
  "loglevel": "1.9.2"
93
93
  },
94
94
  "devDependencies": {
95
- "@cornerstonejs/metadata": "5.2.2"
95
+ "@cornerstonejs/metadata": "5.4.0"
96
96
  },
97
97
  "peerDependencies": {
98
- "@cornerstonejs/metadata": "5.2.2",
99
- "@cornerstonejs/utils": "5.2.2"
98
+ "@cornerstonejs/metadata": "5.4.0",
99
+ "@cornerstonejs/utils": "5.4.0"
100
100
  },
101
101
  "contributors": [
102
102
  {
@@ -109,5 +109,5 @@
109
109
  "type": "individual",
110
110
  "url": "https://ohif.org/donate"
111
111
  },
112
- "gitHead": "16455de96d51253a436e44cfec9971d7bbc1b2e4"
112
+ "gitHead": "66acfc1d1cacf70845a9eb52a7b7a770a6b1fd3d"
113
113
  }