mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-19 20:19:05 +00:00
Implement alpha premultiplication (#507)
* Implement alpha premultiplication * Add benchmark to resize * Only display "Premultiply alpha" if it's one of the rust resize types. * Add comment about division by zero
This commit is contained in:
41
codecs/resize/benchmark.js
Normal file
41
codecs/resize/benchmark.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
|
||||
self = global = this;
|
||||
load('./pkg/resize.js');
|
||||
|
||||
async function init() {
|
||||
// Adjustable constants.
|
||||
const inputDimensions = 2000;
|
||||
const outputDimensions = 1500;
|
||||
const algorithm = 3; // Lanczos
|
||||
const iterations = new Array(100);
|
||||
|
||||
// Constants. Don’t change.
|
||||
const imageByteSize = inputDimensions * inputDimensions * 4;
|
||||
const imageBuffer = new Uint8ClampedArray(imageByteSize);
|
||||
|
||||
const module = await WebAssembly.compile(readbuffer("./pkg/resize_bg.wasm"));
|
||||
await wasm_bindgen(module);
|
||||
[false, true].forEach(premulti => {
|
||||
print(`\npremultiplication: ${premulti}`);
|
||||
print(`==============================`);
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const start = Date.now();
|
||||
wasm_bindgen.resize(imageBuffer, inputDimensions, inputDimensions, outputDimensions, outputDimensions, algorithm, premulti);
|
||||
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, e.stack));
|
||||
@@ -2,6 +2,7 @@
|
||||
"name": "resize",
|
||||
"scripts": {
|
||||
"build:image": "docker build -t squoosh-resize .",
|
||||
"build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh"
|
||||
"build": "docker run --rm -v $(pwd):/src squoosh-resize ./build.sh",
|
||||
"benchmark": "v8 --no-liftoff --no-wasm-tier-up ./benchmark.js"
|
||||
}
|
||||
}
|
||||
|
||||
3
codecs/resize/pkg/resize.d.ts
vendored
3
codecs/resize/pkg/resize.d.ts
vendored
@@ -6,6 +6,7 @@
|
||||
* @param {number} arg3
|
||||
* @param {number} arg4
|
||||
* @param {number} arg5
|
||||
* @param {boolean} arg6
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function resize(arg0: Uint8Array, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number): Uint8Array;
|
||||
export function resize(arg0: Uint8Array, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number, arg6: boolean): Uint8Array;
|
||||
|
||||
@@ -46,13 +46,14 @@
|
||||
* @param {number} arg3
|
||||
* @param {number} arg4
|
||||
* @param {number} arg5
|
||||
* @param {boolean} arg6
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
__exports.resize = function(arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
__exports.resize = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) {
|
||||
const ptr0 = passArray8ToWasm(arg0);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const retptr = globalArgumentPtr();
|
||||
wasm.resize(retptr, ptr0, len0, arg1, arg2, arg3, arg4, arg5);
|
||||
wasm.resize(retptr, ptr0, len0, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
const mem = getUint32Memory();
|
||||
const rustptr = mem[retptr / 4];
|
||||
const rustlen = mem[retptr / 4 + 1];
|
||||
|
||||
2
codecs/resize/pkg/resize_bg.d.ts
vendored
2
codecs/resize/pkg/resize_bg.d.ts
vendored
@@ -3,4 +3,4 @@ export const memory: WebAssembly.Memory;
|
||||
export function __wbindgen_global_argument_ptr(): number;
|
||||
export function __wbindgen_malloc(a: number): number;
|
||||
export function __wbindgen_free(a: number, b: number): void;
|
||||
export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number): void;
|
||||
export function resize(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): void;
|
||||
|
||||
Binary file not shown.
@@ -22,12 +22,13 @@ cfg_if! {
|
||||
#[wasm_bindgen]
|
||||
#[no_mangle]
|
||||
pub fn resize(
|
||||
input_image: Vec<u8>,
|
||||
mut input_image: Vec<u8>,
|
||||
input_width: usize,
|
||||
input_height: usize,
|
||||
output_width: usize,
|
||||
output_height: usize,
|
||||
typ_idx: usize,
|
||||
premultiply: bool,
|
||||
) -> Vec<u8> {
|
||||
let typ = match typ_idx {
|
||||
0 => Type::Triangle,
|
||||
@@ -36,7 +37,19 @@ pub fn resize(
|
||||
3 => Type::Lanczos3,
|
||||
_ => panic!("Nope"),
|
||||
};
|
||||
let num_input_pixels = input_width * input_height;
|
||||
let num_output_pixels = output_width * output_height;
|
||||
|
||||
if premultiply {
|
||||
for i in 0..num_input_pixels {
|
||||
for j in 0..3 {
|
||||
input_image[4 * i + j] = ((input_image[4 * i + j] as f32)
|
||||
* (input_image[4 * i + 3] as f32)
|
||||
/ 255.0) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut resizer = resize::new(
|
||||
input_width,
|
||||
input_height,
|
||||
@@ -48,5 +61,19 @@ pub fn resize(
|
||||
let mut output_image = Vec::<u8>::with_capacity(num_output_pixels * 4);
|
||||
output_image.resize(num_output_pixels * 4, 0);
|
||||
resizer.resize(input_image.as_slice(), output_image.as_mut_slice());
|
||||
|
||||
if premultiply {
|
||||
for i in 0..num_output_pixels {
|
||||
for j in 0..3 {
|
||||
// We don’t need to worry about division by zero, as division by zero
|
||||
// is well-defined on floats to return `±Inf`. ±Inf is converted to 0
|
||||
// when casting to integers.
|
||||
output_image[4 * i + j] = ((output_image[4 * i + j] as f32) * 255.0
|
||||
/ (output_image[4 * i + 3] as f32))
|
||||
as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output_image;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user