Skip to content

Commit

Permalink
Merge pull request #493 from Sharktheone/rendering/svg
Browse files Browse the repository at this point in the history
Implement SVG rendering
  • Loading branch information
Sharktheone committed Jun 25, 2024
2 parents 18f3e80 + 0d86229 commit 27241dc
Show file tree
Hide file tree
Showing 20 changed files with 937 additions and 96 deletions.
342 changes: 333 additions & 9 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/gosub_html5/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ pub mod parser;
pub mod tokenizer;
pub mod util;
pub mod visit;
pub mod writer;
4 changes: 4 additions & 0 deletions crates/gosub_html5/src/node/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ impl NodeArena {
self.nodes.insert(id, node);
id
}

pub fn nodes(&self) -> &HashMap<NodeId, Node> {
&self.nodes
}
}

impl Default for NodeArena {
Expand Down
4 changes: 4 additions & 0 deletions crates/gosub_html5/src/parser/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@ impl Document {

false
}

pub fn nodes(&self) -> &HashMap<NodeId, Node> {
self.arena.nodes()
}
}

// Walk the document tree with the given visitor
Expand Down
136 changes: 136 additions & 0 deletions crates/gosub_html5/src/writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::{
node::{Node, NodeData, NodeId},
parser::document::Document,
visit::Visitor,
};

impl Document {
pub fn write_document(&self) -> String {
Writer::write_from_node(NodeId::root(), self)
}

pub fn write_from_node(&self, node: NodeId) -> String {
Writer::write_from_node(node, self)
}
}

struct Writer {
buffer: String,
comments: bool,
}

impl Writer {
pub fn write_from_node(node: NodeId, doc: &Document) -> String {
let mut w = Self {
comments: false,
buffer: String::new(),
};

w.visit_node(node, doc);

w.buffer
}

pub fn visit_node(&mut self, id: NodeId, doc: &Document) {
let Some(node) = doc.get_node_by_id(id) else {
return;
};

match node.data {
NodeData::Document(ref data) => {
self.document_enter(node, data);

self.visit_children(&node.children, doc);

self.document_leave(node, data);
}

NodeData::DocType(ref data) => {
self.doctype_enter(node, data);

self.visit_children(&node.children, doc);

self.doctype_leave(node, data);
}

NodeData::Text(ref data) => {
self.text_enter(node, data);

self.visit_children(&node.children, doc);

self.text_leave(node, data);
}
NodeData::Comment(ref data) => {
self.comment_enter(node, data);

self.visit_children(&node.children, doc);

self.comment_leave(node, data);
}

NodeData::Element(ref data) => {
self.element_enter(node, data);

self.visit_children(&node.children, doc);

self.element_leave(node, data);
}
}
}

pub fn visit_children(&mut self, children: &Vec<NodeId>, doc: &Document) {
for child in children {
self.visit_node(*child, doc);
}
}
}

impl Visitor<Node> for Writer {
fn text_enter(&mut self, _node: &Node, data: &crate::node::data::text::TextData) {
self.buffer.push_str(&data.value);
}

fn text_leave(&mut self, _node: &Node, _data: &crate::node::data::text::TextData) {}

fn doctype_enter(&mut self, _node: &Node, data: &crate::node::data::doctype::DocTypeData) {
self.buffer.push_str("<!DOCTYPE ");
self.buffer.push_str(&data.name);
self.buffer.push('>');
}

fn doctype_leave(&mut self, _node: &Node, _data: &crate::node::data::doctype::DocTypeData) {}

fn comment_enter(&mut self, _node: &Node, data: &crate::node::data::comment::CommentData) {
if self.comments {
self.buffer.push_str("<!--");
self.buffer.push_str(&data.value);
self.buffer.push_str("-->");
}
}

fn comment_leave(&mut self, _node: &Node, _data: &crate::node::data::comment::CommentData) {}

fn element_enter(&mut self, _node: &Node, data: &crate::node::data::element::ElementData) {
self.buffer.push('<');
self.buffer.push_str(&data.name);
for (name, value) in &data.attributes {
self.buffer.push(' ');
self.buffer.push_str(name);
self.buffer.push_str("=\"");
self.buffer.push_str(value);
self.buffer.push('"');
}

self.buffer.push('>');
}

fn element_leave(&mut self, _node: &Node, data: &crate::node::data::element::ElementData) {
self.buffer.push_str("</");
self.buffer.push_str(&data.name);
self.buffer.push('>');
}

fn document_enter(&mut self, _node: &Node, _data: &crate::node::data::document::DocumentData) {}

fn document_leave(&mut self, _node: &Node, _data: &crate::node::data::document::DocumentData) {}
}
1 change: 1 addition & 0 deletions crates/gosub_render_backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ smallvec = "1.13.2"
image = "0.25.1"
raw-window-handle = "0.6.2"
gosub_shared = { path = "../gosub_shared" }
gosub_html5 = { path = "../gosub_html5" }

