Skip to content

Commit 5e01c83

Browse files
committed
Deploying to gh-pages from @ 49db36a 🚀
1 parent 372de04 commit 5e01c83

7 files changed

Lines changed: 134 additions & 14 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ edition = "2024"
77
crate-type = ["cdylib", "rlib"]
88

99
[dependencies]
10-
image = "0.25.8"
10+
image = { version = "0.25.8", features = ["default-formats"] }
1111
js-sys = "0.3.78"
1212
wasm-bindgen = "0.2.101"

index.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,38 @@ <h1>Offline Image Converter</h1>
138138
</select>
139139
</div>
140140

141+
<details id="advancedInput">
142+
<summary>
143+
Advanced options (click to expand)
144+
</summary>
145+
<div>
146+
<label for="quality">Quality (0-100, where 100 is best quality):</label>
147+
<input type="number" id="quality" name="quality" min="0" max="100" />
148+
</div>
149+
150+
<div>
151+
<label for="compression">Compression method:</label>
152+
<select id="compression" name="compression">
153+
<option value="fast">Fast</option>
154+
<option value="best">Best</option>
155+
<option value="default">Default</option>
156+
</select>
157+
</div>
158+
159+
<div>
160+
<label for="filter">Filter type:</label>
161+
<select id="filter" name="filter">
162+
<option value="adaptive">Adaptive</option>
163+
<option value="no_filter">No filter</option>
164+
<option value="sub">Sub</option>
165+
<option value="up">Up</option>
166+
<option value="avg">Avg</option>
167+
<option value="paeth">Paeth</option>
168+
</select>
169+
</div>
170+
171+
</details>
172+
141173
<button id="convertBtn" onclick="convert()">Convert</button>
142174

143175
<div id="loader" style="display: none;">

pkg/image_convert.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
/* tslint:disable */
22
/* eslint-disable */
3-
export function convert_exposed(image_data: Uint8Array, input: string, output: string): Uint8Array;
3+
export function convert_exposed(image_data: Uint8Array, input: string, output: string, options: EncoderInput): Uint8Array;
4+
export class EncoderInput {
5+
private constructor();
6+
free(): void;
7+
}
48

59
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
610

711
export interface InitOutput {
812
readonly memory: WebAssembly.Memory;
9-
readonly convert_exposed: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
13+
readonly __wbg_encoderinput_free: (a: number, b: number) => void;
14+
readonly convert_exposed: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number, number];
1015
readonly __wbindgen_exn_store: (a: number) => void;
1116
readonly __externref_table_alloc: () => number;
1217
readonly __wbindgen_export_2: WebAssembly.Table;

pkg/image_convert.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ function passArray8ToWasm0(arg, malloc) {
186186
return ptr;
187187
}
188188

