Skip to content

Commit

Permalink
feat: WASM png support (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
william-silversmith authored Feb 8, 2022
1 parent adf81f8 commit 60c8c03
Show file tree
Hide file tree
Showing 13 changed files with 452 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,6 @@ jobs:
- uses: actions/checkout@v2
- run: ./src/neuroglancer/mesh/draco/build.sh
- run: ./src/neuroglancer/sliceview/compresso/build.sh
- run: ./src/neuroglancer/sliceview/png/build.sh
# Check that there are no differences.
- run: git diff --exit-code
1 change: 1 addition & 0 deletions config/bundle-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const DEFAULT_DATA_SOURCES = exports.DEFAULT_DATA_SOURCES = [
'neuroglancer/async_computation/decode_jpeg',
'neuroglancer/async_computation/decode_gzip',
'neuroglancer/async_computation/decode_compresso',
'neuroglancer/async_computation/decode_png',
],
},
{
Expand Down
33 changes: 33 additions & 0 deletions src/neuroglancer/async_computation/decode_png.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @license
* Copyright 2022 William Silversmith
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {decompressPng} from 'neuroglancer/sliceview/png';
import {decodePng} from 'neuroglancer/async_computation/decode_png_request';
import {registerAsyncComputation} from 'neuroglancer/async_computation/handler';

registerAsyncComputation(
decodePng,
async function(
data: Uint8Array, width: number, height: number,
numComponents: number, bytesPerPixel:number,
convertToGrayscale: boolean
) {
const result = await decompressPng(
data, width, height, numComponents,
bytesPerPixel, convertToGrayscale
);
return { value: result, transfer: [result.buffer] };
});
22 changes: 22 additions & 0 deletions src/neuroglancer/async_computation/decode_png_request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @license
* Copyright 2022 William Silversmith
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {asyncComputation} from 'neuroglancer/async_computation';

export const decodePng = asyncComputation<(
data: Uint8Array, width: number, height: number,
numComponents: number, bytesPerPixel:number,
convertToGrayscale: boolean
) => Uint8Array>('decodePng');
2 changes: 2 additions & 0 deletions src/neuroglancer/datasource/precomputed/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {ChunkDecoder} from 'neuroglancer/sliceview/backend_chunk_decoders';
import {decodeCompressedSegmentationChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/compressed_segmentation';
import {decodeCompressoChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/compresso';
import {decodeJpegChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/jpeg';
import {decodePngChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/png';
import {decodeRawChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/raw';
import {VolumeChunk, VolumeChunkSource} from 'neuroglancer/sliceview/volume/backend';
import {fetchSpecialHttpByteRange} from 'neuroglancer/util/byte_range_http_requests';
Expand Down Expand Up @@ -255,6 +256,7 @@ chunkDecoders.set(VolumeChunkEncoding.RAW, decodeRawChunk);
chunkDecoders.set(VolumeChunkEncoding.JPEG, decodeJpegChunk);
chunkDecoders.set(VolumeChunkEncoding.COMPRESSED_SEGMENTATION, decodeCompressedSegmentationChunk);
chunkDecoders.set(VolumeChunkEncoding.COMPRESSO, decodeCompressoChunk);
chunkDecoders.set(VolumeChunkEncoding.PNG, decodePngChunk);

@registerSharedObject() export class PrecomputedVolumeChunkSource extends
(WithParameters(WithSharedCredentialsProviderCounterpart<SpecialProtocolCredentials>()(VolumeChunkSource), VolumeChunkSourceParameters)) {
Expand Down
3 changes: 2 additions & 1 deletion src/neuroglancer/datasource/precomputed/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export enum VolumeChunkEncoding {
RAW,
JPEG,
COMPRESSED_SEGMENTATION,
COMPRESSO
COMPRESSO,
PNG
}

export class VolumeChunkSourceParameters {
Expand Down
41 changes: 41 additions & 0 deletions src/neuroglancer/sliceview/backend_chunk_decoders/png.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @license
* Copyright 2022 William Silvermsith.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {TypedArray} from 'neuroglancer/util/array';
import {DATA_TYPE_BYTES} from 'neuroglancer/util/data_type';
import {decodeRawChunk} from 'neuroglancer/sliceview/backend_chunk_decoders/raw';
import {VolumeChunk} from 'neuroglancer/sliceview/volume/backend';
import {CancellationToken} from 'neuroglancer/util/cancellation';
import {decodePng} from 'neuroglancer/async_computation/decode_png_request';
import {requestAsyncComputation} from 'neuroglancer/async_computation/request';

export async function decodePngChunk(
chunk: VolumeChunk, cancellationToken: CancellationToken, response: ArrayBuffer) {

const chunkDataSize = chunk.chunkDataSize!;
const dataType = chunk.source!.spec.dataType;
let image : TypedArray = await requestAsyncComputation(
decodePng, cancellationToken, [response],
/*buffer=*/(new Uint8Array(response)),
/*width=*/chunkDataSize[0],
/*height=*/chunkDataSize[1] * chunkDataSize[2],
/*numComponents=*/chunkDataSize[3] || 1,
/*bytesPerPixel=*/DATA_TYPE_BYTES[dataType],
/*convertToGrayscale=*/false
);

await decodeRawChunk(chunk, cancellationToken, image.buffer);
}
21 changes: 21 additions & 0 deletions src/neuroglancer/sliceview/png/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 2.0.24 (2021-06-10)
FROM emscripten/emsdk@sha256:81ec54b7a096d28f24d906955dbf98ff336cca47658d980c243baa36f6484f9f

ENV SPNG_0_7_1_SHA256 0726a4914ad7155028f3baa94027244d439cd2a2fbe8daf780c2150c4c951d8e
ENV MINIZ_2_2_0_SHA256 bd1136d0a1554520dcb527a239655777148d90fd2d51cf02c36540afc552e6ec

RUN mkdir -p /usr/src/spng \
&& curl -SL -o /usr/src/spng.tar.gz https://github.com/randy408/libspng/archive/refs/tags/v0.7.1.tar.gz

RUN echo "${SPNG_0_7_1_SHA256} /usr/src/spng.tar.gz" | sha256sum --check --status
RUN tar -xzC /usr/src/spng -f /usr/src/spng.tar.gz --strip-components=1 \
&& rm /usr/src/spng.tar.gz

RUN mkdir -p /usr/src/miniz \
&& curl -SL -o /usr/src/miniz.tar.gz https://github.com/richgel999/miniz/archive/refs/tags/2.2.0.tar.gz
RUN echo "${MINIZ_2_2_0_SHA256} /usr/src/miniz.tar.gz" | sha256sum --check --status
RUN tar -xzC /usr/src/miniz -f /usr/src/miniz.tar.gz --strip-components=1 \
&& rm /usr/src/miniz.tar.gz

RUN mkdir -p /usr/src/miniz/build && cd /usr/src/miniz/build && cmake ..
RUN cp /usr/src/miniz/build/miniz_export.h /usr/src/miniz/
13 changes: 13 additions & 0 deletions src/neuroglancer/sliceview/png/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash -xve

# This script builds `libpng.wasm` using emsdk in a docker container.

cd "$(dirname "$0")"

docker build .
docker run \
--rm \
-v ${PWD}:/src \
-u $(id -u):$(id -g) \
$(docker build -q .) \
./build_wasm.sh
30 changes: 30 additions & 0 deletions src/neuroglancer/sliceview/png/build_wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash -xve

SPNG=/usr/src/spng/spng
MINIZ=/usr/src/miniz

compile_options=(
-O2
-I$MINIZ -DMINIZ_NO_STDIO=1
$MINIZ/miniz_zip.c $MINIZ/miniz_tinfl.c $MINIZ/miniz_tdef.c $MINIZ/miniz.c
# spng defaults to zlib if we don't force miniz
# it also prints a warning about SIMD if we don't disable SIMD
# using the SPNG_DISABLE_OPT flag.
# As of this writng, WASM doesn't support SIMD by default anyway.
-I$SPNG -DSPNG_USE_MINIZ=1 -DSPNG_DISABLE_OPT=1 $SPNG/spng.c
png_wasm.c
-DNDEBUG
--no-entry
-s FILESYSTEM=0
-s ALLOW_MEMORY_GROWTH=1
-s TOTAL_STACK=32768
-s TOTAL_MEMORY=64kb
-s EXPORTED_FUNCTIONS='["_png_decompress","_malloc","_free"]'
-s MALLOC=emmalloc
-s ENVIRONMENT=worker
-s STANDALONE_WASM=1
# -s ASSERTIONS=1
# -s SAFE_HEAP=1
-o libpng.wasm
)
emcc ${compile_options[@]}
Loading

0 comments on commit 60c8c03

Please sign in to comment.