@cornerstonejs/core 1.75.2 → 1.76.1

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 (60) hide show
  1. package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.d.ts +1 -1
  2. package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js +2 -2
  3. package/dist/cjs/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -1
  4. package/dist/cjs/loaders/imageLoader.js +6 -1
  5. package/dist/cjs/loaders/imageLoader.js.map +1 -1
  6. package/dist/cjs/types/IStackViewport.d.ts +2 -49
  7. package/dist/cjs/types/IStackViewport.js +1 -0
  8. package/dist/cjs/types/IStackViewport.js.map +1 -1
  9. package/dist/cjs/utilities/getScalingParameters.js +3 -2
  10. package/dist/cjs/utilities/getScalingParameters.js.map +1 -1
  11. package/dist/cjs/utilities/loadImageToCanvas.d.ts +21 -5
  12. package/dist/cjs/utilities/loadImageToCanvas.js +32 -17
  13. package/dist/cjs/utilities/loadImageToCanvas.js.map +1 -1
  14. package/dist/cjs/utilities/renderToCanvasCPU.d.ts +3 -2
  15. package/dist/cjs/utilities/renderToCanvasCPU.js +7 -2
  16. package/dist/cjs/utilities/renderToCanvasCPU.js.map +1 -1
  17. package/dist/cjs/utilities/renderToCanvasGPU.d.ts +5 -2
  18. package/dist/cjs/utilities/renderToCanvasGPU.js +45 -12
  19. package/dist/cjs/utilities/renderToCanvasGPU.js.map +1 -1
  20. package/dist/cjs/utilities/sortImageIdsAndGetSpacing.js +7 -3
  21. package/dist/cjs/utilities/sortImageIdsAndGetSpacing.js.map +1 -1
  22. package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js +2 -2
  23. package/dist/esm/RenderingEngine/helpers/addImageSlicesToViewports.js.map +1 -1
  24. package/dist/esm/loaders/imageLoader.js +13 -1
  25. package/dist/esm/loaders/imageLoader.js.map +1 -1
  26. package/dist/esm/types/IStackViewport.js +1 -1
  27. package/dist/esm/types/IStackViewport.js.map +1 -1
  28. package/dist/esm/utilities/getScalingParameters.js +2 -2
  29. package/dist/esm/utilities/getScalingParameters.js.map +1 -1
  30. package/dist/esm/utilities/loadImageToCanvas.js +33 -17
  31. package/dist/esm/utilities/loadImageToCanvas.js.map +1 -1
  32. package/dist/esm/utilities/renderToCanvasCPU.js +7 -2
  33. package/dist/esm/utilities/renderToCanvasCPU.js.map +1 -1
  34. package/dist/esm/utilities/renderToCanvasGPU.js +45 -12
  35. package/dist/esm/utilities/renderToCanvasGPU.js.map +1 -1
  36. package/dist/esm/utilities/sortImageIdsAndGetSpacing.js +7 -3
  37. package/dist/esm/utilities/sortImageIdsAndGetSpacing.js.map +1 -1
  38. package/dist/types/RenderingEngine/helpers/addImageSlicesToViewports.d.ts +1 -1
  39. package/dist/types/RenderingEngine/helpers/addImageSlicesToViewports.d.ts.map +1 -1
  40. package/dist/types/loaders/imageLoader.d.ts.map +1 -1
  41. package/dist/types/types/IStackViewport.d.ts +2 -49
  42. package/dist/types/types/IStackViewport.d.ts.map +1 -1
  43. package/dist/types/utilities/loadImageToCanvas.d.ts +21 -5
  44. package/dist/types/utilities/loadImageToCanvas.d.ts.map +1 -1
  45. package/dist/types/utilities/renderToCanvasCPU.d.ts +3 -2
  46. package/dist/types/utilities/renderToCanvasCPU.d.ts.map +1 -1
  47. package/dist/types/utilities/renderToCanvasGPU.d.ts +5 -2
  48. package/dist/types/utilities/renderToCanvasGPU.d.ts.map +1 -1
  49. package/dist/types/utilities/sortImageIdsAndGetSpacing.d.ts.map +1 -1
  50. package/dist/umd/index.js +1 -1
  51. package/dist/umd/index.js.map +1 -1
  52. package/package.json +2 -2
  53. package/src/RenderingEngine/helpers/addImageSlicesToViewports.ts +2 -5
  54. package/src/loaders/imageLoader.ts +20 -8
  55. package/src/types/IStackViewport.ts +2 -178
  56. package/src/utilities/getScalingParameters.ts +2 -2
  57. package/src/utilities/loadImageToCanvas.ts +93 -32
  58. package/src/utilities/renderToCanvasCPU.ts +10 -3
  59. package/src/utilities/renderToCanvasGPU.ts +61 -24
  60. package/src/utilities/sortImageIdsAndGetSpacing.ts +12 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "1.75.2",