48 changes: 45 additions & 3 deletions crates/gosub_render_backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::fmt::Debug;
use std::ops::{Div, Mul, MulAssign};

use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use smallvec::SmallVec;

use gosub_shared::types::Result;

use gosub_shared::types::Size as SizeT;

use crate::svg::SvgRenderer;

pub mod svg;

pub type Size = SizeT<f32>;
pub type SizeU32 = SizeT<u32>;

Expand All @@ -28,6 +31,7 @@ pub trait RenderBackend: Sized + Debug {
type Image: Image;
type Brush: Brush<Self>;
type Scene: Scene<Self>;
type SVGRenderer: SvgRenderer<Self>;

type ActiveWindowData<'a>;
type WindowData<'a>;
Expand Down Expand Up @@ -583,7 +587,10 @@ pub trait Color {
pub trait Image {
fn new(size: (FP, FP), data: Vec<u8>) -> Self;

fn from_img(img: &image::DynamicImage) -> Self;
fn from_img(img: image::DynamicImage) -> Self;

fn width(&self) -> u32;
fn height(&self) -> u32;
}

pub trait Brush<B: RenderBackend>: Clone {
Expand All @@ -593,3 +600,38 @@ pub trait Brush<B: RenderBackend>: Clone {

fn image(image: B::Image) -> Self;
}

pub enum ImageBuffer<B: RenderBackend> {
Image(B::Image),
Scene(B::Scene, SizeU32),
}

impl<B: RenderBackend> ImageBuffer<B> {
pub fn width(&self) -> u32 {
match self {
ImageBuffer::Image(img) => img.width(),
ImageBuffer::Scene(_, size) => size.width,
}
}

pub fn height(&self) -> u32 {
match self {
ImageBuffer::Image(img) => img.height(),
ImageBuffer::Scene(_, size) => size.height,
}
}

pub fn size(&self) -> SizeU32 {
match self {
ImageBuffer::Image(img) => SizeU32::new(img.width(), img.height()),
ImageBuffer::Scene(_, size) => *size,
}
}

pub fn size_tuple(&self) -> (FP, FP) {
match self {
ImageBuffer::Image(img) => (img.width() as FP, img.height() as FP),
ImageBuffer::Scene(_, size) => (size.width as FP, size.height as FP),
}
}
}
13 changes: 11 additions & 2 deletions crates/gosub_render_backend/src/svg.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
use crate::RenderBackend;
use gosub_html5::node::NodeId;
use gosub_html5::parser::document::DocumentHandle;
use gosub_shared::types::Result;

use crate::{ImageBuffer, RenderBackend};

pub trait SvgRenderer<B: RenderBackend> {
type SvgDocument;

fn new(wd: &mut B::WindowData<'_>) -> Self;

pub trait SvgRenderer<B: RenderBackend> {
fn parse_external(data: String) -> Result<Self::SvgDocument>;
fn parse_internal(tree: DocumentHandle, id: NodeId) -> Result<Self::SvgDocument>;

fn render(&mut self, doc: &Self::SvgDocument) -> Result<ImageBuffer<B>>;
}
Loading

0 comments on commit 27241dc

Please sign in to comment.