Compare commits
6 Commits
live
...
assemblysc
Author | SHA1 | Date | |
---|---|---|---|
41f085438b | |||
fdbc0932e4 | |||
2412e925ba | |||
a676e1a2e9 | |||
a8265d5350 | |||
8f5c03e05b |
124
codecs/rotate/package-lock.json
generated
Normal file
124
codecs/rotate/package-lock.json
generated
Normal file
@ -0,0 +1,124 @@
|
||||
{
|
||||
"name": "rotate",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
|
||||
"dev": true
|
||||
},
|
||||
"assemblyscript": {
|
||||
"version": "github:AssemblyScript/assemblyscript#54b02c287c4d42092e4b01cc5d11bce1a53e9fb2",
|
||||
"from": "github:AssemblyScript/assemblyscript",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"binaryen": "63.0.0-nightly.20190107",
|
||||
"glob": "^7.1.3",
|
||||
"long": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"binaryen": {
|
||||
"version": "63.0.0-nightly.20190107",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-63.0.0-nightly.20190107.tgz",
|
||||
"integrity": "sha512-vck6ZESU9q6DeEK9v/Fk2O8d2jBsSrNlia8jpmdLZv5eqmNWpkNli+QMRa9Ezfb4wi3c+NVMuxT3Ck0g9GYe8A==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
9
codecs/rotate/package.json
Normal file
9
codecs/rotate/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "rotate",
|
||||
"scripts": {
|
||||
"build": "mv rotate.{as,ts} && asc rotate.ts -b rotate.wasm --validate -O3 --importMemory && mv rotate.{ts,as}"
|
||||
},
|
||||
"devDependencies": {
|
||||
"assemblyscript": "github:AssemblyScript/assemblyscript"
|
||||
}
|
||||
}
|
64
codecs/rotate/rotate.as
Normal file
64
codecs/rotate/rotate.as
Normal file
@ -0,0 +1,64 @@
|
||||
export function rotate(inputWidth: i32, inputHeight: i32, rotate: i32): void {
|
||||
const bpp = 4;
|
||||
let offset = inputWidth * inputHeight * bpp;
|
||||
let i = 0;
|
||||
|
||||
// In the straight-copy case, d1 is x, d2 is y.
|
||||
// x starts at 0 and increases.
|
||||
// y starts at 0 and increases.
|
||||
let d1Start = 0;
|
||||
let d1Limit = inputWidth;
|
||||
let d1Advance = 1;
|
||||
let d1Multiplier = 1;
|
||||
let d2Start = 0;
|
||||
let d2Limit = inputHeight;
|
||||
let d2Advance = 1;
|
||||
let d2Multiplier = inputWidth;
|
||||
|
||||
if (rotate === 90) {
|
||||
// d1 is y, d2 is x.
|
||||
// y starts at its max value and decreases.
|
||||
// x starts at 0 and increases.
|
||||
d1Start = inputHeight - 1;
|
||||
d1Limit = inputHeight;
|
||||
d1Advance = -1;
|
||||
d1Multiplier = inputWidth;
|
||||
d2Start = 0;
|
||||
d2Limit = inputWidth;
|
||||
d2Advance = 1;
|
||||
d2Multiplier = 1;
|
||||
} else if (rotate === 180) {
|
||||
// d1 is x, d2 is y.
|
||||
// x starts at its max and decreases.
|
||||
// y starts at its max and decreases.
|
||||
d1Start = inputWidth - 1;
|
||||
d1Limit = inputWidth;
|
||||
d1Advance = -1;
|
||||
d1Multiplier = 1;
|
||||
d2Start = inputHeight - 1;
|
||||
d2Limit = inputHeight;
|
||||
d2Advance = -1;
|
||||
d2Multiplier = inputWidth;
|
||||
} else if (rotate === 270) {
|
||||
// d1 is y, d2 is x.
|
||||
// y starts at 0 and increases.
|
||||
// x starts at its max and decreases.
|
||||
d1Start = 0;
|
||||
d1Limit = inputHeight;
|
||||
d1Advance = 1;
|
||||
d1Multiplier = inputWidth;
|
||||
d2Start = inputWidth - 1;
|
||||
d2Limit = inputWidth;
|
||||
d2Advance = -1;
|
||||
d2Multiplier = 1;
|
||||
}
|
||||
|
||||
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
|
||||
let d2offset = d2 * d2Multiplier;
|
||||
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
|
||||
let start = ((d1 * d1Multiplier) + d2offset);
|
||||
store<u32>(offset + i * 4, load<u32>(start * 4));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
BIN
codecs/rotate/rotate.wasm
Normal file
BIN
codecs/rotate/rotate.wasm
Normal file
Binary file not shown.
6
codecs/rotate/tsconfig.json
Normal file
6
codecs/rotate/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./node_modules/assemblyscript/std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
@ -3,3 +3,10 @@ export interface RotateOptions {
|
||||
}
|
||||
|
||||
export const defaultOptions: RotateOptions = { rotate: 0 };
|
||||
|
||||
export interface RotateModuleInstance {
|
||||
exports: {
|
||||
memory: WebAssembly.Memory;
|
||||
rotate(width: number, height: number, rotate: 0 | 90 | 180 | 270): void;
|
||||
};
|
||||
}
|
||||
|
@ -1,73 +1,30 @@
|
||||
import { RotateOptions } from './processor-meta';
|
||||
import wasmUrl from '../../../codecs/rotate/rotate.wasm';
|
||||
import { RotateOptions, RotateModuleInstance } from './processor-meta';
|
||||
|
||||
export function rotate(data: ImageData, opts: RotateOptions): ImageData {
|
||||
const { rotate } = opts;
|
||||
const flipDimensions = rotate % 180 !== 0;
|
||||
const { width: inputWidth, height: inputHeight } = data;
|
||||
const outputWidth = flipDimensions ? inputHeight : inputWidth;
|
||||
const outputHeight = flipDimensions ? inputWidth : inputHeight;
|
||||
const out = new ImageData(outputWidth, outputHeight);
|
||||
let i = 0;
|
||||
export async function rotate(
|
||||
data: ImageData,
|
||||
opts: RotateOptions,
|
||||
): Promise<ImageData> {
|
||||
const flipDimensions = opts.rotate % 180 !== 0;
|
||||
// Number of wasm memory pages (á 64KiB) needed to store the image twice.
|
||||
const bytesPerImage = data.width * data.height * 4;
|
||||
const numPagesNeeded = Math.ceil(bytesPerImage * 2 / (64 * 1024));
|
||||
const { instance } = (await (WebAssembly as any).instantiateStreaming(
|
||||
fetch(wasmUrl),
|
||||
{
|
||||
env: {
|
||||
memory: new WebAssembly.Memory({ initial: numPagesNeeded }),
|
||||
},
|
||||
},
|
||||
)) as { instance: RotateModuleInstance };
|
||||
|
||||
// In the straight-copy case, d1 is x, d2 is y.
|
||||
// x starts at 0 and increases.
|
||||
// y starts at 0 and increases.
|
||||
let d1Start = 0;
|
||||
let d1Limit = inputWidth;
|
||||
let d1Advance = 1;
|
||||
let d1Multiplier = 1;
|
||||
let d2Start = 0;
|
||||
let d2Limit = inputHeight;
|
||||
let d2Advance = 1;
|
||||
let d2Multiplier = inputWidth;
|
||||
const view = new Uint8ClampedArray(instance.exports.memory.buffer);
|
||||
view.set(data.data);
|
||||
|
||||
if (rotate === 90) {
|
||||
// d1 is y, d2 is x.
|
||||
// y starts at its max value and decreases.
|
||||
// x starts at 0 and increases.
|
||||
d1Start = inputHeight - 1;
|
||||
d1Limit = inputHeight;
|
||||
d1Advance = -1;
|
||||
d1Multiplier = inputWidth;
|
||||
d2Start = 0;
|
||||
d2Limit = inputWidth;
|
||||
d2Advance = 1;
|
||||
d2Multiplier = 1;
|
||||
} else if (rotate === 180) {
|
||||
// d1 is x, d2 is y.
|
||||
// x starts at its max and decreases.
|
||||
// y starts at its max and decreases.
|
||||
d1Start = inputWidth - 1;
|
||||
d1Limit = inputWidth;
|
||||
d1Advance = -1;
|
||||
d1Multiplier = 1;
|
||||
d2Start = inputHeight - 1;
|
||||
d2Limit = inputHeight;
|
||||
d2Advance = -1;
|
||||
d2Multiplier = inputWidth;
|
||||
} else if (rotate === 270) {
|
||||
// d1 is y, d2 is x.
|
||||
// y starts at 0 and increases.
|
||||
// x starts at its max and decreases.
|
||||
d1Start = 0;
|
||||
d1Limit = inputHeight;
|
||||
d1Advance = 1;
|
||||
d1Multiplier = inputWidth;
|
||||
d2Start = inputWidth - 1;
|
||||
d2Limit = inputWidth;
|
||||
d2Advance = -1;
|
||||
d2Multiplier = 1;
|
||||
}
|
||||
|
||||
const inB = new Uint32Array(data.data.buffer);
|
||||
const outB = new Uint32Array(out.data.buffer);
|
||||
for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
|
||||
for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
|
||||
const start = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
|
||||
outB[i] = inB[start];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
instance.exports.rotate(data.width, data.height, opts.rotate);
|
||||
return new ImageData(
|
||||
view.slice(bytesPerImage, bytesPerImage * 2),
|
||||
flipDimensions ? data.height : data.width,
|
||||
flipDimensions ? data.width : data.height,
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user