1use base64::{decode, encode};
43use image::DynamicImage::ImageRgba8;
44use image::GenericImage;
45use serde::{Deserialize, Serialize};
46use std::io::Cursor;
47
48#[cfg(feature = "enable_wasm")]
49use wasm_bindgen::prelude::*;
50
51#[cfg(feature = "wasm-bindgen")]
52use wasm_bindgen::Clamped;
53
54#[cfg(feature = "web-sys")]
55use web_sys::{
56 Blob, CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement, ImageData,
57};
58
59#[cfg(feature = "wee_alloc")]
62#[global_allocator]
63static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
64
65#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
68#[derive(Serialize, Deserialize, Clone, Debug)]
69pub struct PhotonImage {
70 raw_pixels: Vec<u8>,
71 width: u32,
72 height: u32,
73}
74
75#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
76impl PhotonImage {
77 #[cfg_attr(feature = "enable_wasm", wasm_bindgen(constructor))]
78 pub fn new(raw_pixels: Vec<u8>, width: u32, height: u32) -> PhotonImage {
80 PhotonImage {
81 raw_pixels,
82 width,
83 height,
84 }
85 }
86
87 pub fn new_from_base64(base64: &str) -> PhotonImage {
89 base64_to_image(base64)
90 }
91
92 pub fn new_from_byteslice(vec: Vec<u8>) -> PhotonImage {
94 let slice = vec.as_slice();
95
96 let img = image::load_from_memory(slice).unwrap();
97
98 let raw_pixels = img.to_rgba8().to_vec();
99
100 PhotonImage {
101 raw_pixels,
102 width: img.width(),
103 height: img.height(),
104 }
105 }
106
107 #[cfg(feature = "web-sys")]
109 pub fn new_from_blob(blob: Blob) -> PhotonImage {
110 let bytes: js_sys::Uint8Array = js_sys::Uint8Array::new(&blob);
111
112 let vec = bytes.to_vec();
113
114 PhotonImage::new_from_byteslice(vec)
115 }
116
117 #[cfg(feature = "web-sys")]
119 pub fn new_from_image(image: HtmlImageElement) -> PhotonImage {
120 set_panic_hook();
121
122 let document = web_sys::window().unwrap().document().unwrap();
123
124 let canvas = document
125 .create_element("canvas")
126 .unwrap()
127 .dyn_into::<web_sys::HtmlCanvasElement>()
128 .unwrap();
129
130 canvas.set_width(image.width());
131 canvas.set_height(image.height());
132
133 let context = canvas
134 .get_context("2d")
135 .unwrap()
136 .unwrap()
137 .dyn_into::<CanvasRenderingContext2d>()
138 .unwrap();
139
140 context
141 .draw_image_with_html_image_element(&image, 0.0, 0.0)
142 .unwrap();
143
144 open_image(canvas, context)
145 }
146
147 pub fn get_width(&self) -> u32 {
164 self.width
165 }
166
167 pub fn get_raw_pixels(&self) -> Vec<u8> {
169 self.raw_pixels.clone()
170 }
171
172 pub fn get_height(&self) -> u32 {
174 self.height
175 }
176
177 pub fn get_base64(&self) -> String {
179 let mut img = helpers::dyn_image_from_raw(self);
180 img = ImageRgba8(img.to_rgba8());
181
182 let mut buffer = vec![];
183 img.write_to(&mut Cursor::new(&mut buffer), image::ImageOutputFormat::Png)
184 .unwrap();
185 let base64 = encode(&buffer);
186
187 let res_base64 = format!("data:image/png;base64,{}", base64.replace("\r\n", ""));
188
189 res_base64
190 }
191
192 pub fn get_bytes(&self) -> Vec<u8> {
194 let mut img = helpers::dyn_image_from_raw(self);
195 img = ImageRgba8(img.to_rgba8());
196 let mut buffer = vec![];
197 img.write_to(&mut Cursor::new(&mut buffer), image::ImageOutputFormat::Png)
198 .unwrap();
199 buffer
200 }
201
202 pub fn get_bytes_jpeg(&self, quality: u8) -> Vec<u8> {
204 let mut img = helpers::dyn_image_from_raw(self);
205 img = ImageRgba8(img.to_rgba8());
206 let mut buffer = vec![];
207 let out_format = image::ImageOutputFormat::Jpeg(quality);
208 img.write_to(&mut Cursor::new(&mut buffer), out_format)
209 .unwrap();
210 buffer
211 }
212
213 pub fn get_bytes_webp(&self) -> Vec<u8> {
215 let mut img = helpers::dyn_image_from_raw(self);
216 img = ImageRgba8(img.to_rgba8());
217 let mut buffer = vec![];
218 let out_format = image::ImageOutputFormat::WebP;
219 img.write_to(&mut Cursor::new(&mut buffer), out_format)
220 .unwrap();
221 buffer
222 }
223
224 #[cfg(all(feature = "web-sys", feature = "wasm-bindgen"))]
226 #[allow(clippy::unnecessary_mut_passed)]
227 pub fn get_image_data(&mut self) -> ImageData {
228 ImageData::new_with_u8_clamped_array_and_sh(
229 Clamped(&mut self.raw_pixels),
230 self.width,
231 self.height,
232 )
233 .unwrap()
234 }
235
236 #[cfg(feature = "web-sys")]
238 pub fn set_imgdata(&mut self, img_data: ImageData) {
239 let width = img_data.width();
240 let height = img_data.height();
241 let raw_pixels = to_raw_pixels(img_data);
242 self.width = width;
243 self.height = height;
244 self.raw_pixels = raw_pixels;
245 }
246
247 pub fn get_estimated_filesize(&self) -> u64 {
249 let base64_data = self.get_base64();
250 let padding_count = if base64_data.ends_with("==") {
251 2
252 } else if base64_data.ends_with('=') {
253 1
254 } else {
255 0
256 };
257
258 ((base64_data.len() as f64) * 0.75).ceil() as u64 - padding_count
260 }
261}
262
263#[cfg(feature = "web-sys")]
265impl From<ImageData> for PhotonImage {
266 fn from(imgdata: ImageData) -> Self {
267 let width = imgdata.width();
268 let height = imgdata.height();
269 let raw_pixels = to_raw_pixels(imgdata);
270 PhotonImage {
271 raw_pixels,
272 width,
273 height,
274 }
275 }
276}
277
278#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
280#[derive(Serialize, Deserialize, Debug, Clone)]
281pub struct Rgb {
282 r: u8,
283 g: u8,
284 b: u8,
285}
286
287#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
288impl Rgb {
289 #[cfg_attr(feature = "enable_wasm", wasm_bindgen(constructor))]
290 pub fn new(r: u8, g: u8, b: u8) -> Rgb {
292 Rgb { r, g, b }
293 }
294
295 pub fn set_red(&mut self, r: u8) {
297 self.r = r;
298 }
299
300 pub fn set_green(&mut self, g: u8) {
302 self.g = g;
303 }
304
305 pub fn set_blue(&mut self, b: u8) {
307 self.b = b;
308 }
309
310 pub fn get_red(&self) -> u8 {
312 self.r
313 }
314
315 pub fn get_green(&self) -> u8 {
317 self.g
318 }
319
320 pub fn get_blue(&self) -> u8 {
322 self.b
323 }
324}
325
326impl From<Vec<u8>> for Rgb {
327 fn from(vec: Vec<u8>) -> Self {
328 if vec.len() != 3 {
329 panic!("Vec length must be equal to 3.")
330 }
331 Rgb::new(vec[0], vec[1], vec[2])
332 }
333}
334
335#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
337#[derive(Serialize, Deserialize, Debug)]
338pub struct Rgba {
339 r: u8,
340 g: u8,
341 b: u8,
342 a: u8,
343}
344
345#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
346impl Rgba {
347 #[cfg_attr(feature = "enable_wasm", wasm_bindgen(constructor))]
348 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Rgba {
350 Rgba { r, g, b, a }
351 }
352
353 pub fn set_red(&mut self, r: u8) {
355 self.r = r;
356 }
357
358 pub fn set_green(&mut self, g: u8) {
360 self.g = g;
361 }
362
363 pub fn set_blue(&mut self, b: u8) {
365 self.b = b;
366 }
367
368 pub fn set_alpha(&mut self, a: u8) {
370 self.a = a;
371 }
372
373 pub fn get_red(&self) -> u8 {
375 self.r
376 }
377
378 pub fn get_green(&self) -> u8 {
380 self.g
381 }
382
383 pub fn get_blue(&self) -> u8 {
385 self.b
386 }
387
388 pub fn get_alpha(&self) -> u8 {
390 self.a
391 }
392}
393
394impl From<Vec<u8>> for Rgba {
395 fn from(vec: Vec<u8>) -> Self {
396 if vec.len() != 4 {
397 panic!("Vec length must be equal to 4.")
398 }
399 Rgba::new(vec[0], vec[1], vec[2], vec[3])
400 }
401}
402
403#[cfg(feature = "enable_wasm")]
405#[wasm_bindgen]
406pub fn run() -> Result<(), JsValue> {
407 set_panic_hook();
408
409 let window = web_sys::window().expect("No Window found, should have a Window");
410 let document = window
411 .document()
412 .expect("No Document found, should have a Document");
413
414 let p: web_sys::Node = document.create_element("p")?.into();
415 p.set_text_content(Some("You're successfully running WASM!"));
416
417 let body = document
418 .body()
419 .expect("ERR: No body found, should have a body");
420 let body: &web_sys::Node = body.as_ref();
421 body.append_child(&p)?;
422 Ok(())
423}
424
425#[cfg(feature = "web-sys")]
427#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
428pub fn get_image_data(
429 canvas: &HtmlCanvasElement,
430 ctx: &CanvasRenderingContext2d,
431) -> ImageData {
432 set_panic_hook();
433 let width = canvas.width();
434 let height = canvas.height();
435
436 let data = ctx
438 .get_image_data(0.0, 0.0, width as f64, height as f64)
439 .unwrap();
440 let _vec_data = data.data().to_vec();
441 data
442}
443
444#[cfg(all(feature = "web-sys", feature = "wasm-bindgen"))]
446#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
447#[allow(non_snake_case)]
448#[allow(clippy::unnecessary_mut_passed)]
449pub fn putImageData(
450 canvas: HtmlCanvasElement,
451 ctx: CanvasRenderingContext2d,
452 new_image: PhotonImage,
453) {
454 let mut raw_pixels = new_image.raw_pixels;
456 let new_img_data = ImageData::new_with_u8_clamped_array_and_sh(
457 Clamped(&mut raw_pixels),
458 canvas.width(),
459 canvas.height(),
460 );
461
462 ctx.put_image_data(&new_img_data.unwrap(), 0.0, 0.0)
464 .expect("Should put image data on Canvas");
465}
466
467#[cfg(feature = "web-sys")]
472#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
473#[no_mangle]
474pub fn open_image(
475 canvas: HtmlCanvasElement,
476 ctx: CanvasRenderingContext2d,
477) -> PhotonImage {
478 let imgdata = get_image_data(&canvas, &ctx);
479 let raw_pixels = to_raw_pixels(imgdata);
480 PhotonImage {
481 raw_pixels,
482 width: canvas.width(),
483 height: canvas.height(),
484 }
485}
486
487#[cfg(feature = "web-sys")]
489#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
490pub fn to_raw_pixels(imgdata: ImageData) -> Vec<u8> {
491 imgdata.data().to_vec()
492}
493
494#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
496pub fn base64_to_image(base64: &str) -> PhotonImage {
497 let base64_to_vec: Vec<u8> = base64_to_vec(base64);
498
499 let slice = base64_to_vec.as_slice();
500
501 let mut img = image::load_from_memory(slice).unwrap();
502 img = ImageRgba8(img.to_rgba8());
503
504 let width = img.width();
505 let height = img.height();
506
507 let raw_pixels = img.into_bytes();
508
509 PhotonImage {
510 raw_pixels,
511 width,
512 height,
513 }
514}
515
516#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
518pub fn base64_to_vec(base64: &str) -> Vec<u8> {
519 decode(base64).unwrap()
520}
521
522#[cfg(all(feature = "web-sys", feature = "wasm-bindgen"))]
524#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
525#[allow(clippy::unnecessary_mut_passed)]
526pub fn to_image_data(photon_image: PhotonImage) -> ImageData {
527 let mut raw_pixels = photon_image.raw_pixels;
528 let width = photon_image.width;
529 let height = photon_image.height;
530 ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut raw_pixels), width, height)
531 .unwrap()
532}
533
534#[cfg(not(target_os = "wasi"))]
535fn set_panic_hook() {
536 #[cfg(feature = "console_error_panic_hook")]
539 console_error_panic_hook::set_once();
540}
541
542pub mod channels;
543pub mod colour_spaces;
544pub mod conv;
545pub mod effects;
546pub mod filters;
547pub mod helpers;
548mod iter;
549pub mod monochrome;
550pub mod multiple;
551pub mod native;
552pub mod noise;
553mod tests;
554pub mod text;
555pub mod transform;