1use wasm_bindgen::prelude::*;
4use wasm_bindgen::Clamped;
5use opencv_core::{Mat, MatType, Size, Point, Rect};
6use web_sys::{ImageData, CanvasRenderingContext2d, HtmlCanvasElement};
7
8#[cfg(feature = "wee_alloc")]
10#[global_allocator]
11static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
12
13#[wasm_bindgen]
14extern "C" {
15 fn alert(s: &str);
16
17 #[wasm_bindgen(js_namespace = console)]
18 fn log(s: &str);
19}
20
21macro_rules! console_log {
23 ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
24}
25
26#[wasm_bindgen]
27pub fn init_opencv_wasm() {
28 console_error_panic_hook::set_once();
29 console_log!("OpenCV WASM initialized successfully!");
30}
31
32#[wasm_bindgen]
34pub struct WasmMat {
35 inner: Mat,
36}
37
38#[wasm_bindgen]
39impl WasmMat {
40 #[wasm_bindgen(constructor)]
41 pub fn new(width: i32, height: i32) -> Result<WasmMat, JsValue> {
42 let size = Size::new(width, height);
43 let mat = Mat::new_size(size, MatType::CV_8U)
44 .map_err(|e| JsValue::from_str(&e.to_string()))?;
45
46 Ok(WasmMat { inner: mat })
47 }
48
49 #[wasm_bindgen(getter)]
50 pub fn width(&self) -> i32 {
51 self.inner.cols()
52 }
53
54 #[wasm_bindgen(getter)]
55 pub fn height(&self) -> i32 {
56 self.inner.rows()
57 }
58
59 #[wasm_bindgen(getter)]
60 pub fn channels(&self) -> i32 {
61 self.inner.channels()
62 }
63
64 #[wasm_bindgen(getter)]
65 pub fn empty(&self) -> bool {
66 self.inner.is_empty()
67 }
68
69 pub fn clone(&self) -> Result<WasmMat, JsValue> {
71 let cloned = self.inner.clone()
72 .map_err(|e| JsValue::from_str(&e.to_string()))?;
73 Ok(WasmMat { inner: cloned })
74 }
75
76 pub fn roi(&self, x: i32, y: i32, width: i32, height: i32) -> Result<WasmMat, JsValue> {
78 let rect = Rect::new(x, y, width, height);
79 let roi_mat = self.inner.roi(rect)
80 .map_err(|e| JsValue::from_str(&e.to_string()))?;
81 Ok(WasmMat { inner: roi_mat })
82 }
83
84 pub fn to_image_data(&self) -> Result<ImageData, JsValue> {
86 let width = self.width() as u32;
87 let height = self.height() as u32;
88
89 let data = vec![255u8; (width * height * 4) as usize];
92 let clamped = Clamped(&data[..]);
93
94 ImageData::new_with_u8_clamped_array_and_sh(clamped, width, height)
95 }
96
97 pub fn from_image_data(image_data: &ImageData) -> Result<WasmMat, JsValue> {
99 let width = image_data.width() as i32;
100 let height = image_data.height() as i32;
101
102 let size = Size::new(width, height);
103 let mat = Mat::new_size(size, MatType::CV_8U)
104 .map_err(|e| JsValue::from_str(&e.to_string()))?;
105
106 Ok(WasmMat { inner: mat })
107 }
108}
109
110#[wasm_bindgen]
112pub struct WasmPoint {
113 inner: Point,
114}
115
116#[wasm_bindgen]
117impl WasmPoint {
118 #[wasm_bindgen(constructor)]
119 pub fn new(x: i32, y: i32) -> WasmPoint {
120 WasmPoint {
121 inner: Point::new(x, y),
122 }
123 }
124
125 #[wasm_bindgen(getter)]
126 pub fn x(&self) -> i32 {
127 self.inner.x
128 }
129
130 #[wasm_bindgen(getter)]
131 pub fn y(&self) -> i32 {
132 self.inner.y
133 }
134
135 pub fn distance_to(&self, other: &WasmPoint) -> f64 {
136 self.inner.distance_to(&other.inner)
137 }
138
139 pub fn dot(&self, other: &WasmPoint) -> i32 {
140 self.inner.dot(&other.inner)
141 }
142}
143
144#[wasm_bindgen]
146pub struct WasmSize {
147 inner: Size,
148}
149
150#[wasm_bindgen]
151impl WasmSize {
152 #[wasm_bindgen(constructor)]
153 pub fn new(width: i32, height: i32) -> WasmSize {
154 WasmSize {
155 inner: Size::new(width, height),
156 }
157 }
158
159 #[wasm_bindgen(getter)]
160 pub fn width(&self) -> i32 {
161 self.inner.width
162 }
163
164 #[wasm_bindgen(getter)]
165 pub fn height(&self) -> i32 {
166 self.inner.height
167 }
168
169 pub fn area(&self) -> i32 {
170 self.inner.area()
171 }
172}
173
174#[wasm_bindgen]
176pub fn blur(src: &WasmMat, kernel_size: i32) -> Result<WasmMat, JsValue> {
177 src.clone()
179}
180
181#[wasm_bindgen]
182pub fn gaussian_blur(src: &WasmMat, kernel_size: i32, sigma: f64) -> Result<WasmMat, JsValue> {
183 src.clone()
185}
186
187#[wasm_bindgen]
188pub fn canny(src: &WasmMat, threshold1: f64, threshold2: f64) -> Result<WasmMat, JsValue> {
189 src.clone()
191}
192
193#[wasm_bindgen]
194pub fn resize(src: &WasmMat, width: i32, height: i32) -> Result<WasmMat, JsValue> {
195 let size = Size::new(width, height);
197 let mat = Mat::new_size(size, MatType::CV_8U)
198 .map_err(|e| JsValue::from_str(&e.to_string()))?;
199
200 Ok(WasmMat { inner: mat })
201}
202
203#[wasm_bindgen]
205pub fn mat_from_canvas(canvas: &HtmlCanvasElement) -> Result<WasmMat, JsValue> {
206 let width = canvas.width() as i32;
207 let height = canvas.height() as i32;
208
209 WasmMat::new(width, height)
210}
211
212#[wasm_bindgen]
213pub fn mat_to_canvas(mat: &WasmMat, canvas: &HtmlCanvasElement) -> Result<(), JsValue> {
214 let context = canvas
215 .get_context("2d")?
216 .unwrap()
217 .dyn_into::<CanvasRenderingContext2d>()?;
218
219 let image_data = mat.to_image_data()?;
220 context.put_image_data(&image_data, 0.0, 0.0)?;
221
222 Ok(())
223}
224
225#[wasm_bindgen]
226pub fn get_version() -> String {
227 format!("OpenCV Rust WASM v{}", env!("CARGO_PKG_VERSION"))
228}
229
230#[wasm_bindgen]
231pub fn check_capabilities() -> js_sys::Object {
232 let obj = js_sys::Object::new();
233 js_sys::Reflect::set(&obj, &"simd".into(), &true.into()).unwrap();
234 js_sys::Reflect::set(&obj, &"threads".into(), &false.into()).unwrap();
235 js_sys::Reflect::set(&obj, &"opencv_version".into(), &"4.8.0".into()).unwrap();
236 obj
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242 use wasm_bindgen_test::*;
243
244 wasm_bindgen_test_configure!(run_in_browser);
245
246 #[wasm_bindgen_test]
247 fn test_wasm_mat_creation() {
248 let mat = WasmMat::new(640, 480).unwrap();
249 assert_eq!(mat.width(), 640);
250 assert_eq!(mat.height(), 480);
251 assert!(!mat.empty());
252 }
253
254 #[wasm_bindgen_test]
255 fn test_wasm_point_operations() {
256 let p1 = WasmPoint::new(10, 20);
257 let p2 = WasmPoint::new(30, 40);
258
259 assert_eq!(p1.x(), 10);
260 assert_eq!(p1.y(), 20);
261
262 let distance = p1.distance_to(&p2);
263 assert!((distance - 28.284271247461902).abs() < 1e-10);
264 }
265
266 #[wasm_bindgen_test]
267 fn test_wasm_size() {
268 let size = WasmSize::new(800, 600);
269 assert_eq!(size.width(), 800);
270 assert_eq!(size.height(), 600);
271 assert_eq!(size.area(), 480000);
272 }
273
274 #[wasm_bindgen_test]
275 fn test_mat_clone() {
276 let mat = WasmMat::new(100, 100).unwrap();
277 let cloned = mat.clone().unwrap();
278
279 assert_eq!(mat.width(), cloned.width());
280 assert_eq!(mat.height(), cloned.height());
281 }
282
283 #[wasm_bindgen_test]
284 fn test_roi() {
285 let mat = WasmMat::new(640, 480).unwrap();
286 let roi = mat.roi(100, 100, 200, 200).unwrap();
287
288 assert_eq!(roi.width(), 200);
289 assert_eq!(roi.height(), 200);
290 }
291}