mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-13 09:17:20 +00:00
Compare commits
3 Commits
v1.3.4
...
windows-bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ace858bfe | ||
|
|
0388bde540 | ||
|
|
26fb713560 |
@@ -1,45 +0,0 @@
|
|||||||
// THIS IS NOT A NODE SCRIPT
|
|
||||||
// This is a d8 script. Please install jsvu[1] and install v8.
|
|
||||||
// Then run `npm run --silent benchmark`.
|
|
||||||
// [1]: https://github.com/GoogleChromeLabs/jsvu
|
|
||||||
async function init() {
|
|
||||||
// Adjustable constants.
|
|
||||||
const imageDimensions = 4096;
|
|
||||||
const iterations = new Array(100);
|
|
||||||
|
|
||||||
// Constants. Don’t change.
|
|
||||||
const imageByteSize = imageDimensions * imageDimensions * 4;
|
|
||||||
const wasmPageSize = 64 * 1024;
|
|
||||||
|
|
||||||
const buffer = readbuffer("rotate.wasm");
|
|
||||||
const { instance } = await WebAssembly.instantiate(buffer);
|
|
||||||
|
|
||||||
const pagesAvailable = Math.floor(
|
|
||||||
instance.exports.memory.buffer.byteLength / wasmPageSize
|
|
||||||
);
|
|
||||||
const pagesNeeded = Math.floor((imageByteSize * 2 + 4) / wasmPageSize) + 1;
|
|
||||||
const additionalPagesNeeded = pagesNeeded - pagesAvailable;
|
|
||||||
if (additionalPagesNeeded > 0) {
|
|
||||||
instance.exports.memory.grow(additionalPagesNeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
[0, 90, 180, 270].forEach(rotation => {
|
|
||||||
print(`\n${rotation} degrees`);
|
|
||||||
print(`==============================`);
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
const start = Date.now();
|
|
||||||
instance.exports.rotate(imageDimensions, imageDimensions, rotation);
|
|
||||||
iterations[i] = Date.now() - start;
|
|
||||||
}
|
|
||||||
const average = iterations.reduce((sum, c) => sum + c) / iterations.length;
|
|
||||||
const stddev = Math.sqrt(
|
|
||||||
iterations
|
|
||||||
.map(i => Math.pow(i - average, 2))
|
|
||||||
.reduce((sum, c) => sum + c) / iterations.length
|
|
||||||
);
|
|
||||||
print(`n = ${iterations.length}`);
|
|
||||||
print(`Average: ${average}`);
|
|
||||||
print(`StdDev: ${stddev}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
init().catch(e => console.error(e.stack));
|
|
||||||
@@ -2,10 +2,6 @@
|
|||||||
"name": "rotate",
|
"name": "rotate",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:image": "docker build -t squoosh-rotate .",
|
"build:image": "docker build -t squoosh-rotate .",
|
||||||
"build": "docker run --rm -v $(pwd):/src squoosh-rotate ./build.sh",
|
"build": "docker run --rm -v $(pwd):/src squoosh-rotate ./build.sh"
|
||||||
"benchmark": "echo File size after gzip && npm run benchmark:filesize && echo Optimizing && npm run -s benchmark:optimizing",
|
|
||||||
"benchmark:baseline": "v8 --liftoff --no-wasm-tier-up --no-opt ./benchmark.js",
|
|
||||||
"benchmark:optimizing": "v8 --no-liftoff --no-wasm-tier-up ./benchmark.js",
|
|
||||||
"benchmark:filesize": "cat rotate.wasm | gzip -c9n | wc -c"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +1,90 @@
|
|||||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
use std::slice::from_raw_parts_mut;
|
||||||
|
|
||||||
// This function is taken from Zachary Dremann
|
// This function is taken from
|
||||||
// https://github.com/GoogleChromeLabs/squoosh/pull/462
|
// https://rustwasm.github.io/book/reference/code-size.html
|
||||||
trait HardUnwrap<T> {
|
#[cfg(not(debug_assertions))]
|
||||||
fn unwrap_hard(self) -> T;
|
#[inline]
|
||||||
}
|
pub fn unwrap_abort<T>(o: Option<T>) -> T {
|
||||||
|
use std::process;
|
||||||
impl<T> HardUnwrap<T> for Option<T> {
|
match o {
|
||||||
#[cfg(not(debug_assertions))]
|
Some(t) => t,
|
||||||
#[inline]
|
None => process::abort(),
|
||||||
fn unwrap_hard(self) -> T {
|
|
||||||
match self {
|
|
||||||
Some(t) => t,
|
|
||||||
None => std::process::abort(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
fn unwrap_hard(self) -> T {
|
|
||||||
self.unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TILE_SIZE: usize = 16;
|
// Normal panic-y behavior for debug builds
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
fn get_buffers<'a>(width: usize, height: usize) -> (&'a [u32], &'a mut [u32]) {
|
unsafe fn unchecked_unwrap<T>(o: Option<T>) -> T {
|
||||||
let num_pixels = width * height;
|
o.unwrap()
|
||||||
let in_b: &[u32];
|
|
||||||
let out_b: &mut [u32];
|
|
||||||
unsafe {
|
|
||||||
in_b = from_raw_parts::<u32>(8 as *const u32, num_pixels);
|
|
||||||
out_b = from_raw_parts_mut::<u32>((num_pixels * 4 + 8) as *mut u32, num_pixels);
|
|
||||||
}
|
|
||||||
return (in_b, out_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
fn rotate_0(width: usize, height: usize) {
|
|
||||||
let (in_b, out_b) = get_buffers(width, height);
|
|
||||||
for (in_p, out_p) in in_b.iter().zip(out_b.iter_mut()) {
|
|
||||||
*out_p = *in_p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
fn rotate_90(width: usize, height: usize) {
|
|
||||||
let (in_b, out_b) = get_buffers(width, height);
|
|
||||||
let new_width = height;
|
|
||||||
let _new_height = width;
|
|
||||||
for y_start in (0..height).step_by(TILE_SIZE) {
|
|
||||||
for x_start in (0..width).step_by(TILE_SIZE) {
|
|
||||||
for y in y_start..(y_start + TILE_SIZE).min(height) {
|
|
||||||
let in_offset = y * width;
|
|
||||||
let in_bounds = if x_start + TILE_SIZE < width {
|
|
||||||
(in_offset + x_start)..(in_offset + x_start + TILE_SIZE)
|
|
||||||
} else {
|
|
||||||
(in_offset + x_start)..(in_offset + width)
|
|
||||||
};
|
|
||||||
let in_chunk = in_b.get(in_bounds).unwrap_hard();
|
|
||||||
for (x, in_p) in in_chunk.iter().enumerate() {
|
|
||||||
let new_x = (new_width - 1) - y;
|
|
||||||
let new_y = x + x_start;
|
|
||||||
*out_b.get_mut(new_y * new_width + new_x).unwrap_hard() = *in_p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
fn rotate_180(width: usize, height: usize) {
|
|
||||||
let (in_b, out_b) = get_buffers(width, height);
|
|
||||||
for (in_p, out_p) in in_b.iter().zip(out_b.iter_mut().rev()) {
|
|
||||||
*out_p = *in_p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
fn rotate_270(width: usize, height: usize) {
|
|
||||||
let (in_b, out_b) = get_buffers(width, height);
|
|
||||||
let new_width = height;
|
|
||||||
let new_height = width;
|
|
||||||
for y_start in (0..height).step_by(TILE_SIZE) {
|
|
||||||
for x_start in (0..width).step_by(TILE_SIZE) {
|
|
||||||
for y in y_start..(y_start + TILE_SIZE).min(height) {
|
|
||||||
let in_offset = y * width;
|
|
||||||
let in_bounds = if x_start + TILE_SIZE < width {
|
|
||||||
(in_offset + x_start)..(in_offset + x_start + TILE_SIZE)
|
|
||||||
} else {
|
|
||||||
(in_offset + x_start)..(in_offset + width)
|
|
||||||
};
|
|
||||||
let in_chunk = in_b.get(in_bounds).unwrap_hard();
|
|
||||||
for (x, in_p) in in_chunk.iter().enumerate() {
|
|
||||||
let new_x = y;
|
|
||||||
let new_y = new_height - 1 - (x_start + x);
|
|
||||||
*out_b.get_mut(new_y * new_width + new_x).unwrap_hard() = *in_p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn rotate(width: usize, height: usize, rotate: usize) {
|
fn rotate(input_width: isize, input_height: isize, rotate: isize) {
|
||||||
match rotate {
|
let mut i = 0isize;
|
||||||
0 => rotate_0(width, height),
|
|
||||||
90 => rotate_90(width, height),
|
// In the straight-copy case, d1 is x, d2 is y.
|
||||||
180 => rotate_180(width, height),
|
// x starts at 0 and increases.
|
||||||
270 => rotate_270(width, height),
|
// y starts at 0 and increases.
|
||||||
_ => std::process::abort(),
|
let mut d1_start: isize = 0;
|
||||||
|
let mut d1_limit: isize = input_width;
|
||||||
|
let mut d1_advance: isize = 1;
|
||||||
|
let mut d1_multiplier: isize = 1;
|
||||||
|
let mut d2_start: isize = 0;
|
||||||
|
let mut d2_limit: isize = input_height;
|
||||||
|
let mut d2_advance: isize = 1;
|
||||||
|
let mut d2_multiplier: isize = input_width;
|
||||||
|
|
||||||
|
if rotate == 90 {
|
||||||
|
// d1 is y, d2 is x.
|
||||||
|
// y starts at its max value and decreases.
|
||||||
|
// x starts at 0 and increases.
|
||||||
|
d1_start = input_height - 1;
|
||||||
|
d1_limit = input_height;
|
||||||
|
d1_advance = -1;
|
||||||
|
d1_multiplier = input_width;
|
||||||
|
d2_start = 0;
|
||||||
|
d2_limit = input_width;
|
||||||
|
d2_advance = 1;
|
||||||
|
d2_multiplier = 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.
|
||||||
|
d1_start = input_width - 1;
|
||||||
|
d1_limit = input_width;
|
||||||
|
d1_advance = -1;
|
||||||
|
d1_multiplier = 1;
|
||||||
|
d2_start = input_height - 1;
|
||||||
|
d2_limit = input_height;
|
||||||
|
d2_advance = -1;
|
||||||
|
d2_multiplier = input_width;
|
||||||
|
} else if rotate == 270 {
|
||||||
|
// d1 is y, d2 is x.
|
||||||
|
// y starts at 0 and increases.
|
||||||
|
// x starts at its max and decreases.
|
||||||
|
d1_start = 0;
|
||||||
|
d1_limit = input_height;
|
||||||
|
d1_advance = 1;
|
||||||
|
d1_multiplier = input_width;
|
||||||
|
d2_start = input_width - 1;
|
||||||
|
d2_limit = input_width;
|
||||||
|
d2_advance = -1;
|
||||||
|
d2_multiplier = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_pixels = (input_width * input_height) as usize;
|
||||||
|
let in_b: &mut [u32];
|
||||||
|
let out_b: &mut [u32];
|
||||||
|
unsafe {
|
||||||
|
in_b = from_raw_parts_mut::<u32>(4 as *mut u32, num_pixels);
|
||||||
|
out_b = from_raw_parts_mut::<u32>((input_width * input_height * 4 + 4) as *mut u32, num_pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
for d2 in 0..d2_limit {
|
||||||
|
for d1 in 0..d1_limit {
|
||||||
|
let in_idx = (d1_start + d1 * d1_advance) * d1_multiplier + (d2_start + d2 * d2_advance) * d2_multiplier;
|
||||||
|
*unwrap_abort(out_b.get_mut(i as usize)) = *unwrap_abort(in_b.get(in_idx as usize));
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -22,7 +22,6 @@ function fetchTravisBuildInfo(user, repo, branch) {
|
|||||||
'branch.name': branch,
|
'branch.name': branch,
|
||||||
state: 'passed',
|
state: 'passed',
|
||||||
limit: 1,
|
limit: 1,
|
||||||
event_type: 'push',
|
|
||||||
}).then(r => r.json());
|
}).then(r => r.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +32,9 @@ function fetchTravisText(path) {
|
|||||||
/**
|
/**
|
||||||
* Recursively-read a directory and turn it into an array of { name, size, gzipSize }
|
* Recursively-read a directory and turn it into an array of { name, size, gzipSize }
|
||||||
*/
|
*/
|
||||||
async function dirToInfoArray(startPath) {
|
async function dirToInfoArray(startPath, {
|
||||||
|
namePrefix = '',
|
||||||
|
} = {}) {
|
||||||
const results = await new Promise((resolve, reject) => {
|
const results = await new Promise((resolve, reject) => {
|
||||||
readdirp({ root: startPath }, (err, results) => {
|
readdirp({ root: startPath }, (err, results) => {
|
||||||
if (err) reject(err); else resolve(results);
|
if (err) reject(err); else resolve(results);
|
||||||
|
|||||||
1019
package-lock.json
generated
1019
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "squoosh",
|
"name": "squoosh",
|
||||||
"version": "1.3.4",
|
"version": "1.3.3",
|
||||||
"license": "apache-2.0",
|
"license": "apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --host 0.0.0.0 --hot",
|
"start": "webpack-dev-server --host 0.0.0.0 --hot",
|
||||||
@@ -16,24 +16,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "10.12.29",
|
"@types/node": "10.12.26",
|
||||||
"@types/pretty-bytes": "5.1.0",
|
"@types/pretty-bytes": "5.1.0",
|
||||||
"@types/webassembly-js-api": "0.0.2",
|
"@types/webassembly-js-api": "0.0.2",
|
||||||
"@webcomponents/custom-elements": "1.2.1",
|
"@webcomponents/custom-elements": "1.2.1",
|
||||||
"@webpack-cli/serve": "0.1.3",
|
"@webpack-cli/serve": "0.1.3",
|
||||||
"assets-webpack-plugin": "3.9.10",
|
"assets-webpack-plugin": "3.9.7",
|
||||||
"chokidar": "2.1.2",
|
"chokidar": "2.1.1",
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
"clean-webpack-plugin": "1.0.1",
|
"clean-webpack-plugin": "1.0.1",
|
||||||
"comlink": "3.1.1",
|
"comlink": "3.1.1",
|
||||||
"copy-webpack-plugin": "5.0.0",
|
"copy-webpack-plugin": "4.6.0",
|
||||||
"critters-webpack-plugin": "2.3.0",
|
"critters-webpack-plugin": "2.2.0",
|
||||||
"css-loader": "1.0.1",
|
"css-loader": "1.0.1",
|
||||||
"ejs": "2.6.1",
|
"ejs": "2.6.1",
|
||||||
"escape-string-regexp": "1.0.5",
|
"escape-string-regexp": "1.0.5",
|
||||||
"exports-loader": "0.7.0",
|
"exports-loader": "0.7.0",
|
||||||
"file-drop-element": "0.2.0",
|
"file-drop-element": "0.0.9",
|
||||||
"file-loader": "3.0.1",
|
"file-loader": "3.0.1",
|
||||||
"gzip-size": "5.0.0",
|
"gzip-size": "5.0.0",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"html-webpack-plugin": "3.2.0",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"optimize-css-assets-webpack-plugin": "5.0.1",
|
"optimize-css-assets-webpack-plugin": "5.0.1",
|
||||||
"pointer-tracker": "2.0.3",
|
"pointer-tracker": "2.0.3",
|
||||||
"preact": "8.4.2",
|
"preact": "8.4.2",
|
||||||
"prerender-loader": "1.3.0",
|
"prerender-loader": "1.2.0",
|
||||||
"pretty-bytes": "5.1.0",
|
"pretty-bytes": "5.1.0",
|
||||||
"progress-bar-webpack-plugin": "1.12.1",
|
"progress-bar-webpack-plugin": "1.12.1",
|
||||||
"raw-loader": "1.0.0",
|
"raw-loader": "1.0.0",
|
||||||
@@ -57,19 +57,19 @@
|
|||||||
"script-ext-html-webpack-plugin": "2.1.3",
|
"script-ext-html-webpack-plugin": "2.1.3",
|
||||||
"source-map-loader": "0.2.4",
|
"source-map-loader": "0.2.4",
|
||||||
"style-loader": "0.23.1",
|
"style-loader": "0.23.1",
|
||||||
"terser-webpack-plugin": "1.2.3",
|
"terser-webpack-plugin": "1.2.2",
|
||||||
"ts-loader": "5.3.3",
|
"ts-loader": "5.3.3",
|
||||||
"tslint": "5.13.1",
|
"tslint": "5.12.1",
|
||||||
"tslint-config-airbnb": "5.11.1",
|
"tslint-config-airbnb": "5.11.1",
|
||||||
"tslint-config-semistandard": "7.0.0",
|
"tslint-config-semistandard": "7.0.0",
|
||||||
"tslint-react": "3.6.0",
|
"tslint-react": "3.6.0",
|
||||||
"typed-css-modules": "0.4.1",
|
"typed-css-modules": "0.3.7",
|
||||||
"typescript": "3.3.3333",
|
"typescript": "3.2.4",
|
||||||
"url-loader": "1.1.2",
|
"url-loader": "1.1.2",
|
||||||
"webpack": "4.28.0",
|
"webpack": "4.28.0",
|
||||||
"webpack-bundle-analyzer": "3.1.0",
|
"webpack-bundle-analyzer": "3.0.4",
|
||||||
"webpack-cli": "3.2.3",
|
"webpack-cli": "3.2.3",
|
||||||
"webpack-dev-server": "3.2.1",
|
"webpack-dev-server": "3.1.14",
|
||||||
"worker-plugin": "3.1.0"
|
"worker-plugin": "3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ async function mozjpegEncode(
|
|||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
const { encode } = await import(
|
const { encode } = await import(
|
||||||
/* webpackChunkName: "process-mozjpeg-enc" */
|
/* webpackChunkName: "process-mozjpeg-enc" */
|
||||||
'../mozjpeg/encoder');
|
'../mozjpeg/encoder',
|
||||||
|
);
|
||||||
return encode(data, options);
|
return encode(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +15,8 @@ async function quantize(
|
|||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
const { process } = await import(
|
const { process } = await import(
|
||||||
/* webpackChunkName: "process-imagequant" */
|
/* webpackChunkName: "process-imagequant" */
|
||||||
'../imagequant/processor');
|
'../imagequant/processor',
|
||||||
|
);
|
||||||
return process(data, opts);
|
return process(data, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +25,8 @@ async function rotate(
|
|||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
const { rotate } = await import(
|
const { rotate } = await import(
|
||||||
/* webpackChunkName: "process-rotate" */
|
/* webpackChunkName: "process-rotate" */
|
||||||
'../rotate/processor');
|
'../rotate/processor',
|
||||||
|
);
|
||||||
|
|
||||||
return rotate(data, opts);
|
return rotate(data, opts);
|
||||||
}
|
}
|
||||||
@@ -33,7 +36,8 @@ async function optiPngEncode(
|
|||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
const { compress } = await import(
|
const { compress } = await import(
|
||||||
/* webpackChunkName: "process-optipng" */
|
/* webpackChunkName: "process-optipng" */
|
||||||
'../optipng/encoder');
|
'../optipng/encoder',
|
||||||
|
);
|
||||||
return compress(data, options);
|
return compress(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +46,16 @@ async function webpEncode(
|
|||||||
): Promise<ArrayBuffer> {
|
): Promise<ArrayBuffer> {
|
||||||
const { encode } = await import(
|
const { encode } = await import(
|
||||||
/* webpackChunkName: "process-webp-enc" */
|
/* webpackChunkName: "process-webp-enc" */
|
||||||
'../webp/encoder');
|
'../webp/encoder',
|
||||||
|
);
|
||||||
return encode(data, options);
|
return encode(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function webpDecode(data: ArrayBuffer): Promise<ImageData> {
|
async function webpDecode(data: ArrayBuffer): Promise<ImageData> {
|
||||||
const { decode } = await import(
|
const { decode } = await import(
|
||||||
/* webpackChunkName: "process-webp-dec" */
|
/* webpackChunkName: "process-webp-dec" */
|
||||||
'../webp/decoder');
|
'../webp/decoder',
|
||||||
|
);
|
||||||
return decode(data);
|
return decode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,32 @@
|
|||||||
import wasmUrl from '../../../codecs/rotate/rotate.wasm';
|
import wasmUrl from '../../../codecs/rotate/rotate.wasm';
|
||||||
import { RotateOptions, RotateModuleInstance } from './processor-meta';
|
import { RotateOptions, RotateModuleInstance } from './processor-meta';
|
||||||
|
|
||||||
// We are loading a 500B module here. Loading the code to feature-detect
|
const instancePromise = (WebAssembly as any).instantiateStreaming(fetch(wasmUrl));
|
||||||
// `instantiateStreaming` probably takes longer to load than the time we save by
|
|
||||||
// using `instantiateStreaming` in the first place. So let’s just use
|
|
||||||
// `ArrayBuffer`s here.
|
|
||||||
const instancePromise = fetch(wasmUrl)
|
|
||||||
.then(r => r.arrayBuffer())
|
|
||||||
.then(buf => WebAssembly.instantiate(buf));
|
|
||||||
|
|
||||||
export async function rotate(
|
export async function rotate(
|
||||||
data: ImageData,
|
data: ImageData,
|
||||||
opts: RotateOptions,
|
opts: RotateOptions,
|
||||||
): Promise<ImageData> {
|
): Promise<ImageData> {
|
||||||
const { instance } = (await instancePromise) as {
|
const { instance } = (await instancePromise) as {instance: RotateModuleInstance};
|
||||||
instance: RotateModuleInstance;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Number of wasm memory pages (á 64KiB) needed to store the image twice.
|
// Number of wasm memory pages (á 64KiB) needed to store the image twice.
|
||||||
const bytesPerImage = data.width * data.height * 4;
|
const bytesPerImage = data.width * data.height * 4;
|
||||||
const numPagesNeeded = Math.ceil((bytesPerImage * 2 + 8) / (64 * 1024));
|
const numPagesNeeded = Math.ceil((bytesPerImage * 2 + 4) / (64 * 1024));
|
||||||
// Only count full pages, just to be safe.
|
// Only count full pages, just to be safe.
|
||||||
const numPagesAvailable = Math.floor(
|
const numPagesAvailable = Math.floor(instance.exports.memory.buffer.byteLength / (64 * 1024));
|
||||||
instance.exports.memory.buffer.byteLength / (64 * 1024),
|
|
||||||
);
|
|
||||||
const additionalPagesToAllocate = numPagesNeeded - numPagesAvailable;
|
const additionalPagesToAllocate = numPagesNeeded - numPagesAvailable;
|
||||||
|
|
||||||
if (additionalPagesToAllocate > 0) {
|
if (additionalPagesToAllocate > 0) {
|
||||||
instance.exports.memory.grow(additionalPagesToAllocate);
|
instance.exports.memory.grow(additionalPagesToAllocate);
|
||||||
}
|
}
|
||||||
const view = new Uint8ClampedArray(instance.exports.memory.buffer);
|
const view = new Uint8ClampedArray(instance.exports.memory.buffer);
|
||||||
view.set(data.data, 8);
|
view.set(data.data, 4);
|
||||||
|
|
||||||
instance.exports.rotate(data.width, data.height, opts.rotate);
|
instance.exports.rotate(data.width, data.height, opts.rotate);
|
||||||
|
|
||||||
const flipDimensions = opts.rotate % 180 !== 0;
|
const flipDimensions = opts.rotate % 180 !== 0;
|
||||||
return new ImageData(
|
return new ImageData(
|
||||||
view.slice(bytesPerImage + 8, bytesPerImage * 2 + 8),
|
view.slice(bytesPerImage + 4, bytesPerImage * 2 + 4),
|
||||||
flipDimensions ? data.height : data.width,
|
flipDimensions ? data.height : data.width,
|
||||||
flipDimensions ? data.width : data.height,
|
flipDimensions ? data.width : data.height,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ const ROUTE_EDITOR = '/editor';
|
|||||||
|
|
||||||
const compressPromise = import(
|
const compressPromise = import(
|
||||||
/* webpackChunkName: "main-app" */
|
/* webpackChunkName: "main-app" */
|
||||||
'../compress');
|
'../compress',
|
||||||
|
);
|
||||||
const offlinerPromise = import(
|
const offlinerPromise = import(
|
||||||
/* webpackChunkName: "offliner" */
|
/* webpackChunkName: "offliner" */
|
||||||
'../../lib/offliner');
|
'../../lib/offliner',
|
||||||
|
);
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
@@ -72,9 +74,8 @@ export default class App extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
private onFileDrop({ files }: FileDropEvent) {
|
private onFileDrop({ file }: FileDropEvent) {
|
||||||
if (!files || files.length === 0) return;
|
if (!file) return;
|
||||||
const file = files[0];
|
|
||||||
this.openEditor();
|
this.openEditor();
|
||||||
this.setState({ file });
|
this.setState({ file });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ function init() {
|
|||||||
if (!('customElements' in self)) {
|
if (!('customElements' in self)) {
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "wc-polyfill" */
|
/* webpackChunkName: "wc-polyfill" */
|
||||||
'@webcomponents/custom-elements').then(init);
|
'@webcomponents/custom-elements',
|
||||||
|
).then(init);
|
||||||
} else {
|
} else {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const CrittersPlugin = require('critters-webpack-plugin');
|
|||||||
const AssetTemplatePlugin = require('./config/asset-template-plugin');
|
const AssetTemplatePlugin = require('./config/asset-template-plugin');
|
||||||
const addCssTypes = require('./config/add-css-types');
|
const addCssTypes = require('./config/add-css-types');
|
||||||
|
|
||||||
|
const regexpPathSep = path.sep === '\\' ? '\\\\' : '/';
|
||||||
|
|
||||||
function readJson (filename) {
|
function readJson (filename) {
|
||||||
return JSON.parse(fs.readFileSync(filename));
|
return JSON.parse(fs.readFileSync(filename));
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,7 @@ module.exports = async function (_, env) {
|
|||||||
return {
|
return {
|
||||||
mode: isProd ? 'production' : 'development',
|
mode: isProd ? 'production' : 'development',
|
||||||
entry: {
|
entry: {
|
||||||
'first-interaction': './src/index'
|
'first-interaction': path.join('.', 'src', 'index'),
|
||||||
},
|
},
|
||||||
devtool: isProd ? 'source-map' : 'inline-source-map',
|
devtool: isProd ? 'source-map' : 'inline-source-map',
|
||||||
stats: 'minimal',
|
stats: 'minimal',
|
||||||
@@ -52,13 +54,13 @@ module.exports = async function (_, env) {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.mjs', '.js', '.scss', '.css'],
|
extensions: ['.ts', '.tsx', '.mjs', '.js', '.scss', '.css'],
|
||||||
alias: {
|
alias: {
|
||||||
style: path.join(__dirname, 'src/style')
|
style: path.join(__dirname, 'src', 'style')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveLoader: {
|
resolveLoader: {
|
||||||
alias: {
|
alias: {
|
||||||
// async-component-loader returns a wrapper component that waits for the import to load before rendering:
|
// async-component-loader returns a wrapper component that waits for the import to load before rendering:
|
||||||
async: path.join(__dirname, 'config/async-component-loader')
|
async: path.join(__dirname, 'config', 'async-component-loader')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
@@ -147,11 +149,11 @@ module.exports = async function (_, env) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`.
|
// All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`.
|
||||||
test: /\/codecs\/.*\.js$/,
|
test: new RegExp(`${regexpPathSep}codecs${regexpPathSep}.*\.js`),
|
||||||
loader: 'exports-loader'
|
loader: 'exports-loader'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\/codecs\/.*\.wasm$/,
|
test: new RegExp(`${regexpPathSep}codecs${regexpPathSep}.*\.wasm`),
|
||||||
// This is needed to make webpack NOT process wasm files.
|
// This is needed to make webpack NOT process wasm files.
|
||||||
// See https://github.com/webpack/webpack/issues/6725
|
// See https://github.com/webpack/webpack/issues/6725
|
||||||
type: 'javascript/auto',
|
type: 'javascript/auto',
|
||||||
@@ -172,7 +174,7 @@ module.exports = async function (_, env) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new webpack.IgnorePlugin(
|
new webpack.IgnorePlugin(
|
||||||
/(fs|crypto|path)/,
|
/(fs|crypto|path)/,
|
||||||
new RegExp(`${path.sep}codecs${path.sep}`)
|
new RegExp(`${regexpPathSep}codecs${regexpPathSep}`)
|
||||||
),
|
),
|
||||||
|
|
||||||
// Pretty progressbar showing build progress:
|
// Pretty progressbar showing build progress:
|
||||||
|
|||||||
Reference in New Issue
Block a user