3
+ "version": "1.76.1",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/index.d.ts",
@@ -47,5 +47,5 @@
47
47
  "type": "individual",
48
48
  "url": "https://ohif.org/donate"
49
49
  },
50
- "gitHead": "f3239736baf99aec3e0744d0905b9cc1a43ae7cd"
50
+ "gitHead": "be9803cb19c0a08fb4b1765ed1e51244e57f2bfb"
51
51
  }
@@ -1,4 +1,3 @@
1
- import { StackViewport } from '..';
2
1
  import type {
3
2
  IStackViewport,
4
3
  IStackInput,
@@ -20,9 +19,7 @@ import type {
20
19
  async function addImageSlicesToViewports(
21
20
  renderingEngine: IRenderingEngine,
22
21
  stackInputs: Array<IStackInput>,
23
- viewportIds: Array<string>,
24
- immediateRender = false,
25
- suppressEvents = false
22
+ viewportIds: Array<string>
26
23
  ): Promise<void> {
27
24
  // Check if all viewports are volumeViewports
28
25
  for (const viewportId of viewportIds) {
@@ -45,7 +42,7 @@ async function addImageSlicesToViewports(
45
42
  const addStackPromises = viewportIds.map(async (viewportId) => {
46
43
  const viewport = renderingEngine.getViewport(viewportId) as IStackViewport;
47
44
 
48
- return viewport.addImages(stackInputs, immediateRender, suppressEvents);
45
+ return viewport.addImages(stackInputs);
49
46
  });
50
47
 
51
48
  await Promise.all(addStackPromises);
@@ -283,14 +283,26 @@ export function createAndCacheDerivedImage(
283
283
  );
284
284
  const derivedImageId = imageId;
285
285
 
286
- ['imagePixelModule', 'imagePlaneModule', 'generalSeriesModule'].forEach(
287
- (type) => {
288
- genericMetadataProvider.add(derivedImageId, {
289
- type,
290
- metadata: metaData.get(type, referencedImageId),
291
- });
292
- }
293
- );
286
+ ['imagePlaneModule', 'generalSeriesModule'].forEach((type) => {
287
+ genericMetadataProvider.add(derivedImageId, {
288
+ type,
289
+ metadata: metaData.get(type, referencedImageId),
290
+ });
291
+ });
292
+
293
+ const imagePixelModule = metaData.get('imagePixelModule', referencedImageId);
294
+ // TODO - add a general way to specify this
295
+ genericMetadataProvider.add(derivedImageId, {
296
+ type: 'imagePixelModule',
297
+ metadata: {
298
+ ...imagePixelModule,
299
+ bitsAllocated: 8,
300
+ bitsStored: 8,
301
+ highBit: 7,
302
+ samplesPerPixel: 1,
303
+ pixelRepresentation: 0,
304
+ },
305
+ });
294
306
 
295
307
  const localImage = createAndCacheLocalImage(
296
308
  { scalarData: imageScalarData, onCacheAdd, skipCreateBuffer },
@@ -1,179 +1,3 @@
1
- import CPUIImageData from './CPUIImageData';
2
- import ICamera from './ICamera';
3
- import IImageData from './IImageData';
4
- import { IViewport } from './IViewport';
5
- import Point2 from './Point2';
6
- import Point3 from './Point3';
7
- import { Scaling } from './ScalingParameters';
8
- import StackViewportProperties from './StackViewportProperties';
9
- import type IImage from './IImage';
10
- import { IStackInput } from './IStackInput';
11
- /**
12
- * Interface for Stack Viewport
13
- */
14
- export default interface IStackViewport extends IViewport {
15
- modality: string;
16
- /** Scaling parameters */
17
- scaling: Scaling;
1
+ import type IStackViewport from '../RenderingEngine/StackViewport';
18
2
 
19
- stackActorReInitialized: boolean;
20
- /**
21
- * Resizes the viewport - only used in CPU fallback for StackViewport. The
22
- * GPU resizing happens inside the RenderingEngine.
23
- */
24
- resize: () => void;
25
- /**
26
- * Returns the frame of reference UID, if the image doesn't have imagePlaneModule
27
- * metadata, it returns undefined, otherwise, frameOfReferenceUID is returned.
28
- */
29
- getFrameOfReferenceUID: () => string;
30
-
31
- /**
32
- * Update the default properties of the viewport and add properties by imageId if specified
33
- * setting the VOI, inverting the colors and setting the interpolation type, rotation
34
- */
35
- setDefaultProperties(
36
- ViewportProperties: StackViewportProperties,
37
- imageId?: string
38
- ): void;
39
-
40
- /**
41
- * Remove the global default properties of the viewport or remove default properties for an imageId if specified
42
- */
43
- clearDefaultProperties(imageId?: string): void;
44
- /**
45
- * Sets the properties for the viewport on the default actor. Properties include
46
- * setting the VOI, inverting the colors and setting the interpolation type, rotation
47
- */
48
- setProperties(
49
- {
50
- voiRange,
51
- invert,
52
- interpolationType,
53
- rotation,
54
- colormap,
55
- }: StackViewportProperties,
56
- suppressEvents?: boolean
57
- ): void;
58
- /**
59
- * Retrieve the viewport default properties
60
- */
61
- getDefaultProperties: (imageId?: string) => StackViewportProperties;
62
- /**
63
- * Retrieve the viewport properties
64
- */
65
- getProperties: () => StackViewportProperties;
66
- /**
67
- * canvasToWorld Returns the world coordinates of the given `canvasPos`
68
- * projected onto the plane defined by the `Viewport`'s camera.
69
- */
70
- canvasToWorld: (canvasPos: Point2) => Point3;
71
- /**
72
- * Returns the canvas coordinates of the given `worldPos`
73
- * projected onto the `Viewport`'s `canvas`.
74
- */
75
- worldToCanvas: (worldPos: Point3) => Point2;
76
- /**
77
- * Returns the index of the imageId being renderer
78
- */
79
- getCurrentImageIdIndex: () => number;
80
- /**
81
- * Returns the list of image Ids for the current viewport
82
- */
83
- getImageIds: () => string[];
84
- /**
85
- * Returns true if the viewport contains the imageId
86
- */
87
- hasImageId: (imageId: string) => boolean;
88
- /**
89
- * Returns true if the viewport contains the imageURI
90
- */
91
- hasImageURI: (imageURI: string) => boolean;
92
- /**
93
- * Returns the currently rendered imageId
94
- */
95
- getCurrentImageId: () => string;
96
- /**
97
- * Add Image Slices actors to the viewport
98
- */
99
- addImages(
100
- stackInputs: Array<IStackInput>,
101
- immediateRender: boolean,
102
- suppressEvents: boolean
103
- );
104
-
105
- getImageDataMetadata(image: IImage): any;
106
- /**
107
- * Custom rendering pipeline for the rendering for the CPU fallback
108
- */
109
- customRenderViewportToCanvas: () => {
110
- canvas: HTMLCanvasElement;
111
- element: HTMLDivElement;
112
- viewportId: string;
113
- renderingEngineId: string;
114
- };
115
- /**
116
- * Returns the image and its properties that is being shown inside the
117
- * stack viewport. It returns, the image dimensions, image direction,
118
- * image scalar data, vtkImageData object, metadata, and scaling (e.g., PET suvbw)
119
- */
120
- getImageData(): IImageData | CPUIImageData;
121
- /**
122
- * Returns the raw/loaded image being shown inside the stack viewport.
123
- */
124
- getCornerstoneImage: () => IImage;
125
- /**
126
- * Reset the viewport properties to the his default values if possible
127
- */
128
- resetToDefaultProperties(): void;
129
- /**
130
- * Reset the viewport properties to the default metadata values
131
- */
132
- resetProperties(): void;
133
- /**
134
- * If the user has selected CPU rendering, return the CPU camera, otherwise
135
- * return the default camera
136
- */
137
- getCamera(): ICamera;
138
- /**
139
- * Set the camera based on the provided camera object.
140
- */
141
- setCamera(cameraInterface: ICamera): void;
142
- /**
143
- * Sets the imageIds to be visualized inside the stack viewport. It accepts
144
- * list of imageIds, the index of the first imageId to be viewed. It is a
145
- * asynchronous function that returns a promise resolving to imageId being
146
- * displayed in the stack viewport.
147
- *
148
- * @param retrieveConfiguration - Set this to a progressive retriever of your
149
- * choice for progressive retrieval, or leave empty for non-progressive.
150
- */
151
- setStack(
152
- imageIds: Array<string>,
153
- currentImageIdIndex?: number
154
- ): Promise<string>;
155
- /**
156
- * Centers Pan and resets the zoom for stack viewport.
157
- */
158
- resetCamera(resetPan?: boolean, resetZoom?: boolean): boolean;
159
- /**
160
- * Loads the image based on the provided imageIdIndex. It is an Async function which
161
- * returns a promise that resolves to the imageId.
162
- */
163
- setImageIdIndex(imageIdIndex: number): Promise<string>;
164
- /**
165
- * Calibrates the image with new metadata that has been added for imageId. To calibrate
166
- * a viewport, you should add your calibration data manually to
167
- * calibratedPixelSpacingMetadataProvider and call viewport.calibrateSpacing
168
- * for it get applied.
169
- */
170
- calibrateSpacing(imageId: string): void;
171
- /**
172
- * If the renderer is CPU based, throw an error. Otherwise, returns the `vtkRenderer` responsible for rendering the `Viewport`.
173
- */
174
- getRenderer(): any;
175
- /**
176
- * It sets the colormap to the default colormap.
177
- */
178
- unsetColormap(): void;
179
- }
3
+ export default IStackViewport;
@@ -17,8 +17,8 @@ export default function getScalingParameters(
17
17
  const { modality } = generalSeriesModule;
18
18
 
19
19
  const scalingParameters = {
20
- rescaleSlope: modalityLutModule.rescaleSlope,
21
- rescaleIntercept: modalityLutModule.rescaleIntercept,
20
+ rescaleSlope: modalityLutModule.rescaleSlope || 1,
21
+ rescaleIntercept: modalityLutModule.rescaleIntercept ?? 0,
22
22
  modality,
23
23
  };
24
24
 
@@ -1,4 +1,11 @@
1
- import type { IImage, ViewportInputOptions } from '../types';
1
+ import type {
2
+ IImage,
3
+ ViewPresentation,
4
+ ViewReference,
5
+ ViewportInputOptions,
6
+ Point3,
7
+ IVolume,
8
+ } from '../types';
2
9
 
3
10
  import { loadAndCacheImage } from '../loaders/imageLoader';
4
11
  import * as metaData from '../metaData';
@@ -7,10 +14,52 @@ import imageLoadPoolManager from '../requestPool/imageLoadPoolManager';
7
14
  import renderToCanvasGPU from './renderToCanvasGPU';
8
15
  import renderToCanvasCPU from './renderToCanvasCPU';
9
16
  import { getConfiguration } from '../init';
17
+ import cache from '../cache';
10
18
 
11
- export interface LoadImageOptions {
12
- canvas: HTMLCanvasElement;
19
+ /**
20
+ * The original load image options specified just an image id, which is optimal
21
+ * for things like thumbnails rendering a single image.
22
+ */
23
+ export type StackLoadImageOptions = {
13
24
  imageId: string;
25
+ };
26
+
27
+ /**
28
+ * The full image load options allows specifying more parameters for both the
29
+ * presentation and the view so that a specific view can be referenced/displayed.
30
+ */
31
+ export type FullImageLoadOptions = {
32
+ viewReference: ViewReference;
33
+ viewPresentation: ViewPresentation;
34
+ imageId: undefined;
35
+ };
36
+
37
+ /**
38
+ * The canvas load position allows for determining the rendered position of
39
+ * image data within the canvas, and can be used to map loaded canvas points
40
+ * to and from other viewport positions for things like external computations
41
+ * on the load image to canvas view and the viewport view (which may contain
42
+ * extraneous data such as segmentation and thus not be usable for external
43
+ * computations.)
44
+ */
45
+ export type CanvasLoadPosition = {
46
+ origin: Point3;
47
+ topRight: Point3;
48
+ bottomLeft: Point3;
49
+ thicknessMm: number;
50
+ };
51
+
52
+ /**
53
+ * The image canvas can be loaded/set with various view conditions to specify the initial
54
+ * view as well as how and where ot render the image.
55
+ */
56
+ export type LoadImageOptions = {
57
+ canvas: HTMLCanvasElement;
58
+ // Define the view specification as optional here, and then incorporate specific
59
+ // requirements in mix in types.
60
+ imageId?: string;
61
+ viewReference?: ViewReference;
62
+ viewPresentation?: ViewPresentation;
14
63
  requestType?: RequestType;
15
64
  priority?: number;
16
65
  renderingEngineId?: string;
@@ -24,7 +73,7 @@ export interface LoadImageOptions {
24
73
  physicalPixels?: boolean;
25
74
  // Sets the viewport input options Defaults to scale to fit 110%
26
75
  viewportOptions?: ViewportInputOptions;
27
- }
76
+ } & (StackLoadImageOptions | FullImageLoadOptions);
28
77
 
29
78
  /**
30
79
  * Loads and renders an imageId to a Canvas. It will use the GPU rendering pipeline
@@ -46,55 +95,61 @@ export interface LoadImageOptions {
46
95
  * @param useCPURendering - Force the use of the CPU rendering pipeline (default to false)
47
96
  * @param thumbnail - Render a thumbnail image
48
97
  * @param imageAspect - assign the width based on the aspect ratio of the image
49
- * @param physicalPixels - set the width/height to the physical pixel size
50
98
  * @returns - A promise that resolves when the image has been rendered with the imageId
51
99
  */
52
100
  export default function loadImageToCanvas(
53
101
  options: LoadImageOptions
54
- ): Promise<string> {
102
+ ): Promise<CanvasLoadPosition> {
55
103
  const {
56
104
  canvas,
57
105
  imageId,
106
+ viewReference,
58
107
  requestType = RequestType.Thumbnail,
59
108
  priority = -5,
60
109
  renderingEngineId = '_thumbnails',
61
110
  useCPURendering = false,
62
111
  thumbnail = false,
63
112
  imageAspect = false,
64
- physicalPixels = false,
65
- viewportOptions,
113
+ viewportOptions: baseViewportOptions,
66
114
  } = options;
115
+ const volumeId = viewReference?.volumeId;
116
+ const isVolume = volumeId && !imageId;
117
+ const viewportOptions =
118
+ viewReference && baseViewportOptions
119
+ ? { ...baseViewportOptions, viewReference }
120
+ : baseViewportOptions;
67
121
 
68
- const devicePixelRatio = window.devicePixelRatio || 1;
69
122
  const renderFn = useCPURendering ? renderToCanvasCPU : renderToCanvasGPU;
70
123
 
71
124
  return new Promise((resolve, reject) => {
72
- function successCallback(image: IImage, imageId: string) {
125
+ function successCallback(imageOrVolume: IImage | IVolume, imageId: string) {
73
126
  const { modality } = metaData.get('generalSeriesModule', imageId) || {};
74
127
 
75
- image.isPreScaled = image.isPreScaled || image.preScale?.scaled;
128
+ const image = !isVolume && (imageOrVolume as IImage);
129
+ const volume = isVolume && (imageOrVolume as IVolume);
130
+ if (image) {
131
+ image.isPreScaled = image.isPreScaled || image.preScale?.scaled;
132
+ }
76
133
 
77
134
  if (thumbnail) {
78
135
  canvas.height = 256;
79
136
  canvas.width = 256;
80
137
  }
81
- if (physicalPixels) {
82
- canvas.width = canvas.offsetWidth * devicePixelRatio;
83
- canvas.height = canvas.offsetHeight * devicePixelRatio;
138
+ if (imageAspect && image) {
139
+ canvas.width = image && (canvas.height * image.width) / image.height;
84
140
  }
85
- if (imageAspect) {
86
- canvas.width = (canvas.height * image.width) / image.height;
141
+ canvas.style.width = `${canvas.width / devicePixelRatio}px`;
142
+ canvas.style.height = `${canvas.height / devicePixelRatio}px`;
143
+ if (volume && useCPURendering) {
144
+ reject(new Error('CPU rendering of volume not supported'));
87
145
  }
88
-
89
146
  renderFn(
90
147
  canvas,
91
- image,
148
+ imageOrVolume,
92
149
  modality,
93
150
  renderingEngineId,
94
151
  viewportOptions
95
- ).then(() => {
96
- resolve(imageId);
97
- });
152
+ ).then(resolve);
98
153
  }
99
154
 
100
155
  function errorCallback(error: Error, imageId: string) {
@@ -113,29 +168,35 @@ export default function loadImageToCanvas(
113
168
  );
114
169
  }
115
170
 
116
- const { useNorm16Texture, preferSizeOverAccuracy } =
117
- getConfiguration().rendering;
118
- const useNativeDataType = useNorm16Texture || preferSizeOverAccuracy;
171
+ const { useNorm16Texture } = getConfiguration().rendering;
119
172
 
120
173
  // IMPORTANT: Request type should be passed if not the 'interaction'
121
174
  // highest priority will be used for the request type in the imageRetrievalPool
122
175
  const options = {
123
176
  targetBuffer: {
124
- type: useNativeDataType ? undefined : 'Float32Array',
177
+ type: useNorm16Texture ? undefined : 'Float32Array',
125
178
  },
126
179
  preScale: {
127
180
  enabled: true,
128
181
  },
129
- useNativeDataType,
130
182
  useRGBA: !!useCPURendering,
131
183
  requestType,
132
184
  };
133
185
 
134
- imageLoadPoolManager.addRequest(
135
- sendRequest.bind(null, imageId, null, options),
136
- requestType,
137
- { imageId },
138
- priority
139
- );
186
+ if (volumeId) {
187
+ const volume = cache.getVolume(volumeId) as unknown as IVolume;
188
+ if (!volume) {
189
+ reject(new Error(`Volume id ${volumeId} not found in cache`));
190
+ }
191
+ const useImageId = volume.imageIds[0];
192
+ successCallback(volume, useImageId);
193
+ } else {
194
+ imageLoadPoolManager.addRequest(
195
+ sendRequest.bind(null, imageId, null, options),
196
+ requestType,
197
+ { imageId },
198
+ priority
199
+ );
200
+ }
140
201
  });
141
202
  }
@@ -2,11 +2,13 @@ import {
2
2
  IImage,
3
3
  CPUFallbackEnabledElement,
4
4
  ViewportInputOptions,
5
+ IVolume,
5
6
  } from '../types';
6
7
 
7
8
  import getDefaultViewport from '../RenderingEngine/helpers/cpuFallback/rendering/getDefaultViewport';
8
9
  import calculateTransform from '../RenderingEngine/helpers/cpuFallback/rendering/calculateTransform';
9
10
  import drawImageSync from '../RenderingEngine/helpers/cpuFallback/drawImageSync';
11
+ import type { CanvasLoadPosition } from './loadImageToCanvas';
10
12
 
11
13
  /**
12
14
  * Renders a cornerstone image object to a canvas.
@@ -17,11 +19,16 @@ import drawImageSync from '../RenderingEngine/helpers/cpuFallback/drawImageSync'
17
19
  */
18
20
  export default function renderToCanvasCPU(
19
21
  canvas: HTMLCanvasElement,
20
- image: IImage,
22
+ imageOrVolume: IImage | IVolume,
21
23
  modality?: string,
22
24
  _renderingEngineId?: string,
23
25
  _viewportOptions?: ViewportInputOptions
24
- ): Promise<string> {
26
+ ): Promise<CanvasLoadPosition> {
27
+ const volume = imageOrVolume as IVolume;
28
+ if (volume.volumeId) {
29
+ throw new Error('Unsupported volume rendering for CPU');
30
+ }
31
+ const image = imageOrVolume as IImage;
25
32
  const viewport = getDefaultViewport(canvas, image, modality);
26
33
 
27
34
  const enabledElement: CPUFallbackEnabledElement = {
@@ -36,6 +43,6 @@ export default function renderToCanvasCPU(
36
43
  const invalidated = true;
37
44
  return new Promise((resolve, reject) => {
38
45
  drawImageSync(enabledElement, invalidated);
39
- resolve(image.imageId);
46
+ resolve(null);
40
47
  });
41
48
  }
@@ -2,11 +2,18 @@ import getOrCreateCanvas, {
2
2
  EPSILON,
3
3
  } from '../RenderingEngine/helpers/getOrCreateCanvas';
4
4
  import { ViewportType, Events } from '../enums';
5
- import StackViewport from '../RenderingEngine/StackViewport';
6
- import { IImage, ViewportInputOptions } from '../types';
5
+ import {
6
+ IImage,
7
+ IStackViewport,
8
+ IVolume,
9
+ ViewportInputOptions,
10
+ IVolumeViewport,
11
+ ViewReference,
12
+ } from '../types';
7
13
  import { getRenderingEngine } from '../RenderingEngine/getRenderingEngine';
8
14
  import RenderingEngine from '../RenderingEngine';
9
15
  import isPTPrescaledWithSUV from './isPTPrescaledWithSUV';
16
+ import { CanvasLoadPosition } from './loadImageToCanvas';
10
17
 
11
18
  /**
12
19
  * Renders an cornerstone image to a Canvas. This method will handle creation
@@ -23,26 +30,33 @@ import isPTPrescaledWithSUV from './isPTPrescaledWithSUV';
23
30
  * renderToCanvasGPU(canvas, image)
24
31
  * ```
25
32
  * @param canvas - Canvas element to render to
26
- * @param image - The image to render
33
+ * @param imageOrVolume - The image to render
27
34
  * @param modality - [Default = undefined] The modality of the image
28
35
  * @returns - A promise that resolves when the image has been rendered with the imageId
29
36
  */
30
37
  export default function renderToCanvasGPU(
31
38
  canvas: HTMLCanvasElement,
32
- image: IImage,
39
+ imageOrVolume: IImage | IVolume,
33
40
  modality = undefined,
34
41
  renderingEngineId = '_thumbnails',
35
- viewportOptions: ViewportInputOptions = { displayArea: { imageArea: [1, 1] } }
36
- ): Promise<string> {
42
+ viewportOptions: ViewportInputOptions & { viewReference?: ViewReference } = {
43
+ displayArea: { imageArea: [1, 1] },
44
+ }
45
+ ): Promise<CanvasLoadPosition> {
37
46
  if (!canvas || !(canvas instanceof HTMLCanvasElement)) {
38
47
  throw new Error('canvas element is required');
39
48
  }
40
49
 
41
- const imageIdToPrint = image.imageId;
50
+ const isVolume = !(imageOrVolume as IImage).imageId;
51
+ const image = !isVolume && (imageOrVolume as IImage);
52
+ const volume = isVolume && (imageOrVolume as IVolume);
53
+ const imageIdToPrint = image?.imageId || volume?.volumeId;
42
54
  const viewportId = `renderGPUViewport-${imageIdToPrint}`;
43
- const imageId = image.imageId;
44
55
  const element = document.createElement('div');
45
56
  const devicePixelRatio = window.devicePixelRatio || 1;
57
+ if (!viewportOptions.displayArea) {
58
+ viewportOptions.displayArea = { imageArea: [1, 1] };
59
+ }
46
60
  const originalWidth = canvas.width;
47
61
  const originalHeight = canvas.height;
48
62
  // The canvas width/height are set by flooring the CSS size converted
@@ -50,17 +64,12 @@ export default function renderToCanvasGPU(
50
64
  // isn't exact, and using the exact value sometimes leads to an off by 1
51
65
  // in the actual size, so adding EPSILON to the size resolves
52
66
  // the problem.
53
- element.style.width = `${originalWidth + EPSILON}px`;
54
- element.style.height = `${originalHeight + EPSILON}px`;
67
+ // Don't touch the canvas size here as what we get out is a canvas at the right size
68
+ element.style.width = `${originalWidth / devicePixelRatio + EPSILON}px`;
69
+ element.style.height = `${originalHeight / devicePixelRatio + EPSILON}px`;
55
70
  element.style.visibility = 'hidden';
56
71
  element.style.position = 'absolute';
57
72
 
58
- // Up-sampling the provided canvas to match the device pixel ratio
59
- // since we use device pixel ratio to determine the size of the canvas
60
- // inside the rendering engine.
61
- canvas.width = originalWidth * devicePixelRatio;
62
- canvas.height = originalHeight * devicePixelRatio;
63
-
64
73
  document.body.appendChild(element);
65
74
 
66
75
  // add id to the element so we can find it later, and fix the : which is not allowed in css
@@ -73,20 +82,20 @@ export default function renderToCanvasGPU(
73
82
  (getRenderingEngine(renderingEngineId) as RenderingEngine) ||
74
83
  new RenderingEngine(renderingEngineId);
75
84
 
76
- let viewport = renderingEngine.getViewport(viewportId) as StackViewport;
85
+ let viewport = renderingEngine.getViewport(viewportId);
77
86
 
78
87
  if (!viewport) {
79
- const stackViewportInput = {
88
+ const viewportInput = {
80
89
  viewportId,
81
- type: ViewportType.STACK,
90
+ type: isVolume ? ViewportType.ORTHOGRAPHIC : ViewportType.STACK,
82
91
  element,
83
92
  defaultOptions: {
84
93
  ...viewportOptions,
85
94
  suppressEvents: true,
86
95
  },
87
96
  };
88
- renderingEngine.enableElement(stackViewportInput);
89
- viewport = renderingEngine.getViewport(viewportId) as StackViewport;
97
+ renderingEngine.enableElement(viewportInput);
98
+ viewport = renderingEngine.getViewport(viewportId);
90
99
  }
91
100
 
92
101
  return new Promise((resolve) => {
@@ -94,11 +103,20 @@ export default function renderToCanvasGPU(
94
103
  // enable it and later disable it without losing the canvas context
95
104
  let elementRendered = false;
96
105
 
106
+ let { viewReference } = viewportOptions;
107
+
97
108
  // Create a named function to handle the event
98
109
  const onImageRendered = (eventDetail) => {
99
110
  if (elementRendered) {
100
111
  return;
101
112
  }
113
+ if (viewReference) {
114
+ const useViewRef = viewReference;
115
+ viewReference = null;
116
+ viewport.setViewReference(useViewRef);
117
+ viewport.render();
118
+ return;
119
+ }
102
120
 
103
121
  // Copy the temporary canvas to the given canvas
104
122
  const context = canvas.getContext('2d');
@@ -114,6 +132,16 @@ export default function renderToCanvasGPU(
114
132
  canvas.height // destination dimensions
115
133
  );
116
134
 
135
+ const origin = viewport.canvasToWorld([0, 0]);
136
+ const topRight = viewport.canvasToWorld([
137
+ temporaryCanvas.width / devicePixelRatio,
138
+ 0,
139
+ ]);
140
+ const bottomLeft = viewport.canvasToWorld([
141
+ 0,
142
+ temporaryCanvas.height / devicePixelRatio,
143
+ ]);
144
+ const thicknessMm = 1;
117
145
  elementRendered = true;
118
146
 
119
147
  // remove based on id
@@ -135,17 +163,26 @@ export default function renderToCanvasGPU(
135
163
  element.remove();
136
164
  });
137
165
  }, 0);
138
- resolve(imageId);
166
+ resolve({
167
+ origin,
168
+ bottomLeft,
169
+ topRight,
170
+ thicknessMm,
171
+ });
139
172
  };
140
173
 
141
174
  element.addEventListener(Events.IMAGE_RENDERED, onImageRendered);
142
- viewport.renderImageObject(image);
175
+ if (isVolume) {
176
+ (viewport as IVolumeViewport).setVolumes([volume], false, true);
177
+ } else {
178
+ (viewport as IStackViewport).renderImageObject(imageOrVolume);
179
+ }
143
180
 
144
181
  // force a reset camera to center the image and undo the small scaling
145
182
  viewport.resetCamera();
146
183
 
147
184
  if (modality === 'PT' && !isPTPrescaledWithSUV(image)) {
148
- viewport.setProperties({
185
+ (viewport as IStackViewport).setProperties({
149
186
  voiRange: {
150
187
  lower: image.minPixelValue,
151
188
  upper: image.maxPixelValue,