Skip to content

Commit a538ec7

Browse files
committed
feat(readers): implement readImageFile
readImageFile passes the file data as an ArrayBuffer to a web worker in the transfer list. Inside the web worker, the file's contents are mounted to the Emscripten filesystem with the WORKERFS as a Blob. The generated Emscripten modules are no longer UMD modules to permit proper loading in the web worker. BREAKING CHANGE: Emscripten modules no longer wrapped as UMD modules.
1 parent ddfc7cd commit a538ec7

5 files changed

Lines changed: 101 additions & 39 deletions

File tree

src/EmscriptenModule/itkJSImageIOPost.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ Module['unmountContainingDirectory'] = function (filePath) {
4040
FS.unmount(containingDir)
4141
}
4242

43-
// UMD module code
44-
return Module
45-
}))
43+
/** Mount file blobs into the Emscripten filesystem. The blobs argument should be an
44+
* array of { name: 'filename', data: blob } objects. */
45+
Module['mountBlobs'] = function (mountpoint, blobs) {
46+
if (! ENVIRONMENT_IS_WORKER) {
47+
return
48+
}
49+
50+
var currentDir = '/'
51+
var splitMountpoint = mountpoint.split('/')
52+
53+
for (var ii = 1; ii < splitMountpoint.length; ++ii) {
54+
currentDir += splitMountpoint[ii]
55+
if (!FS.analyzePath(currentDir).exists) {
56+
FS.mkdir(currentDir)
57+
}
58+
currentDir += '/'
59+
}
60+
61+
FS.mount(WORKERFS, {blobs: blobs}, mountpoint)
62+
}
63+
64+
Module['unmountBlobs'] = function (mountpoint) {
65+
if (! ENVIRONMENT_IS_WORKER) {
66+
return
67+
}
68+
69+
FS.unmount(mountpoint)
70+
}
Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1 @@
1-
// Export UMD wrapper function for different module loaders.
2-
(function(root, factory) {
3-
// AMD.
4-
if(typeof(define) == 'function' && define.amd) {
5-
define([], factory)
6-
}
7-
// Node.js style CommonJS.
8-
else if(typeof(module) == 'object' && module.exports) {
9-
module.exports = factory()
10-
}
11-
else {
12-
root.Module = factory()
13-
}
14-
15-
// Define wrapper function
16-
}(this, function() {
17-
var Module = {}
18-
//Module['print'] = console.log
19-
//Module['printErr'] = console.warn
20-
//Module['print'] = (typeof(console !== "undefined") ? (function(x) { console.log(x) }) : (function(x) {}))
21-
//Module['printErr'] = (typeof(console !== "undefined") ? (function(x) { console.warn(x) }) : (function(x) {}))
22-
23-
// Begin emscripten-generated asm.js code.
1+
var Module = {}

src/WebWorkers/ImageIOWorker.js

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,58 @@
11
const registerPromiseWorker = require('promise-worker-transferable/register')
22

3-
// const loadEmscriptenModule = require('./itkloadEmscriptenModule.js')
43
const ImageType = require('../itkImageType.js')
54
const Image = require('../itkImage.js')
65

6+
const mimeToIO = require('../itkMimeToIO.js')
7+
const getFileExtension = require('../itkgetFileExtension.js')
8+
const extensionToIO = require('../itkExtensionToIO.js')
9+
const readImageEmscriptenFSFile = require('../itkreadImageEmscriptenFSFile.js')
10+
11+
// todo: How to make this configurable?
12+
const config = require('../itkConfig.js')
13+
14+
// To cache loaded io modules
15+
let ioToModule = {}
16+
17+
/**
18+
* input is an object with keys:
19+
* name: fileNameString
20+
* type: mimeTypeString
21+
* buffer: fileContentsArrayBuffer
22+
**/
723
registerPromiseWorker(function (input, withTransferList) {
8-
console.log('received:')
9-
console.log(input)
10-
// const modulePath = path.join(config.imageIOsPath, 'itkPNGImageIOJSBinding.js')
11-
// const Module = loadEmscriptenModule(modulePath)
12-
// const image = readImageEmscriptenFSFile(Module, filePath)
13-
// resolve(image)
14-
let imageType = new ImageType(3)
15-
let image = new Image(imageType)
16-
return withTransferList(image, [image.buffer])
24+
const extension = getFileExtension(input.name)
25+
26+
let io = null
27+
if (mimeToIO.hasOwnProperty(input.type)) {
28+
io = mimeToIO[input.type]
29+
} else if (extensionToIO.hasOwnProperty(extension)) {
30+
io = extensionToIO[extension]
31+
} else {
32+
// todo: Iterate through available IO's and have them run
33+
// .CanReadFile(filePath)
34+
}
35+
if (io === null) {
36+
return Error('Could not find IO for: ' + input.name)
37+
}
38+
39+
let ioModule = null
40+
if (io in ioToModule) {
41+
ioModule = ioToModule[io]
42+
} else {
43+
const modulePath = config.imageIOsPath + '/' + io + '.js'
44+
importScripts(modulePath)
45+
ioToModule[io] = Module
46+
ioModule = Module
47+
}
48+
49+
const blob = new Blob([input.buffer])
50+
const blobs = [{ name: input.name, data: blob }]
51+
const mountpoint = '/work'
52+
ioModule.mountBlobs(mountpoint, blobs)
53+
const filePath = mountpoint + '/' + input.name
54+
const image = readImageEmscriptenFSFile(ioModule, filePath)
55+
ioModule.unmountBlobs(mountpoint)
56+
57+
return withTransferList(image, [image.buffer.buffer])
1758
})
18-
console.log('in the web worker')

src/itkreadImageLocalFile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ const readImageLocalFile = (filePath) => {
2525
// todo: Iterate through available IO's and have them run
2626
// .CanReadFile(filePath)
2727
}
28-
2928
if (io === null) {
3029
reject(Error('Could not find IO for: ' + filePath))
3130
}
31+
3232
const modulePath = path.join(config.imageIOsPath, io)
3333
const Module = loadEmscriptenModule(modulePath)
3434
Module.mountContainingDirectory(filePath)

test/Browser/itkreadImageFileTest.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ const test = require('tape')
22

33
const readImageFile = require('itkreadImageFile.js')
44

5+
const IntTypes = require('itkIntTypes.js')
6+
const PixelTypes = require('itkPixelTypes.js')
7+
58
const cthead1SmallBase64DataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfhBQYVKw8AZTNIAAADdklEQVQ4y2WTa2wUVRiGp6W7O3POnLmc2VrstokJlrBIUBJigjfSICVCCAo/QKM/FFNRIESJQKAws3M7M2f20t3GthRKQQq0kkoXMIq9oFwCXkg0UpMakGLgR9EmJF4TNOvZhRBb31+TvM955/vO+T6Ou69pAgSwKCCAEPc/lYUhFEUkMgH2ESmbYocEEUmKLIQqBKmEgUlERQhAPhyJiDMXPFZZDmRGoP8Q5TwC4ciMpatfXE9zmT2NVRVIQiLi76cDUVRDT/m72zLUc/Srv+gNCi8jhCrupvMAQIWf1zJx58pRj7g7h/sduunhiIIkUAJ4AUBZ0LZev3TondmeS42TuaYms6kOapJUalYQAAKxt+j4qD3yxvMZ0z47NLi/ydhWA7GMinWyAH6G1Wwe/OdUz6dz33T35dPdIxdIYrPGK0qxTnYrobVtjm+3pNvPxGu9/dTRgw8/e89et0AKF1uFItS2u7ZP7fr4K3H19VbP94me/T6fXRifM6+a/QKC6N5+PWGYZhVeNn9pzvUoTVnt3/QEz81dUTONgwjis4UzvS2Z5JbY9JlPdxmEuFZzX9va0yu5WlXmRAlWd3Tmjg980vXBprJZbYPtza0dXw40ZleeP1ZbrWKOXXpsu7Grb3gnsY/27B46+e3ElVuF3w+sm7Pki2VAUxkAo1t0a7TL8YnVPZxy6KG9fX/+2qu/+9DARoAVBiDYaHjnfc/3nHOdicA1Em6WpnOdG/I6zwCA5PCzrn6uw6VO99gBnRBKGUyIMfz3BgmrHHta8cEdu04dN6wjPwy6FinaTNT8emKNzGrgBEmJLLf7T6Tf/60wpFP2oKToB/bNr+pVTWHjghQxZuTzW51C4aIZENdj8gMv+1f3I7iYwPEqrFu+z1/zzI3vHN/ziEd9P0haV39aXxXFRaBMRrCu9Vjj5o/S5C4QBCnjws+pJ9SoqpZmRlqyeNWlPa922El22PMCl5if38q9FGV+CeAaFuK4OZY5nLRoksnsPX19nL5do2GsREoAlCtr68lo4VoXNROWdXD8j7GUNV96AMPye5MtYgU/ujF/887tHy+PXLt9o9/asUipvDfWpc1QNFWKPfla8PHI5Ysnsua2l2dH1Un7WS6rKlamxx9f/MKKhkX1syoxmLqcUMVRDTNMlZGkilPsUrOsJ6wxRSel/wuAkzbenLRf4gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0wNlQxNzoyNjozNC0wNDowMORO/MMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMDZUMTc6MjY6MzQtMDQ6MDCVE0R/AAAAAElFTkSuQmCC'
69
const byteString = window.atob(cthead1SmallBase64DataURI.split(',')[1])
710
const mimeString = cthead1SmallBase64DataURI.split(',')[0].split(':')[1].split(';')[0]
@@ -14,7 +17,23 @@ const cthead1SmallFile = new window.File([cthead1SmallBlob], 'cthead1Small.png')
1417

1518
test('readImageFile reads a File', t => {
1619
return readImageFile(cthead1SmallFile).then(function (image) {
17-
t.is(image.imageType.dimension, 3, 'dimension is 3')
20+
t.is(image.imageType.dimension, 2, 'dimension')
21+
t.is(image.imageType.componentType, IntTypes.UInt8, 'componentType')
22+
t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType')
23+
t.is(image.imageType.components, 1, 'components')
24+
t.is(image.origin[0], 0.0, 'origin[0]')
25+
t.is(image.origin[1], 0.0, 'origin[1]')
26+
t.is(image.spacing[0], 1.0, 'spacing[0]')
27+
t.is(image.spacing[1], 1.0, 'spacing[1]')
28+
t.is(image.direction, 'aoeu')
29+
t.is(image.direction.getElement(0, 0), 1.0, 'direction (0, 0)')
30+
t.is(image.direction.getElement(0, 1), 0.0, 'direction (0, 1)')
31+
t.is(image.direction.getElement(1, 0), 0.0, 'direction (1, 0)')
32+
t.is(image.direction.getElement(1, 1), 1.0, 'direction (1, 1)')
33+
t.is(image.size[0], 32, 'size[0]')
34+
t.is(image.size[1], 32, 'size[1]')
35+
t.is(image.buffer.length, 1024, 'buffer.length')
36+
t.is(image.buffer[512], 1024, 'buffer[512]')
1837
t.end()
1938
})
2039
})

0 commit comments

Comments
 (0)