mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-23 19:00:13 +02:00
camera.md
This commit is contained in:
parent
afbd29e814
commit
449ac4d302
@ -309,7 +309,8 @@
|
||||
- [Borrow Checker](webassembly/limitations/borrow-checker.md)
|
||||
- [Closures](webassembly/limitations/closures.md)
|
||||
- [Async](webassembly/async.md)
|
||||
|
||||
- [Exercises](exercises/webassembly/webassembly.md)
|
||||
- [Camera](exercises/webassembly/camera.md)
|
||||
|
||||
# Final Words
|
||||
|
||||
|
48
src/exercises/webassembly/camera.md
Normal file
48
src/exercises/webassembly/camera.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Camera
|
||||
|
||||
In this exercise we will play with the camera in real time.
|
||||
|
||||
Serve the web server and navigate to [http://localhost:8000/exercises/camera/](http://localhost:8000/exercises/camera/).
|
||||
|
||||
You will edit [lib.rs](../../rust-wasm-template/lib.rs) as usual.
|
||||
|
||||
Here is the basic code you will need:
|
||||
|
||||
```rust
|
||||
extern crate console_error_panic_hook;
|
||||
|
||||
use js_sys::Uint8ClampedArray;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn alert(s: &str);
|
||||
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
pub fn log(s: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn set_panic_hook() {
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We want to edit the function `edit_bitmap`. `image_data` is a reference to the [Uint8ClampedArray](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Uint8ClampedArray.html) being rendered on your screen. `Uint8ClampedArray` is a flat array of unsigned int of `8` bits.
|
||||
It represents `srgb` image, which means that each pixel is represented as a vector of 4 elements [red, green, blue, illumination].
|
||||
The image can be thought of as a matrix of dimension `(width, height, 4)`, it is row-major.
|
||||
|
||||
We will reuse our different implementations. So do not erase them when going from one exercise to the next.
|
||||
|
||||
1. The first exercise is to turn the top half the image to black.
|
||||
2. Turn the left half of the image to white.
|
||||
3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance `0<->255`, `100<->155`. Beware of illumination.
|
||||
4. You can play with the function, for instance you can implement greyscale, or add random noise.
|
||||
5. In the `setup` function create a dropdown (`<select>`) that will change which transformation to apply to the image.
|
||||
6. Blur only the background. This can be done by figuring out only the pixels that didn't change between multiple frames.
|
||||
While this can be achieved without touching Javascript, I recommend editing it.
|
85
src/exercises/webassembly/solutions-camera.md
Normal file
85
src/exercises/webassembly/solutions-camera.md
Normal file
@ -0,0 +1,85 @@
|
||||
# Camera exercise solution
|
||||
|
||||
1. The first exercise is to turn the top half the image to black.
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
|
||||
for i in 0..image_data.length() / 2 {
|
||||
image_data.set_index(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
2. The second exercise is to turn the left half of the image to white.
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
|
||||
for i in 0..height {
|
||||
for j in 0..(width * 4) / 2 {
|
||||
image_data.set_index(i * 4 * width + j, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance 0<->255, 100<->155. Beware of illumination.
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
|
||||
for i in 0..image_data.length() {
|
||||
if i % 4 == 3 {
|
||||
continue;
|
||||
}
|
||||
let pixel = image_data.get_index(i);
|
||||
image_data.set_index(i, 255 - pixel);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. You can play with the function, for instance you can implement greyscale, or add random noise.
|
||||
5. In the `setup` function create a dropdown (`<select>`) that will change which transformation to apply to the image.
|
||||
```rust
|
||||
static METHODS: [&str; 3] = ["xray", "top-black", "left-white"];
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn setup() -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
let window = web_sys::window().expect("no global `window` exists");
|
||||
let document = window.document().expect("should have a document on window");
|
||||
let body = document.body().expect("document should have a body");
|
||||
|
||||
let select = document.create_element("select")?;
|
||||
for option_value in METHODS {
|
||||
let option = document.create_element("option")?;
|
||||
option.set_inner_html(option_value);
|
||||
option.set_attribute("value", option_value)?;
|
||||
select.append_child(&option)?;
|
||||
}
|
||||
body.append_child(&select)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn edit_bitmap(
|
||||
image_data: &Uint8ClampedArray,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<(), JsValue> {
|
||||
let window = web_sys::window().expect("no global `window` exists");
|
||||
let document = window.document().expect("should have a document on window");
|
||||
let select = document
|
||||
.query_selector("select")?
|
||||
.expect("No select")
|
||||
.dyn_into::<web_sys::HtmlSelectElement>()
|
||||
.expect("Failed to cast <select>");
|
||||
let selected_option = select.value();
|
||||
match selected_option.as_str() {
|
||||
"xray" => xray(image_data, width, height),
|
||||
"top-black" => top_black(image_data, width, height),
|
||||
"left-white" => left_white(image_data, width, height),
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
```
|
1
src/exercises/webassembly/webassembly.md
Normal file
1
src/exercises/webassembly/webassembly.md
Normal file
@ -0,0 +1 @@
|
||||
# Exercises
|
@ -25,6 +25,7 @@ features = [
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
'Element',
|
||||
'HtmlSelectElement',
|
||||
'HtmlElement',
|
||||
'Node',
|
||||
'Window',
|
||||
|
30
src/rust-wasm-template/static/exercises/camera/index.html
Normal file
30
src/rust-wasm-template/static/exercises/camera/index.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>comprehensive-rust camera exercise</title>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
<style>
|
||||
video, canvas {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
display: block;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
select {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
comprehensive-rust camera exercise
|
||||
</h1>
|
||||
<video src=""></video>
|
||||
<canvas></canvas>
|
||||
<script type="module" src="/exercises/camera/index.mjs"></script>
|
||||
</body>
|
||||
</html>
|
38
src/rust-wasm-template/static/exercises/camera/index.mjs
Normal file
38
src/rust-wasm-template/static/exercises/camera/index.mjs
Normal file
@ -0,0 +1,38 @@
|
||||
import init, {setup, edit_bitmap} from '/wasm/project.js';
|
||||
|
||||
const renderCanvas = (ctx, video, bitmapConversionCanvas) => {
|
||||
const bitmapconversionContext = bitmapConversionCanvas.getContext("2d");
|
||||
requestAnimationFrame(async () => {
|
||||
try {
|
||||
bitmapconversionContext.drawImage(video, 0, 0);
|
||||
const bitmap = bitmapconversionContext.getImageData(0, 0, 400, 400);
|
||||
edit_bitmap(bitmap.data, 400, 400);
|
||||
ctx.putImageData(bitmap, 0, 0);
|
||||
} catch(e){
|
||||
console.error(e);
|
||||
} finally {
|
||||
renderCanvas(ctx, video, bitmapConversionCanvas, bitmapconversionContext);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const initCamera = async (video, canvas, bitmapConversionCanvas) => {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({video: {width: 400, height: 400}});
|
||||
video.srcObject = stream;
|
||||
video.onloadedmetadata = () => {
|
||||
video.play();
|
||||
};
|
||||
canvas.width = 400;
|
||||
canvas.height = 400;
|
||||
renderCanvas(canvas.getContext("2d"), video, bitmapConversionCanvas);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// Run the init method to initiate the WebAssembly module.
|
||||
await init();
|
||||
setup();
|
||||
const video = document.querySelector("video");
|
||||
const canvas = document.querySelector("canvas");
|
||||
const bitmapConversionCanvas = new OffscreenCanvas(400, 400);
|
||||
initCamera(video, canvas, bitmapConversionCanvas);
|
||||
})();
|
Loading…
x
Reference in New Issue
Block a user