Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial background image support #158

Merged
merged 8 commits into from
Nov 19, 2024
53 changes: 47 additions & 6 deletions packages/blitz-dom/src/document.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::events::{EventData, HitResult, RendererEvent};
use crate::node::{ImageData, NodeSpecificData, TextBrush};
use crate::node::{ImageData, NodeSpecificData, Status, TextBrush};
use crate::util::ImageType;
use crate::{ElementNodeData, Node, NodeData, TextNodeData, Viewport};
use app_units::Au;
use blitz_traits::net::{DummyProvider, SharedProvider};
use html5ever::local_name;
use parley::{FontContext, PlainEditorOp};
use peniko::kurbo;
Expand All @@ -17,6 +19,7 @@ use slab::Slab;
use std::any::Any;
use std::collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use style::selector_parser::ServoElementSnapshot;
use style::servo::media_queries::FontMetricsProvider;
use style::servo_arc::Arc as ServoArc;
Expand Down Expand Up @@ -125,6 +128,9 @@ pub struct Document {
pub(crate) focus_node_id: Option<usize>,

pub changed: HashSet<usize>,

/// Network provider. Can be used to fetch assets.
pub net_provider: SharedProvider<Resource>,
}

impl DocumentLike for Document {
Expand Down Expand Up @@ -428,6 +434,7 @@ impl Document {
hover_node_id: None,
focus_node_id: None,
changed: HashSet::new(),
net_provider: Arc::new(DummyProvider::default()),
};

// Initialise document with root Document node
Expand All @@ -436,6 +443,11 @@ impl Document {
doc
}

/// Set the Document's networking provider
pub fn set_net_provider(&mut self, net_provider: SharedProvider<Resource>) {
self.net_provider = net_provider;
}

/// Set base url for resolving linked resources (stylesheets, images, fonts, etc)
pub fn set_base_url(&mut self, url: &str) {
self.base_url = Some(Url::parse(url).unwrap());
Expand Down Expand Up @@ -755,14 +767,43 @@ impl Document {
Resource::Css(node_id, css) => {
self.add_stylesheet_for_node(css, node_id);
}
Resource::Image(node_id, image) => {
Resource::Image(node_id, kind, image) => {
let node = self.get_node_mut(node_id).unwrap();
node.element_data_mut().unwrap().node_specific_data =
NodeSpecificData::Image(ImageData::new(image))

match kind {
ImageType::Image => {
node.element_data_mut().unwrap().node_specific_data =
NodeSpecificData::Image(ImageData::from(image))
}
ImageType::Background(idx) => {
if let Some(Some(bg_image)) = node
.element_data_mut()
.and_then(|el| el.background_images.get_mut(idx))
{
bg_image.status = Status::Ok;
bg_image.image = ImageData::from(image);
}
}
}
}
Resource::Svg(node_id, tree) => {
Resource::Svg(node_id, kind, tree) => {
let node = self.get_node_mut(node_id).unwrap();
node.element_data_mut().unwrap().node_specific_data = NodeSpecificData::Svg(*tree)

match kind {
ImageType::Image => {
node.element_data_mut().unwrap().node_specific_data =
NodeSpecificData::Image(ImageData::Svg(*tree));
}
ImageType::Background(idx) => {
if let Some(Some(bg_image)) = node
.element_data_mut()
.and_then(|el| el.background_images.get_mut(idx))
{
bg_image.status = Status::Ok;
bg_image.image = ImageData::Svg(*tree);
}
}
}
}
Resource::Font(bytes) => {
self.font_ctx.collection.register_fonts(bytes.to_vec());
Expand Down
15 changes: 9 additions & 6 deletions packages/blitz-dom/src/html_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,28 @@ impl HtmlDocument {
) -> Self {
// Spin up the virtualdom and include the default stylesheet
let viewport = Viewport::new(0, 0, 1.0);
let mut dom = match font_ctx {
let mut doc = match font_ctx {
Some(font_ctx) => Document::with_font_ctx(viewport, font_ctx),
None => Document::new(viewport),
};

// Set base url if configured
if let Some(url) = &base_url {
dom.set_base_url(url);
doc.set_base_url(url);
}

// Set the net provider
doc.set_net_provider(net_provider.clone());

// Include default and user-specified stylesheets
dom.add_user_agent_stylesheet(DEFAULT_CSS);
doc.add_user_agent_stylesheet(DEFAULT_CSS);
for ss in &stylesheets {
dom.add_user_agent_stylesheet(ss);
doc.add_user_agent_stylesheet(ss);
}

// Parse HTML string into document
DocumentHtmlParser::parse_into_doc(&mut dom, html, net_provider);
DocumentHtmlParser::parse_into_doc(&mut doc, html, net_provider);

HtmlDocument { inner: dom }
HtmlDocument { inner: doc }
}
}
7 changes: 5 additions & 2 deletions packages/blitz-dom/src/htmlsink.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::net::{CssHandler, ImageHandler, Resource};
use crate::util::ImageType;
use std::borrow::Cow;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::HashSet;
Expand Down Expand Up @@ -131,8 +132,10 @@ impl DocumentHtmlParser<'_> {
if let Some(raw_src) = node.attr(local_name!("src")) {
if !raw_src.is_empty() {
let src = self.doc.borrow().resolve_url(raw_src);
self.net_provider
.fetch(src, Box::new(ImageHandler(target_id)));
self.net_provider.fetch(
src,
Box::new(ImageHandler::new(target_id, ImageType::Image)),
);
}
}
}
Expand Down
71 changes: 55 additions & 16 deletions packages/blitz-dom/src/layout/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use style::{

use crate::{
node::{
ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData, TextBrush,
TextInputData, TextLayout,
ImageData, ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData,
TextBrush, TextInputData, TextLayout,
},
stylo_to_parley,
util::parse_svg,
Expand All @@ -35,6 +35,16 @@ const DUMMY_NAME: QualName = QualName {
local: local_name!("div"),
};

fn push_children_and_pseudos(layout_children: &mut Vec<usize>, node: &Node) {
if let Some(before) = node.before {
layout_children.push(before);
}
layout_children.extend_from_slice(&node.children);
if let Some(after) = node.after {
layout_children.push(after);
}
}

pub(crate) fn collect_layout_children(
doc: &mut Document,
container_node_id: usize,
Expand Down Expand Up @@ -82,7 +92,7 @@ pub(crate) fn collect_layout_children(
.unwrap()
.element_data_mut()
.unwrap()
.node_specific_data = NodeSpecificData::Svg(svg);
.node_specific_data = NodeSpecificData::Image(ImageData::Svg(svg));
}
Err(err) => {
println!("{} SVG parse failed", container_node_id);
Expand Down Expand Up @@ -173,13 +183,20 @@ pub(crate) fn collect_layout_children(
.downcast_element_mut()
.unwrap()
.inline_layout_data = Some(Box::new(inline_layout));
return layout_children.extend_from_slice(&ilayout_children);
if let Some(before) = doc.nodes[container_node_id].before {
layout_children.push(before);
}
layout_children.extend_from_slice(&ilayout_children);
if let Some(after) = doc.nodes[container_node_id].after {
layout_children.push(after);
}
return;
}

// If the children are either all inline or all block then simply return the regular children
// as the layout children
if (all_block | all_inline) & !has_contents {
return layout_children.extend_from_slice(&doc.nodes[container_node_id].children);
return push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
}

fn block_item_needs_wrap(
Expand Down Expand Up @@ -210,7 +227,7 @@ pub(crate) fn collect_layout_children(
});

if !has_text_node_or_contents {
return layout_children.extend_from_slice(&doc.nodes[container_node_id].children);
return push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
}

fn flex_or_grid_item_needs_wrap(
Expand All @@ -237,18 +254,18 @@ pub(crate) fn collect_layout_children(
.downcast_element_mut()
.unwrap()
.node_specific_data = NodeSpecificData::TableRoot(Arc::new(table_context));
layout_children.extend_from_slice(&tlayout_children);
}

_ => {
if let Some(before) = doc.nodes[container_node_id].before {
layout_children.push(before);
}
layout_children.extend_from_slice(&doc.nodes[container_node_id].children);
layout_children.extend_from_slice(&tlayout_children);
if let Some(after) = doc.nodes[container_node_id].after {
layout_children.push(after);
}
}

_ => {
push_children_and_pseudos(layout_children, &doc.nodes[container_node_id]);
}
}
}

Expand Down Expand Up @@ -277,14 +294,14 @@ fn flush_pseudo_elements(doc: &mut Document, node_id: usize) {
(1, before_style, before_node_id),
(0, after_style, after_node_id),
] {
// Delete psuedo element if it exists
if let Some(pe_node_id) = pe_node_id {
// Delete psuedo element if it exists but shouldn't
if let (Some(pe_node_id), None) = (pe_node_id, &pe_style) {
doc.remove_and_drop_node(pe_node_id);
doc.nodes[node_id].set_pe_by_index(idx, None);
}

// (Re)create pseudo element if it should exist
if let Some(pe_style) = pe_style {
// Create pseudo element if it should exist but doesn't
if let (None, Some(pe_style)) = (pe_node_id, &pe_style) {
let new_node_id = doc.create_node(NodeData::AnonymousBlock(ElementNodeData::new(
DUMMY_NAME,
Vec::new(),
Expand All @@ -305,12 +322,34 @@ fn flush_pseudo_elements(doc: &mut Document, node_id: usize) {
}

let mut element_data = ElementData::default();
element_data.styles.primary = Some(pe_style);
element_data.styles.primary = Some(pe_style.clone());
element_data.set_restyled();
*doc.nodes[new_node_id].stylo_element_data.borrow_mut() = Some(element_data);

doc.nodes[node_id].set_pe_by_index(idx, Some(new_node_id));
}

// Else: Update psuedo element
if let (Some(pe_node_id), Some(pe_style)) = (pe_node_id, pe_style) {
// TODO: Update content

let mut node_styles = doc.nodes[pe_node_id].stylo_element_data.borrow_mut();

if &**(*node_styles)
.as_ref()
.unwrap()
.styles
.primary
.as_ref()
.unwrap() as *const _
!= &*pe_style as *const _
{
let mut element_data = ElementData::default();
element_data.styles.primary = Some(pe_style);
element_data.set_restyled();
*node_styles = Some(element_data);
}
}
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/blitz-dom/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! However, in Blitz, we do a style pass then a layout pass.
//! This is slower, yes, but happens fast enough that it's not a huge issue.

use crate::node::{NodeData, NodeKind, NodeSpecificData};
use crate::node::{ImageData, NodeData, NodeKind, NodeSpecificData};
use crate::{
document::Document,
image::{image_measure_function, ImageContext},
Expand Down Expand Up @@ -223,11 +223,11 @@ impl LayoutPartialTree for Document {

// Get image's native size
let inherent_size = match &element_data.node_specific_data {
NodeSpecificData::Image(data) => taffy::Size {
NodeSpecificData::Image(ImageData::Raster(data)) => taffy::Size {
width: data.image.width() as f32,
height: data.image.height() as f32,
},
NodeSpecificData::Svg(svg) => {
NodeSpecificData::Image(ImageData::Svg(svg)) => {
let size = svg.size();
taffy::Size {
width: size.width(),
Expand All @@ -238,7 +238,6 @@ impl LayoutPartialTree for Document {
width: 0.0,
height: 0.0,
},

_ => unreachable!(),
};

Expand Down
17 changes: 11 additions & 6 deletions packages/blitz-dom/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ use blitz_traits::net::{Bytes, RequestHandler, SharedCallback, SharedProvider};
use url::Url;
use usvg::Tree;

use crate::util::parse_svg;
use crate::util::{parse_svg, ImageType};

#[derive(Clone, Debug)]
pub enum Resource {
Image(usize, Arc<DynamicImage>),
Svg(usize, Box<Tree>),
Image(usize, ImageType, Arc<DynamicImage>),
Svg(usize, ImageType, Box<Tree>),
Css(usize, DocumentStyleSheet),
Font(Bytes),
}
Expand Down Expand Up @@ -238,7 +238,12 @@ fn fetch_font_face(
});
}

pub(crate) struct ImageHandler(pub usize);
pub(crate) struct ImageHandler(usize, ImageType);
impl ImageHandler {
pub(crate) fn new(node_id: usize, kind: ImageType) -> Self {
Self(node_id, kind)
}
}
impl RequestHandler for ImageHandler {
type Data = Resource;
fn bytes(self: Box<Self>, bytes: Bytes, callback: SharedCallback<Resource>) {
Expand All @@ -248,13 +253,13 @@ impl RequestHandler for ImageHandler {
.expect("IO errors impossible with Cursor")
.decode()
{
callback.call(Resource::Image(self.0, Arc::new(image)));
callback.call(Resource::Image(self.0, self.1, Arc::new(image)));
return;
};

// Try parse SVG
const DUMMY_SVG : &[u8] = r#"<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>"#.as_bytes();
let tree = parse_svg(&bytes).unwrap_or(parse_svg(DUMMY_SVG).unwrap());
callback.call(Resource::Svg(self.0, Box::new(tree)));
callback.call(Resource::Svg(self.0, self.1, Box::new(tree)));
}
}
Loading