wild_doc_script_image/
lib.rs1use std::{path::PathBuf, sync::Arc};
2
3use image::{imageops::FilterType, GenericImageView, RgbaImage};
4use parking_lot::Mutex;
5use wild_doc_script::{
6 anyhow::Result, async_trait, IncludeAdaptor, Stack, WildDocScript, WildDocValue,
7};
8
9pub struct WdImage {}
10
11fn var2u32(value: &str, stack: &Stack) -> Option<u32> {
12 if value.starts_with("$") {
13 let value = unsafe { std::str::from_utf8_unchecked(&value.as_bytes()[1..]) };
14 if let Some(WildDocValue::Number(v)) = stack.get(&Arc::new(value.into())) {
15 if let Some(v) = v.as_u64() {
16 return Some(v as u32);
17 }
18 }
19 } else {
20 if let Ok(v) = value.parse::<u32>() {
21 return Some(v);
22 }
23 }
24 None
25}
26
27#[async_trait(?Send)]
28impl<I: IncludeAdaptor + Send> WildDocScript<I> for WdImage {
29 fn new(_: Arc<Mutex<I>>, _: PathBuf, _: &Stack) -> Result<Self>
30 where
31 Self: Sized,
32 {
33 Ok(Self {})
34 }
35
36 async fn evaluate_module(&mut self, _: &str, _: &str, _: &Stack) -> Result<()> {
37 Ok(())
38 }
39
40 async fn eval(&mut self, code: &str, stack: &Stack) -> Result<WildDocValue> {
41 let splited: Vec<&str> = code.split("?").collect();
42 if let Some(image_var) = splited.get(0) {
43 if let Some(WildDocValue::Binary(image)) = stack.get(&Arc::new(image_var.to_string())) {
44 if let Some(param) = splited.get(1) {
45 if let Ok(mut image) = image::load_from_memory(image) {
46 let mut w = None;
47 let mut h = None;
48 let mut mode: Option<&str> = None;
49 for p in param.split("&").into_iter() {
50 let pp: Vec<&str> = p.split("=").collect();
51 if pp.len() == 2 {
52 let value = unsafe { pp.get_unchecked(1) };
53 match unsafe { pp.get_unchecked(0).as_ref() } {
54 "w" => {
55 w = var2u32(value, stack);
56 }
57 "h" => {
58 h = var2u32(value, stack);
59 }
60 "m" => {
61 if value.starts_with("$") {
62 if let Some(WildDocValue::String(v)) =
63 stack.get(&Arc::new(
64 unsafe {
65 std::str::from_utf8_unchecked(
66 &value.as_bytes()[1..],
67 )
68 }
69 .into(),
70 ))
71 {
72 mode = Some(v);
73 }
74 } else {
75 mode = Some(value);
76 }
77 }
78 _ => {}
79 }
80 }
81 }
82 if w.is_some() && h.is_none() {
83 let r = image.width() as f32 / w.unwrap() as f32;
84 h = Some((image.height() as f32 * r) as u32);
85 } else if w.is_none() && h.is_some() {
86 let r = image.height() as f32 / h.unwrap() as f32;
87 w = Some((image.width() as f32 * r) as u32);
88 }
89 if let (Some(w), Some(h)) = (w, h) {
90 image = match mode {
91 Some("fit") => image.resize(w, h, FilterType::Triangle),
92 Some("cover") => image.resize_to_fill(w, h, FilterType::Triangle),
93 Some("contain") => {
94 let resized = image.resize(w, h, FilterType::Triangle);
95 let resized_w = resized.width();
96 let resized_h = resized.height();
97 if resized_w != w || resized_h != h {
98 let mut image = RgbaImage::new(w, h);
99 let offset_x = (w - resized_w) / 2;
100 let offset_y = (h - resized_h) / 2;
101 for y in 0..resized_h {
102 for x in 0..resized_w {
103 image.put_pixel(
104 x + offset_x,
105 y + offset_y,
106 resized.get_pixel(x, y),
107 );
108 }
109 }
110 image.into()
111 } else {
112 resized
113 }
114 }
115 Some("crop") => image.crop_imm(
116 (image.width() - w) / 2,
117 (image.height() - h) / 2,
118 w,
119 h,
120 ),
121 _ => image.resize_exact(w, h, FilterType::Triangle),
122 };
123 }
124 let mut buffer = std::io::Cursor::new(vec![]);
125 let _ = image.write_to(&mut buffer, image::ImageOutputFormat::WebP);
126 return Ok(WildDocValue::Binary(buffer.into_inner()));
127 }
128 } else {
129 return Ok(WildDocValue::Binary(image.clone()));
130 }
131 }
132 }
133 Ok(WildDocValue::Null)
134 }
135}