簡體   English   中英

使用 Rust Webassembly 更新 HTML Canvas 圖像數據

[英]Updating HTML Canvas imagedata using Rust Webassembly

第一次發帖,如有不對之處請見諒。 作為練習的一部分,我正在嘗試使用 PNG 文件更新 HTML canvas 中的圖像數據。 function fetch_url_binary(url: String) 從 PNG 返回像素信息。

#[wasm_bindgen] 
pub async fn fetch_url_binary(url: String) -> Result<Uint8Array, JsValue> 
{ 
let window = web_sys::window().unwrap(); // Browser window 
let promise = JsFuture::from(window.fetch_with_str(&url)); // File fetch promise 
let result = promise.await?; // Await fulfillment of fetch 
let response: web_sys::Response = result.dyn_into().unwrap(); // Type casting 
let image_data = JsFuture::from(response.array_buffer()?).await?; // Get text
Ok(Uint8Array::new(&image_data))
}

function unred(url: String, canvas: String) 將紅色通道設置為零,然后使用圖像更新 canvas。

#[wasm_bindgen] 
pub async fn unred(url: String, canvas: String) -> Result<(), JsValue>
{
    let binary = fetch_url_binary(url).await.unwrap();
    let mut altbuf: Vec<u8> = Vec::new();
    for n in 0..binary.length() {
        if n % 4 == 0 {
            binary.set_index(n,0);
        }
        altbuf.push(binary.get_index(n));
    }
    let window = web_sys::window().unwrap();
    let document = window.document().expect("Could not get document");
    let canvas = document.get_element_by_id(&canvas).unwrap().dyn_into::<web_sys::HtmlCanvasElement>()?;
    let context = canvas.get_context("2d")?.unwrap().dyn_into::<web_sys::CanvasRenderingContext2d>()?;
    let image_data_temp = ImageData::new_with_u8_clamped_array(Clamped(&altbuf), altbuf.len().try_into().unwrap());
    context.put_image_data(&image_data_temp.unwrap(), 0.0, 0.0);
    Ok(()) 
}

相關的 HTML 代碼在這里:

<!doctype html><html><body> 
  <canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;">
  <script type="module"> 
  import init, {unred, fetch_url_binary} from './pkg/hi_lib.js'; 
  var result;
  async function run() 
  { 
    await init(); // Initialize module 
    unred("myPng.png", "myCanvas");
  } 
  run(); // Execute async wrapper 
</script> 
</body></html>

然后我在 python3 http 服務器中測試代碼,出現以下錯誤:

       Uncaught (in promise) hi_lib_bg.wasm:0x5a79  
    RuntimeError: unreachable
    at hi_lib_bg.wasm:0x5a79
    at hi_lib_bg.wasm:0x663a
    at hi_lib_bg.wasm:0x71ba
    at hi_lib_bg.wasm:0x72c9
    at hi_lib_bg.wasm:0x673c
    at hi_lib_bg.wasm:0x20e7
    at hi_lib_bg.wasm:0x3fea
    at hi_lib_bg.wasm:0x7580
    at __wbg_adapter_16 (hi_lib.js:202)
    at real (hi_lib.js:187)
    $func69 @   hi_lib_bg.wasm:0x5a79
$func86 @   hi_lib_bg.wasm:0x663a
$func120    @   hi_lib_bg.wasm:0x71ba
$func126    @   hi_lib_bg.wasm:0x72c9
$func88 @   hi_lib_bg.wasm:0x673c
$func37 @   hi_lib_bg.wasm:0x20e7
$func48 @   hi_lib_bg.wasm:0x3fea
$_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h51eff7df35816d6c   @   hi_lib_bg.wasm:0x7580
__wbg_adapter_16    @   hi_lib.js:202
real    @   hi_lib.js:187
Promise.then (async)        
imports.wbg.__wbg_then_2fcac196782070cc @   hi_lib.js:440
$func67 @   hi_lib_bg.wasm:0x584d
$func66 @   hi_lib_bg.wasm:0x5709
$func61 @   hi_lib_bg.wasm:0x52af
$func103    @   hi_lib_bg.wasm:0x6cbc
$_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h51eff7df35816d6c   @   hi_lib_bg.wasm:0x7580
__wbg_adapter_16    @   hi_lib.js:202
real    @   hi_lib.js:187
Promise.then (async)        
imports.wbg.__wbg_then_8c2d62e8ae5978f7 @   hi_lib.js:444
$func44 @   hi_lib_bg.wasm:0x3723
$func45 @   hi_lib_bg.wasm:0x399b
$func37 @   hi_lib_bg.wasm:0x1ad6
$func48 @   hi_lib_bg.wasm:0x3fea
$_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h51eff7df35816d6c   @   hi_lib_bg.wasm:0x7580
__wbg_adapter_16    @   hi_lib.js:202
real    @   hi_lib.js:187
Promise.then (async)        
imports.wbg.__wbg_then_2fcac196782070cc @   hi_lib.js:440
$func67 @   hi_lib_bg.wasm:0x584d
$func66 @   hi_lib_bg.wasm:0x5709
$func61 @   hi_lib_bg.wasm:0x52af
$func103    @   hi_lib_bg.wasm:0x6cbc
$_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h51eff7df35816d6c   @   hi_lib_bg.wasm:0x7580
__wbg_adapter_16    @   hi_lib.js:202
real    @   hi_lib.js:187
Promise.then (async)        
imports.wbg.__wbg_then_8c2d62e8ae5978f7 @   hi_lib.js:444
$func44 @   hi_lib_bg.wasm:0x3723
$func45 @   hi_lib_bg.wasm:0x3913
$func37 @   hi_lib_bg.wasm:0x1ad6
$func48 @   hi_lib_bg.wasm:0x3fea
$_dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h51eff7df35816d6c   @   hi_lib_bg.wasm:0x7580
__wbg_adapter_16    @   hi_lib.js:202
real    @   hi_lib.js:187
Promise.then (async)        
imports.wbg.__wbg_then_2fcac196782070cc @   hi_lib.js:440
$func67 @   hi_lib_bg.wasm:0x584d
$func64 @   hi_lib_bg.wasm:0x5503
$func79 @   hi_lib_bg.wasm:0x6202
$wasm_bindgen__convert__closures__invoke2_mut__hc0a39dba83c8fc65    @   hi_lib_bg.wasm:0x7540
__wbg_adapter_55    @   hi_lib.js:296
cb0 @   hi_lib.js:424
imports.wbg.__wbg_new_b1d61b5687f5e73a  @   hi_lib.js:429
$func183    @   hi_lib_bg.wasm:0x77e4
$unred  @   hi_lib_bg.wasm:0x66a2
unred   @   hi_lib.js:226
run @   rust_html.html:16
await in run (async)        
(anonymous) @   rust_html.html:20

