This repository is a demonstration how we can use AssemblyScript to manipulate an image in a browser.
This is an adaptation of Image Manipulation using WebAssembly and C project.
Clone this repository using Codespace.
$ git clone https://github.com/ebaskoro/image-manipulation-assemblyscript
Build the project:
$ npm run asbuild
Run the project:
$ npm start
Open a browser and browse to http://localhost:3000
Initiate an AssemblyScript project in the current directory:
$ npx asinit .
Create additional folder structure as below:
.
├── img
└── js
Edit index.ts file in the assembly folder. Create the first exportable function to adjust the RGBA channels of an image:
export function adjust(length: i32, rAdjustment: f32, gAdjustment: f32, bAdjustment: f32, aAdjustment: f32): void {
for (let i = 0; i < length; i += 4) {
const r = load<u8>(i);
let newR = <u8>(r * rAdjustment);
newR = (newR > 255) ? 255 : ((newR < 0) ? 0 : newR);
store<u8>(i, newR);
const g = load<u8>(i + 1);
let newG = <u8>(g * gAdjustment);
newG = (newG > 255) ? 255 : ((newG < 0) ? 0 : newG);
store<u8>(i + 1, newG);
const b = load<u8>(i + 2);
let newB = <u8>(b * bAdjustment);
newB = (newB > 255) ? 255 : ((newB < 0) ? 0 : newB);
store<u8>(i + 2, newB);
const a = load<u8>(i + 3);
let newA = <u8>(a * (1 - aAdjustment));
newA = (newA > 255) ? 255 : ((newA < 0) ? 0 : newA);
store<u8>(i + 3, newA);
}
}Here we will be using imported memory to load and store the image data passed from the JavaScript API.
Next we define the Sephia filter:
export function sephia(length: i32): void {
for (let i = 0; i < length; i += 4) {
const r = load<u8>(i);
const g = load<u8>(i + 1);
const b = load<u8>(i + 2);
const y = <u8>((r * 0.3) + (g * 0.59) + (b * 0.11));
store<u8>(i, y);
store<u8>(i + 1, y);
store<u8>(i + 2, y);
}
}Next we define the gray scale filter:
export function grayScale(length: i32): void {
for (let i = 0; i < length; i += 4) {
const r = load<u8>(i);
const g = load<u8>(i + 1);
const b = load<u8>(i + 2);
const a = load<u8>(i + 3);
store<u8>(i, r);
store<u8>(i + 1, r);
store<u8>(i + 2, r);
store<u8>(i + 3, a);
}
}Next we define the invert filter:
export function invert(length: i32): void {
for (let i = 0; i < length; i += 4) {
const r = load<u8>(i);
const g = load<u8>(i + 1);
const b = load<u8>(i + 2);
store<u8>(i, 255 - r);
store<u8>(i + 1, 255 - g);
store<u8>(i + 2, 255 - b);
}
}Next we define the noise filter:
export function noise(length: i32): void {
for (let i = 0; i < length; i += 4) {
const r = load<u8>(i);
const g = load<u8>(i + 1);
const b = load<u8>(i + 2);
const random = <u8>((Math.random() * 100 % 70) - 35);
store<u8>(i, r + random);
store<u8>(i + 1, g + random);
store<u8>(i + 2, b + random);
}
}Edit asconfig.json to add an option to the compiler to import memory:
{
"targets": {
"debug": {
"outFile": "build/debug.wasm",
"textFile": "build/debug.wat",
"sourceMap": true,
"debug": true
},
"release": {
"outFile": "build/release.wasm",
"textFile": "build/release.wat",
"sourceMap": true,
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
}
},
"options": {
"bindings": "esm",
"importMemory": true
}
}Build the project:
$ npm run asbuild
Copy image.jpg to the img folder.
Copy index.html and replace the existing file with this version.
Copy app.js to the js folder.