add image node

This commit is contained in:
Daniel Bulant 2024-02-20 22:21:50 +01:00
parent f7ad523459
commit 866a526605
3 changed files with 62 additions and 17 deletions

View file

@ -1,16 +1,27 @@
use std::fmt::Debug; use std::{fmt::Debug, mem, path::PathBuf};
use femtovg::{ImageId, Paint, Path}; use femtovg::{Color, ErrorKind, ImageFlags, ImageId, Paint, Path};
use crate::{events::handler::EventHandlerDatabase, SharedNode, WeakNode}; use crate::{events::handler::EventHandlerDatabase, SharedNode, WeakNode};
use super::{Node, NodeChildren, Style}; use super::{Node, NodeChildren, Style};
#[derive(Debug)] #[derive(Debug)]
/// Simple image node. /// Status of the image - when rendering, image node attempts to load the image and sets this status accordingly.
/// This is basically just a wrapper around Rectangle node with a fill of type Paint::image. /// Changes this if you want to change the image. If the previous status was loaded, free the image.
/// Use that if you need more options. /// In case the loading fails, image load status changes to Error and the node doesn't render.
pub enum ImageLoad {
LoadFile(PathBuf, ImageFlags),
// LoadArray(&[u8]),
LoadVec(Vec<u8>, ImageFlags),
Loaded(ImageId),
Error(ErrorKind)
}
#[derive(Debug)]
/// Image node.
/// Sadly doesn't implement `Default` because of the `ImageLoad` enum.
pub struct Image { pub struct Image {
pub style: Style, pub style: Style,
/// The image to be rendered. You are responsible for freeing the image data. /// The image to be rendered.
pub image: ImageId, pub image: ImageLoad,
/// Image width - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image /// Image width - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image
pub width: f32, pub width: f32,
/// Image height - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image /// Image height - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image
@ -30,7 +41,33 @@ impl Node for Image {
None None
} }
fn render_pre_children(&self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) { fn render_pre_children(&mut self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) {
match &self.image {
ImageLoad::LoadFile(_, _) => {
let image = mem::replace(&mut self.image, ImageLoad::Error(ErrorKind::UnknownError));
if let ImageLoad::LoadFile(path, flags) = image {
match context.canvas.load_image_file(path, flags) {
Ok(image) => {
self.image = ImageLoad::Loaded(image);
},
Err(e) => {
self.image = ImageLoad::Error(e);
}
}
}
},
ImageLoad::LoadVec(data, flags) => {
match context.canvas.load_image_mem(data, *flags) {
Ok(image) => {
self.image = ImageLoad::Loaded(image);
},
Err(e) => {
self.image = ImageLoad::Error(e);
}
}
},
_ => {}
}
let mut path = Path::new(); let mut path = Path::new();
path.rounded_rect( path.rounded_rect(
0., 0.,
@ -39,10 +76,18 @@ impl Node for Image {
layout.size.height, layout.size.height,
self.radius self.radius
); );
context.canvas.fill_path( match &self.image {
&path, ImageLoad::Loaded(image) => {
&Paint::image(self.image, 0., 0., self.width, self.height, 0., 1.) context.canvas.fill_path(
); &path,
&Paint::image(*image, 0., 0., self.width, self.height, 0., 1.)
);
},
ImageLoad::Error(_) => {
context.canvas.fill_path(&path, &Paint::color(Color::rgb(255, 0, 0)))
},
_ => unreachable!("We just loaded the image before, so it's either loaded or errored out.")
}
} }
fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> { fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> {

View file

@ -110,10 +110,10 @@ pub trait Node: Debug {
/// Render the node, called before rendering it's children /// Render the node, called before rendering it's children
/// Canvas considers 0, 0 to be top left corner (for location after layouting happens) /// Canvas considers 0, 0 to be top left corner (for location after layouting happens)
fn render_pre_children(&self, _context: &mut RenderContext, _layout: Layout) {} fn render_pre_children(&mut self, _context: &mut RenderContext, _layout: Layout) {}
/// Render the node, called after rendering it's children /// Render the node, called after rendering it's children
/// Canvas considers 0, 0 to be top left corner (for location after layouting happens) /// Canvas considers 0, 0 to be top left corner (for location after layouting happens)
fn render_post_children(&self, _context: &mut RenderContext, _layout: Layout) {} fn render_post_children(&mut self, _context: &mut RenderContext, _layout: Layout) {}
/// Sets the parent node. /// Sets the parent node.
/// May be called multiple times with the same value. /// May be called multiple times with the same value.
@ -340,12 +340,12 @@ pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext)
} }
} }
drop(read_node); drop(read_node);
sself.read().unwrap().render_pre_children(context, layout); sself.write().unwrap().render_pre_children(context, layout);
if let Some(children) = sself.read().unwrap().children() { if let Some(children) = sself.read().unwrap().children() {
for child in children { for child in children {
render_recursively(child, context); render_recursively(child, context);
} }
} }
sself.read().unwrap().render_post_children(context, layout); sself.write().unwrap().render_post_children(context, layout);
context.canvas.restore(); context.canvas.restore();
} }

View file

@ -30,7 +30,7 @@ impl Node for Rectangle {
fn children(&self) -> Option<&NodeChildren> { fn children(&self) -> Option<&NodeChildren> {
None None
} }
fn render_pre_children(&self, context: &mut RenderContext, layout: Layout) { fn render_pre_children(&mut self, context: &mut RenderContext, layout: Layout) {
let mut path = Path::new(); let mut path = Path::new();
path.rounded_rect( path.rounded_rect(
0., 0.,