我認為它與異步代碼有關,但我不知道如何解決它。 我還試圖避免直接更改 javascript,因為該練習應該完全可以在 Rust 中完成。

編輯(1)在做了一些審查之后,我認為問題在於我將 Uint8Array 轉換為向量然后將其鉗位。 function 似乎需要一個鉗位數組。

編輯 (2)我注意到另一個錯誤,這次是 fetch_url_binary()。 Uint8Array 與 PNG 文件不匹配。 有 44750 個與每個相關聯的 4 個字節信息 (RGBA)。 Uint8Array 的大小為 6384。

您當前的代碼會產生IndexSizeError ,因為ImageData需要一個像素數組 colors(每像素 4 個字節,使用允許的像素表示之一),但您提供的是整個 PNG,例如還包括 png header,但隨后壓縮了一些memory 效率的像素值。 您將需要首先解碼您的 png,例如使用image-rs ,然后僅提供像素值。

下面是一個基本示例,如何使用image crate 做到這一點:

lib.rs

use image::GenericImageView;
use js_sys::Uint8Array;
use wasm_bindgen::{prelude::*, JsCast, Clamped};
use wasm_bindgen_futures::JsFuture;
use web_sys::ImageData;

#[wasm_bindgen]
pub async fn fetch_url_binary(url: String) -> Result<Uint8Array, JsValue> {
    let window = web_sys::window().unwrap(); // Browser window
    let promise = JsFuture::from(window.fetch_with_str(&url)); // File fetch promise
    let result = promise.await?; // Await fulfillment of fetch
    let response: web_sys::Response = result.dyn_into().unwrap(); // Type casting
    let image_data = JsFuture::from(response.array_buffer()?).await?; // Get text
    Ok(Uint8Array::new(&image_data))
}

#[wasm_bindgen]
pub async fn unred(url: String, canvas: String) -> Result<(), JsValue> {
    let binary = fetch_url_binary(url).await?;
    let altbuf = binary.to_vec();

    // Convert the png encoded bytes to an rgba pixel buffer (given the PNG is actually in 8byte RGBA format).
    let image = image::load_from_memory_with_format(&altbuf, image::ImageFormat::Png).unwrap();
    let mut rgba_image = image.to_rgba8();

    // I suppose this is what you tried to do in your original loop
    // judging by the function name:
    for (_, _, pixel) in rgba_image.enumerate_pixels_mut() {
        if pixel[0] > 0 {
            *pixel = image::Rgba([0, pixel[1], pixel[2], pixel[3]]);
        }
    }

    let window = web_sys::window().unwrap();
    let document = window.document().expect("Could not get document");
    let canvas = document
        .get_element_by_id(&canvas)
        .unwrap()
        .dyn_into::<web_sys::HtmlCanvasElement>()?;
    let context = canvas
        .get_context("2d")?
        .unwrap()
        .dyn_into::<web_sys::CanvasRenderingContext2d>()?;
    let clamped_buf: Clamped<&[u8]> = Clamped(rgba_image.as_raw());
    let image_data_temp = 
        ImageData::new_with_u8_clamped_array_and_sh(clamped_buf, image.width(), image.height())?;
    context.put_image_data(&image_data_temp, 0.0, 0.0)?;
    Ok(())
}

貨物.toml

[package]
name = "hi_lib"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
image = "0.23.14"
js-sys = "0.3.55"
wasm-bindgen = "0.2.78"
wasm-bindgen-futures = "0.4.28"
web-sys = {version = "0.3.55", features=[
    "CanvasRenderingContext2d",
    "Document",
    "HtmlCanvasElement",
    "ImageData",
    "Response",
    "Window"
]}

如果您提供了依賴項列表會很好,這樣您就可以節省嘗試測試您的代碼的人相當長的時間(尤其是在具有所有這些web-sys功能的情況下)。

為了完整起見(我沒有更改任何實質性內容) index.html

<!doctype html>
<html lang="en">

<head>
    <title>Unred image</title>
</head>

<body>
    <canvas id="myCanvas" width="1024" height="1024" style="border:1px solid #d3d3d3;">
        <script type="module">
            import init, { unred, fetch_url_binary } from './pkg/hi_lib.js';
            var result;
            async function run() {
                await init(); // Initialize module 
                unred("myPng.png", "myCanvas");
            }
            run(); // Execute async wrapper 
        </script>
</body>

</html>

以及我用於測試的圖像(PNG格式在這里很重要,所以編碼時一定要使用8位RGBA格式,或者相應地轉換圖像): 測試圖像

現在使用wasm-pack build --target web ,然后使用 http 服務器為 crates 的根目錄提供服務,這是 output: 在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM