@@ -2,40 +2,73 @@ import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
22import type { TypedArray } from '@kitware/vtk.js/types' ;
33import { ThumbnailSlice } from '.' ;
44
5- function scalarImageToImageData (
6- values : TypedArray ,
7- width : number ,
8- height : number ,
9- scaleMin : number ,
10- scaleMax : number
11- ) {
5+ type ThumbnailPixels = {
6+ values : TypedArray ;
7+ width : number ;
8+ height : number ;
9+ components : number ;
10+ pixelIndex : ( x : number , y : number ) => number ;
11+ scaleMin : number ;
12+ scaleMax : number ;
13+ } ;
14+
15+ function colorChannelScale ( values : TypedArray ) {
16+ if ( values instanceof Uint8Array || values instanceof Uint8ClampedArray ) {
17+ return 1 ;
18+ }
19+ if ( values instanceof Uint16Array ) return 255 / 65535 ;
20+ return 255 ;
21+ }
22+
23+ function imageSliceToImageData ( {
24+ values,
25+ width,
26+ height,
27+ components,
28+ pixelIndex,
29+ scaleMin,
30+ scaleMax,
31+ } : ThumbnailPixels ) {
1232 const im = new ImageData ( width , height ) ;
13- const arr32 = new Uint32Array ( im . data . buffer ) ;
14- // scale to 1 unsigned byte
1533 const factor = 255 / ( scaleMax - scaleMin ) ;
16- for ( let i = 0 ; i < values . length ; i += 1 ) {
17- const byte = Math . floor ( ( values [ i ] - scaleMin ) * factor ) ;
18- // ABGR order
19-
20- arr32 [ i ] = ( 255 << 24 ) | ( byte << 16 ) | ( byte << 8 ) | byte ;
34+ const byteFactor = colorChannelScale ( values ) ;
35+ const toByte = ( value : number ) => {
36+ if ( components >= 3 ) return value * byteFactor ;
37+ if ( scaleMax === scaleMin ) return 0 ;
38+ return ( value - scaleMin ) * factor ;
39+ } ;
40+ for ( let y = 0 ; y < height ; y += 1 ) {
41+ for ( let x = 0 ; x < width ; x += 1 ) {
42+ const source = pixelIndex ( x , y ) * components ;
43+ const target = ( y * width + x ) * 4 ;
44+ const grayscale = components < 3 ;
45+ im . data [ target ] = toByte ( values [ source ] ) ;
46+ im . data [ target + 1 ] = grayscale
47+ ? im . data [ target ]
48+ : toByte ( values [ source + 1 ] ) ;
49+ im . data [ target + 2 ] = grayscale
50+ ? im . data [ target ]
51+ : toByte ( values [ source + 2 ] ) ;
52+ im . data [ target + 3 ] =
53+ components === 2 || components === 4
54+ ? values [ source + components - 1 ] * byteFactor
55+ : 255 ;
56+ }
2157 }
2258
2359 return im ;
2460}
2561
26- /**
27- * Generates a thumbnail given an image data.
28- *
29- * Assumption: image is comprised of single-component scalars
30- */
62+ /** Generates a thumbnail from one image plane. */
3163function generateThumbnail (
3264 imageData : vtkImageData ,
3365 axis : 0 | 1 | 2 = 2 ,
3466 whichSlice = ThumbnailSlice . Middle
3567) {
3668 const scalars = imageData . getPointData ( ) . getScalars ( ) ;
3769 const data = scalars . getData ( ) as TypedArray ;
38- const dataRange = scalars . getRange ( ) ;
70+ const components = scalars . getNumberOfComponents ( ) ;
71+ const [ scaleMin , scaleMax ] = scalars . getRange ( 0 ) ;
3972 const dims = imageData . getDimensions ( ) ;
4073
4174 // ThumbnailSlice.First
@@ -46,52 +79,30 @@ function generateThumbnail(
4679 slice = dims [ axis ] - 1 ;
4780 }
4881
49- let sliceData : TypedArray ;
5082 let width : number ;
5183 let height : number ;
84+ let pixelIndex : ( x : number , y : number ) => number ;
5285
5386 if ( axis === 0 ) {
54- // work-around for typing data.constructor.
55- // data is not necessarily of type Uint8Array.
56- sliceData = new ( < Uint8ArrayConstructor > data . constructor ) (
57- dims [ 1 ] * dims [ 2 ]
58- ) ;
5987 [ , width , height ] = dims ;
60- for ( let k = 0 ; k < dims [ 2 ] ; k ++ ) {
61- for ( let j = 0 ; j < dims [ 1 ] ; j ++ ) {
62- const index = slice + j * dims [ 0 ] + k * dims [ 0 ] * dims [ 1 ] ;
63- const offset = k * dims [ 1 ] + j ;
64- sliceData [ offset ] = data [ index ] ;
65- }
66- }
88+ pixelIndex = ( x , y ) => slice + x * dims [ 0 ] + y * dims [ 0 ] * dims [ 1 ] ;
6789 } else if ( axis === 1 ) {
68- sliceData = new ( < Uint8ArrayConstructor > data . constructor ) (
69- dims [ 0 ] * dims [ 2 ]
70- ) ;
7190 [ width , , height ] = dims ;
72- for ( let k = 0 ; k < dims [ 2 ] ; k ++ ) {
73- for ( let i = 0 ; i < dims [ 0 ] ; i ++ ) {
74- const index = i + slice * dims [ 0 ] + k * dims [ 0 ] * dims [ 1 ] ;
75- const offset = k * dims [ 0 ] + i ;
76- sliceData [ offset ] = data [ index ] ;
77- }
78- }
79- } else if ( axis === 2 ) {
91+ pixelIndex = ( x , y ) => x + slice * dims [ 0 ] + y * dims [ 0 ] * dims [ 1 ] ;
92+ } else {
8093 [ width , height ] = dims ;
81- const skip = dims [ 0 ] * dims [ 1 ] ;
82- const sliceOffset = slice * skip ;
83- sliceData = Array . isArray ( data )
84- ? data . slice ( sliceOffset , sliceOffset + skip )
85- : data . subarray ( sliceOffset , sliceOffset + skip ) ;
94+ pixelIndex = ( x , y ) => x + y * dims [ 0 ] + slice * dims [ 0 ] * dims [ 1 ] ;
8695 }
8796
88- return scalarImageToImageData (
89- sliceData ! ,
90- width ! ,
91- height ! ,
92- dataRange [ 0 ] ,
93- dataRange [ 1 ]
94- ) ;
97+ return imageSliceToImageData ( {
98+ values : data ,
99+ width,
100+ height,
101+ components,
102+ pixelIndex,
103+ scaleMin,
104+ scaleMax,
105+ } ) ;
95106}
96107
97108export function createVTKImageThumbnailer ( ) {
0 commit comments