WASM from PNG test

Author: Matthew Petroff
Date: 2021-02
License: CC0 1.0

WASM execution result

WASM output (should be 42):

Python 3 encoding code

import numpy as np
from PIL import Image

# From https://github.com/mdn/webassembly-examples/blob/936ab39d0b2f83295966941f7a916c4df65f0f4e/js-api-examples/simple.wasm
filename = 'simple.wasm'

with open(filename, 'rb') as infile:
    wasm = infile.read()

wasm_np = np.frombuffer(wasm, dtype=np.uint8)

# Encode byte count as first four bytes (32-bit, unsigned, little endian)
length = np.frombuffer(wasm_np.size.to_bytes(4, byteorder='little'), dtype=np.uint8)
wasm_np = np.append(length, wasm_np)

width = 2 ** int(np.ceil(np.sqrt(wasm_np.size))).bit_length()
height = int(np.ceil(wasm_np.size / width))
zero_padding = width * height - wasm_np.size

wasm_np = np.append(wasm_np, np.zeros(zero_padding, dtype=np.uint8))
wasm_np = wasm_np.reshape((height, width))
img = Image.fromarray(wasm_np)
img.save(filename.rsplit('.', 1)[0] + '.png')

# Run output PNG through PNG optimizer of your choosing, e.g., OptiPNG or OxiPNG
# Then convert to base64-encoded data URI

WASM execution code

let importObject = {
    imports: {
        imported_func: function(arg) {
            document.getElementById('output').textContent += arg;
        }
    }
};

function decode_wasm_png(src) {
    const img = new Image();
    const ctx = document.createElement('canvas').getContext('2d');
    img.src = src;
    return img.decode().then(() => {
        // Draw image to canvas
        ctx.width = img.width;
        ctx.height = img.height;
        ctx.drawImage(img, 0, 0);
        // Retrieve RGBA data
        let data = ctx.getImageData(0, 0, img.width, img.height).data;
        // Only return R channel (identical to G and B channels)
        data = data.filter((_, idx) => {return idx % 4 === 0});
        // Extract byte count from first 4 bytes (32-bit, unsigned, little endian)
        const length = data[0] + (data[1] << 8) + (data[1] << 16) + (data[1] << 24);
        // Return WASM binary
        return data.slice(4, length + 4).buffer;
    });
}

// simple.wasm encoded as PNG: https://github.com/mdn/webassembly-examples/blob/936ab39d0b2f83295966941f7a916c4df65f0f4e/js-api-examples/simple.wasm
const wasm = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAGCAAAAADvF0E7AAAAUklEQVQIW2PwYwCCxOJcRiDFyMGUwMBYz5DAwMAkyciemVuQX1TCUMwLYaSmxKeV5iUD1TEzMTKyCzLyplaAxRmgEoxcHIxsDI5aAgwM3AwoAAD7DxHMOX1EIQAAAABJRU5ErkJggg==';

decode_wasm_png(wasm).then(bytes =>
    WebAssembly.instantiate(bytes, importObject)
).then(result =>
    result.instance.exports.exported_func()
);