diff --git a/Cargo.lock b/Cargo.lock index 49b9ba875..fac5210ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -210,6 +216,16 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "gif" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "grep-matcher" version = "0.1.8" @@ -302,6 +318,9 @@ checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", + "color_quant", + "gif", + "image-webp", "moxcms", "num-traits", "png", @@ -309,6 +328,16 @@ dependencies = [ "zune-jpeg", ] +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + [[package]] name = "js-sys" version = "0.3.85" @@ -551,6 +580,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.44" @@ -859,6 +894,12 @@ dependencies = [ "string_cache_codegen", ] +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/crates/pi-natives/Cargo.toml b/crates/pi-natives/Cargo.toml index 198cebe7e..8b4afb412 100644 --- a/crates/pi-natives/Cargo.toml +++ b/crates/pi-natives/Cargo.toml @@ -19,7 +19,7 @@ serde-wasm-bindgen = "0.6" grep-regex = "0.1.14" grep-searcher = "0.1.16" grep-matcher = "0.1.8" -image = { version = "0.25", default-features = false, features = ["png", "jpeg"] } +image = { version = "0.25", default-features = false, features = ["png", "jpeg", "gif", "webp"] } unicode-segmentation = "1.11" unicode-width = "0.2" syntect = { version = "5.3", default-features = false, features = ["default-syntaxes", "default-themes", "regex-fancy"] } diff --git a/crates/pi-natives/src/image.rs b/crates/pi-natives/src/image.rs index e3ab1125b..228e5bbc6 100644 --- a/crates/pi-natives/src/image.rs +++ b/crates/pi-natives/src/image.rs @@ -91,6 +91,29 @@ impl PhotonImage { .map_err(|e| JsValue::from_str(&format!("Failed to encode JPEG: {e}")))?; Ok(buffer) } + + /// Export image as lossless WebP bytes. + #[wasm_bindgen(js_name = get_bytes_webp)] + pub fn get_bytes_webp(&self) -> Result, JsValue> { + let mut buffer = Vec::new(); + let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut buffer); + self + .img + .write_with_encoder(encoder) + .map_err(|e| JsValue::from_str(&format!("Failed to encode WebP: {e}")))?; + Ok(buffer) + } + + /// Export image as GIF bytes. + #[wasm_bindgen(js_name = get_bytes_gif)] + pub fn get_bytes_gif(&self) -> Result, JsValue> { + let mut buffer = Vec::new(); + self + .img + .write_to(&mut Cursor::new(&mut buffer), ImageFormat::Gif) + .map_err(|e| JsValue::from_str(&format!("Failed to encode GIF: {e}")))?; + Ok(buffer) + } } /// Resize an image to the specified dimensions. diff --git a/packages/natives/wasm/pi_natives.d.ts b/packages/natives/wasm/pi_natives.d.ts index e1a8621b9..1677619fd 100644 --- a/packages/natives/wasm/pi_natives.d.ts +++ b/packages/natives/wasm/pi_natives.d.ts @@ -42,10 +42,18 @@ export class PhotonImage { * Export image as PNG bytes. */ get_bytes(): Uint8Array; + /** + * Export image as GIF bytes. + */ + get_bytes_gif(): Uint8Array; /** * Export image as JPEG bytes with specified quality (0-100). */ get_bytes_jpeg(quality: number): Uint8Array; + /** + * Export image as lossless WebP bytes. + */ + get_bytes_webp(): Uint8Array; /** * Get the height of the image. */ diff --git a/packages/natives/wasm/pi_natives.js b/packages/natives/wasm/pi_natives.js index 175b79b71..656f850bf 100644 --- a/packages/natives/wasm/pi_natives.js +++ b/packages/natives/wasm/pi_natives.js @@ -133,6 +133,28 @@ export class PhotonImage { wasm.__wbindgen_add_to_stack_pointer(16); } } + /** + * Export image as GIF bytes. + * @returns {Uint8Array} + */ + get_bytes_gif() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.photonimage_get_bytes_gif(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); + var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true); + if (r3) { + throw takeObject(r2); + } + var v1 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_export3(r0, r1 * 1, 1); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } /** * Export image as JPEG bytes with specified quality (0-100). * @param {number} quality @@ -156,6 +178,28 @@ export class PhotonImage { wasm.__wbindgen_add_to_stack_pointer(16); } } + /** + * Export image as lossless WebP bytes. + * @returns {Uint8Array} + */ + get_bytes_webp() { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.photonimage_get_bytes_webp(retptr, this.__wbg_ptr); + var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); + var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true); + var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true); + var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true); + if (r3) { + throw takeObject(r2); + } + var v1 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_export3(r0, r1 * 1, 1); + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } + } /** * Get the height of the image. * @returns {number} diff --git a/packages/natives/wasm/pi_natives_bg.wasm b/packages/natives/wasm/pi_natives_bg.wasm index fc239d0fc..b678c53ec 100644 --- a/packages/natives/wasm/pi_natives_bg.wasm +++ b/packages/natives/wasm/pi_natives_bg.wasm @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd7e6079f999193d10dc7c841d62c31b1e88cdee495b8e5688ee9d1a0252e4c0 -size 3436485 +oid sha256:651b67f5d05c8391d6e1590c04b5c74cc9d2cc8e3c5d9f7ebe6fd83dd103e99f +size 3718674 diff --git a/packages/natives/wasm/pi_natives_bg.wasm.d.ts b/packages/natives/wasm/pi_natives_bg.wasm.d.ts index 12b486276..600d8dac5 100644 --- a/packages/natives/wasm/pi_natives_bg.wasm.d.ts +++ b/packages/natives/wasm/pi_natives_bg.wasm.d.ts @@ -12,7 +12,9 @@ export const has_match: (a: number, b: number, c: number, d: number, e: number, export const highlight_code: (a: number, b: number, c: number, d: number, e: number, f: number) => void; export const html_to_markdown: (a: number, b: number, c: number, d: number) => void; export const photonimage_get_bytes: (a: number, b: number) => void; +export const photonimage_get_bytes_gif: (a: number, b: number) => void; export const photonimage_get_bytes_jpeg: (a: number, b: number, c: number) => void; +export const photonimage_get_bytes_webp: (a: number, b: number) => void; export const photonimage_get_height: (a: number) => number; export const photonimage_get_width: (a: number) => number; export const photonimage_new_from_byteslice: (a: number, b: number, c: number) => void;