Skip to content

Commit

Permalink
feat: Support incremental rendering in use_camera hook (#1034)
Browse files Browse the repository at this point in the history
  • Loading branch information
marc2332 authored Jan 9, 2025
1 parent 009a632 commit 0da8be0
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 31 deletions.
3 changes: 3 additions & 0 deletions crates/elements/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,9 @@ builder_constructors! {

// Image
image_data: String,

// Reference
reference: Reference,
image_reference: String,

// Accessibility
Expand Down
99 changes: 71 additions & 28 deletions crates/hooks/src/use_camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ use std::sync::{
Mutex,
};

use bytes::Bytes;
use dioxus_core::{
prelude::spawn,
use_hook,
AttributeValue,
};
use dioxus_hooks::use_signal;
use dioxus_hooks::{
to_owned,
use_effect,
use_reactive,
use_signal,
};
use dioxus_signals::{
CopyValue,
ReadOnlySignal,
Readable,
Signal,
Writable,
};
use freya_node_state::{
CustomAttributeValues,
ImageReference,
NodeReference,
};
pub use nokhwa::utils::{
CameraIndex,
Expand All @@ -29,9 +39,14 @@ use nokhwa::{
NokhwaError,
};

use crate::use_platform;
use crate::{
use_node,
use_node_with_reference,
use_platform,
};

/// Configuration for a camera
#[derive(Clone, PartialEq, Debug)]
pub struct CameraSettings {
camera_index: CameraIndex,
resolution: Option<Resolution>,
Expand Down Expand Up @@ -68,33 +83,59 @@ impl Default for CameraSettings {
}
}

/// Connect to a given camera and render its frames into an image element
pub fn use_camera(
camera_settings: CameraSettings,
) -> (AttributeValue, Signal<Option<NokhwaError>>) {
let platform = use_platform();
let mut camera_error = use_signal(|| None);
let image_reference = use_hook(|| Arc::new(Mutex::new(None)));
#[derive(Debug, Clone)]
pub struct UseCamera {
error: Signal<Option<NokhwaError>>,
node_reference: NodeReference,
image: Arc<Mutex<Option<Bytes>>>,
}

let image_reference_attr = AttributeValue::any_value(CustomAttributeValues::ImageReference(
ImageReference(image_reference.clone()),
));
impl UseCamera {
/// Get a [AttributeValue] for the `reference` attribute.
pub fn attribute(&self) -> AttributeValue {
AttributeValue::any_value(CustomAttributeValues::Reference(
self.node_reference.clone(),
))
}

use_hook(move || {
spawn(async move {
let mut handle_error = |e: NokhwaError| {
camera_error.set(Some(e));
};
/// Get a [AttributeValue] for the `image_reference` attribute.
pub fn image_attribute(&self) -> AttributeValue {
AttributeValue::any_value(CustomAttributeValues::ImageReference(ImageReference(
self.image.clone(),
)))
}

/// Get a [ReadOnlySignal] of the error.
pub fn error(&self) -> ReadOnlySignal<Option<NokhwaError>> {
self.error.into()
}
}

/// Connect to a given camera and render its frames into an image element
pub fn use_camera(camera_settings: CameraSettings) -> UseCamera {
let platform = use_platform();
let mut error = use_signal(|| None);
let image = use_hook(|| Arc::new(Mutex::new(None)));
let (node_reference, size) = use_node_with_reference();

let camera = UseCamera {
error,
image: image.clone(),
node_reference,
};

use_effect(use_reactive!(|camera_settings| {
to_owned![image];
spawn(async move {
let requested = RequestedFormat::new::<RgbFormat>(camera_settings.camera_format);
let camera = Camera::new(camera_settings.camera_index, requested);

if let Ok(mut camera) = camera {
// Set the custom resolution if specified
if let Some(resolution) = camera_settings.resolution {
camera
.set_resolution(resolution)
.unwrap_or_else(&mut handle_error);
if let Err(err) = camera.set_resolution(resolution) {
error.set(Some(err));
}
}

let mut ticker = platform.new_ticker();
Expand All @@ -107,22 +148,24 @@ pub fn use_camera(
let frame = camera.frame();

if let Ok(frame) = frame {
let bts = frame.buffer_bytes();
// Send the frame to the renderer via the image reference
image_reference.lock().unwrap().replace(bts);
let new_frame = frame.buffer_bytes();

// Replace the old frame with the new
image.lock().unwrap().replace(new_frame);

// Request the renderer to rerender
// Request a rerender
platform.invalidate_drawing_area(size.peek().area);
platform.request_animation_frame();
} else if let Err(err) = frame {
handle_error(err);
error.set(Some(err));
break;
}
}
} else if let Err(err) = camera {
handle_error(err);
error.set(Some(err));
}
});
});
}));

(image_reference_attr, camera_error)
camera
}
19 changes: 19 additions & 0 deletions crates/hooks/src/use_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,25 @@ pub fn use_node_signal_with_prev() -> (
)
}

pub fn use_node_with_reference() -> (NodeReference, ReadOnlySignal<NodeReferenceLayout>) {
let (tx, signal) = use_hook(|| {
let (tx, mut rx) = channel::<NodeReferenceLayout>(NodeReferenceLayout::default());
let mut signal = Signal::new(NodeReferenceLayout::default());

spawn(async move {
while rx.changed().await.is_ok() {
if *signal.peek() != *rx.borrow() {
signal.set(rx.borrow().clone());
}
}
});

(Arc::new(tx), signal)
});

(NodeReference(tx), signal.into())
}

#[cfg(test)]
mod test {
use freya::prelude::*;
Expand Down
7 changes: 4 additions & 3 deletions examples/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ fn main() {
}
#[cfg(feature = "use_camera")]
fn app() -> Element {
let (image_reference, camera_error) = use_camera(CameraSettings::default());
let image = use_camera(CameraSettings::default());

rsx!(
rect {
width: "100%",
height: "100%",
padding: "50",
if let Some(err) = &*camera_error.read() {
if let Some(err) = &*image.error().read() {
label {
color: "black",
"{err}"
Expand All @@ -33,7 +33,8 @@ fn app() -> Element {
image {
width: "100%",
height: "100%",
image_reference
reference: image.attribute(),
image_reference: image.image_attribute()
}
}
}
Expand Down

0 comments on commit 0da8be0

Please sign in to comment.