189+
function _assertClass(instance, klass) {
190+
if (!(instance instanceof klass)) {
191+
throw new Error(`expected instance of ${klass.name}`);
192+
}
193+
}
194+
189195
function takeFromExternrefTable0(idx) {
190196
const value = wasm.__wbindgen_export_2.get(idx);
191197
wasm.__externref_table_dealloc(idx);
@@ -200,22 +206,44 @@ function getArrayU8FromWasm0(ptr, len) {
200206
* @param {Uint8Array} image_data
201207
* @param {string} input
202208
* @param {string} output
209+
* @param {EncoderInput} options
203210
* @returns {Uint8Array}
204211
*/
205-
export function convert_exposed(image_data, input, output) {
212+
export function convert_exposed(image_data, input, output, options) {
206213
const ptr0 = passArray8ToWasm0(image_data, wasm.__wbindgen_malloc);
207214
const len0 = WASM_VECTOR_LEN;
208215
const ptr1 = passStringToWasm0(input, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
209216
const len1 = WASM_VECTOR_LEN;
210217
const ptr2 = passStringToWasm0(output, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
211218
const len2 = WASM_VECTOR_LEN;
212-
const ret = wasm.convert_exposed(ptr0, len0, ptr1, len1, ptr2, len2);
219+
_assertClass(options, EncoderInput);
220+
var ptr3 = options.__destroy_into_raw();
221+
const ret = wasm.convert_exposed(ptr0, len0, ptr1, len1, ptr2, len2, ptr3);
213222
if (ret[3]) {
214223
throw takeFromExternrefTable0(ret[2]);
215224
}
216-
var v4 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
225+
var v5 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
217226
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
218-
return v4;
227+
return v5;
228+
}
229+
230+
const EncoderInputFinalization = (typeof FinalizationRegistry === 'undefined')
231+
? { register: () => {}, unregister: () => {} }
232+
: new FinalizationRegistry(ptr => wasm.__wbg_encoderinput_free(ptr >>> 0, 1));
233+
234+
export class EncoderInput {
235+
236+
__destroy_into_raw() {
237+
const ptr = this.__wbg_ptr;
238+
this.__wbg_ptr = 0;
239+
EncoderInputFinalization.unregister(this);
240+
return ptr;
241+
}
242+
243+
free() {
244+
const ptr = this.__destroy_into_raw();
245+
wasm.__wbg_encoderinput_free(ptr, 0);
246+
}
219247
}
220248

221249
const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']);

pkg/image_convert_bg.wasm

25.4 KB
Binary file not shown.

pkg/image_convert_bg.wasm.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/* tslint:disable */
22
/* eslint-disable */
33
export const memory: WebAssembly.Memory;
4-
export const convert_exposed: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
4+
export const __wbg_encoderinput_free: (a: number, b: number) => void;
5+
export const convert_exposed: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number, number];
56
export const __wbindgen_exn_store: (a: number) => void;
67
export const __externref_table_alloc: () => number;
78
export const __wbindgen_export_2: WebAssembly.Table;

src/lib.rs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,72 @@
11
use std::io::Cursor;
22

3-
use image::{ImageError, ImageFormat};
3+
use image::{codecs::{jpeg::JpegEncoder, png::{CompressionType, FilterType, PngEncoder}}, ImageEncoder, ImageError, ImageFormat};
44
use js_sys::Reflect;
55
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
66

7+
enum EncoderOptions {
8+
Jpeg { quality: u8 },
9+
Png { compression: CompressionType, filter: FilterType },
10+
None,
11+
}
12+
13+
#[wasm_bindgen]
14+
pub struct EncoderInput {
15+
quality: Option<u8>,
16+
compression: Option<String>,
17+
filter: Option<String>,
18+
}
19+
20+
impl EncoderInput {
21+
fn to_options(&self, format: ImageFormat) -> EncoderOptions {
22+
match format {
23+
ImageFormat::Jpeg => self.quality.map(|v| EncoderOptions::Jpeg { quality: v }).unwrap_or(EncoderOptions::None),
24+
ImageFormat::Png => {
25+
let compression = match self.compression.as_deref() {
26+
Some("fast") => Some(CompressionType::Fast),
27+
Some("best") => Some(CompressionType::Best),
28+
Some("default") => Some(CompressionType::Default),
29+
_ => None,
30+
};
31+
let filter = match self.filter.as_deref() {
32+
Some("no_filter") => Some(FilterType::NoFilter),
33+
Some("sub") => Some(FilterType::Sub),
34+
Some("up") => Some(FilterType::Up),
35+
Some("avg") => Some(FilterType::Avg),
36+
Some("paeth") => Some(FilterType::Paeth),
37+
Some("adaptive") => Some(FilterType::Adaptive),
38+
_ => None
39+
};
40+
match (compression, filter) {
41+
(Some(c), Some(f)) => EncoderOptions::Png { compression: c, filter: f },
42+
(_, _) => EncoderOptions::None,
43+
}
44+
}
45+
_ => EncoderOptions::None,
46+
}
47+
}
48+
}
49+
750
fn map_image_err(err: ImageError) -> String {
8-
format!("Image processing error: {}", err)
51+
format!("Image processing error: {}", err)
952
}
1053

11-
fn convert(image_data: Vec<u8>, input: ImageFormat, output: ImageFormat) -> Result<Vec<u8>, String> {
54+
fn convert(image_data: Vec<u8>, input: ImageFormat, output: ImageFormat, options: EncoderOptions) -> Result<Vec<u8>, String> {
1255
report_progress("Loading image...");
1356
let img = image::load_from_memory_with_format(&image_data, input).map_err(map_image_err)?;
1457
let mut output_data: Vec<u8> = Vec::new();
1558
report_progress("Converting to new format...");
16-
img.write_to(&mut Cursor::new(&mut output_data), output).map_err(map_image_err)?;
59+
match options {
60+
EncoderOptions::Jpeg { quality } => {
61+
let mut encoder = JpegEncoder::new_with_quality(&mut output_data, quality);
62+
encoder.encode_image(&img).map_err(map_image_err)?;
63+
},
64+
EncoderOptions::Png { compression, filter } => {
65+
let encoder = PngEncoder::new_with_quality(&mut output_data, compression, filter);
66+
encoder.write_image(&img.as_bytes(), img.width(), img.height(), img.color().into()).map_err(map_image_err)?;
67+
},
68+
EncoderOptions::None => img.write_to(&mut Cursor::new(&mut output_data), output).map_err(map_image_err)?,
69+
};
1770
report_progress("Completed conversion.");
1871
Ok(output_data)
1972
}
@@ -40,10 +93,11 @@ fn str_to_type(s: &str) -> Option<ImageFormat> {
4093
}
4194

4295
#[wasm_bindgen]
43-
pub fn convert_exposed(image_data: Vec<u8>, input: String, output: String) -> Result<Vec<u8>, String> {
96+
pub fn convert_exposed(image_data: Vec<u8>, input: String, output: String, options: EncoderInput) -> Result<Vec<u8>, String> {
4497
let input = str_to_type(&input).ok_or_else(|| format!("Unsupported input format: {}", input))?;
4598
let output = str_to_type(&output).ok_or_else(|| format!("Unsupported output format: {}", output))?;
46-
convert(image_data, input, output)
99+
let options = options.to_options(output);
100+
convert(image_data, input, output, options)
47101
}
48102

49103
fn report_progress(message: &str) {

0 commit comments

Comments
 (0)