diff options
author | Anthony Ramine <nox@nox.paris> | 2019-11-29 09:28:44 +0100 |
---|---|---|
committer | Simon Sapin <simon.sapin@exyr.org> | 2019-12-01 10:56:33 +0100 |
commit | 5a360ac63d374023884289ca86b7c2390394c533 (patch) | |
tree | ddd0f62d6628b36f44d857be64bfa14e8ea1564d /components/layout_2020 | |
parent | f24e8d0557c6cb51fac0a5e8ff4046ec6a1717d3 (diff) | |
download | servo-5a360ac63d374023884289ca86b7c2390394c533.tar.gz servo-5a360ac63d374023884289ca86b7c2390394c533.zip |
Start supporting images in layout 2020
Diffstat (limited to 'components/layout_2020')
-rw-r--r-- | components/layout_2020/Cargo.toml | 1 | ||||
-rw-r--r-- | components/layout_2020/display_list.rs | 28 | ||||
-rw-r--r-- | components/layout_2020/dom_traversal.rs | 33 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 139 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 12 | ||||
-rw-r--r-- | components/layout_2020/formatting_contexts.rs | 2 | ||||
-rw-r--r-- | components/layout_2020/fragments.rs | 9 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 71 |
8 files changed, 249 insertions, 46 deletions
diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 7bfe7cbea16..5a28da8d9ee 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -22,6 +22,7 @@ gfx_traits = {path = "../gfx_traits"} ipc-channel = "0.12" libc = "0.2" msg = {path = "../msg"} +net_traits = {path = "../net_traits"} range = {path = "../range"} rayon = "1" rayon_croissant = "0.1.1" diff --git a/components/layout_2020/display_list.rs b/components/layout_2020/display_list.rs index 5dc5ecb96ad..16592a8a5d0 100644 --- a/components/layout_2020/display_list.rs +++ b/components/layout_2020/display_list.rs @@ -78,6 +78,34 @@ impl Fragment { .wr .push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None); }, + Fragment::Image(i) => { + use style::computed_values::image_rendering::T as ImageRendering; + is_contentful.0 = true; + let rect = i + .content_rect + .to_physical(i.style.writing_mode(), containing_block) + .translate(&containing_block.top_left); + let common = CommonItemProperties { + clip_rect: rect.clone().into(), + clip_id: wr::ClipId::root(builder.pipeline_id), + spatial_id: wr::SpatialId::root_scroll_node(builder.pipeline_id), + hit_info: None, + // TODO(gw): Make use of the WR backface visibility functionality. + flags: PrimitiveFlags::default(), + }; + builder.wr.push_image( + &common, + rect.into(), + match i.style.get_inherited_box().image_rendering { + ImageRendering::Auto => wr::ImageRendering::Auto, + ImageRendering::CrispEdges => wr::ImageRendering::CrispEdges, + ImageRendering::Pixelated => wr::ImageRendering::Pixelated, + }, + wr::AlphaType::PremultipliedAlpha, + i.image_key, + wr::ColorF::WHITE, + ); + }, } } } diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 732dc1afdc9..ee368566bef 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -3,18 +3,22 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::element_data::{LayoutBox, LayoutDataForElement}; +use crate::geom::physical::Vec2; use crate::replaced::ReplacedContent; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside}; use crate::wrapper::GetRawData; use atomic_refcell::{AtomicRefCell, AtomicRefMut}; +use net_traits::image::base::{Image as NetImage, ImageMetadata}; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use servo_arc::Arc as ServoArc; use std::convert::TryInto; use std::marker::PhantomData as marker; +use std::sync::Arc; use style::context::SharedStyleContext; use style::dom::TNode; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; +use style::values::computed::Length; #[derive(Clone, Copy)] pub enum WhichPseudoElement { @@ -90,11 +94,12 @@ fn traverse_element<'dom, Node>( ) where Node: NodeExt<'dom>, { + let replaced = ReplacedContent::for_element(element); let style = element.style(context); match Display::from(style.get_box().display) { Display::None => element.unset_boxes_in_subtree(), Display::Contents => { - if ReplacedContent::for_element(element, context).is_some() { + if replaced.is_some() { // `display: content` on a replaced element computes to `display: none` // <https://drafts.csswg.org/css-display-3/#valdef-display-contents> element.unset_boxes_in_subtree() @@ -107,10 +112,7 @@ fn traverse_element<'dom, Node>( handler.handle_element( &style, display, - match ReplacedContent::for_element(element, context) { - Some(replaced) => Contents::Replaced(replaced), - None => Contents::OfElement(element), - }, + replaced.map_or(Contents::OfElement(element), Contents::Replaced), element.element_box_slot(), ); }, @@ -287,6 +289,7 @@ impl Drop for BoxSlot<'_> { pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync { fn is_element(self) -> bool; fn as_text(self) -> Option<String>; + fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)>; fn first_child(self) -> Option<Self>; fn next_sibling(self) -> Option<Self>; fn parent_node(self) -> Option<Self>; @@ -315,6 +318,26 @@ where } } + fn as_image(self) -> Option<(Option<Arc<NetImage>>, Vec2<Length>)> { + let node = self.to_threadsafe(); + let (resource, metadata) = node.image_data()?; + let (width, height) = resource + .as_ref() + .map(|image| (image.width, image.height)) + .or_else(|| metadata.map(|metadata| (metadata.width, metadata.height))) + .unwrap_or((0, 0)); + let (mut width, mut height) = (width as f32, height as f32); + if let Some(density) = node.image_density().filter(|density| *density != 1.) { + width = (width as f64 / density) as f32; + height = (height as f64 / density) as f32; + } + let size = Vec2 { + x: Length::new(width), + y: Length::new(height), + }; + Some((resource, size)) + } + fn first_child(self) -> Option<Self> { TNode::first_child(&self) } diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 5c2e3d8e988..4b887abde35 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -15,6 +15,7 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{ adjust_static_positions, AbsolutelyPositionedBox, AbsolutelyPositionedFragment, }; +use crate::replaced::ReplacedContent; use crate::style_ext::{ComputedValuesExt, Position}; use crate::{relative_adjustement, ContainingBlock}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; @@ -295,10 +296,12 @@ impl BlockLevelBox { )) }, BlockLevelBox::Independent(contents) => match contents.as_replaced() { - Ok(replaced) => { - // FIXME - match *replaced {} - }, + Ok(replaced) => Fragment::Box(layout_in_flow_replaced_block_level( + layout_context, + containing_block, + &contents.style, + replaced, + )), Err(non_replaced) => Fragment::Box(layout_in_flow_non_replaced_block_level( layout_context, containing_block, @@ -360,24 +363,15 @@ fn layout_in_flow_non_replaced_block_level<'a>( let box_size = style.box_size(); let inline_size = box_size.inline.percentage_relative_to(cbis); if let LengthOrAuto::LengthPercentage(is) = inline_size { - let inline_margins = cbis - is - pb.inline_sum(); - match ( - &mut computed_margin.inline_start, - &mut computed_margin.inline_end, - ) { - (s @ &mut LengthOrAuto::Auto, e @ &mut LengthOrAuto::Auto) => { - *s = LengthOrAuto::LengthPercentage(inline_margins / 2.); - *e = LengthOrAuto::LengthPercentage(inline_margins / 2.); - }, - (s @ &mut LengthOrAuto::Auto, &mut LengthOrAuto::LengthPercentage(e)) => { - *s = LengthOrAuto::LengthPercentage(inline_margins - e); - }, - (&mut LengthOrAuto::LengthPercentage(s), e) => { - // Either the inline-end margin is auto, - // or we’re over-constrained and we do as if it were. - *e = LengthOrAuto::LengthPercentage(inline_margins - s); - }, - } + let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level( + containing_block, + pb.inline_sum(), + computed_margin.inline_start, + computed_margin.inline_end, + is, + ); + computed_margin.inline_start = LengthOrAuto::LengthPercentage(margin_inline_start); + computed_margin.inline_end = LengthOrAuto::LengthPercentage(margin_inline_end); } let margin = computed_margin.auto_is(Length::zero); let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); @@ -476,3 +470,104 @@ fn layout_in_flow_non_replaced_block_level<'a>( block_margins_collapsed_with_children, } } + +/// https://drafts.csswg.org/css2/visudet.html#block-replaced-width +/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-width +/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height +fn layout_in_flow_replaced_block_level<'a>( + layout_context: &LayoutContext, + containing_block: &ContainingBlock, + style: &Arc<ComputedValues>, + replaced: &ReplacedContent, +) -> BoxFragment { + let cbis = containing_block.inline_size; + let padding = style.padding().percentages_relative_to(cbis); + let border = style.border_width(); + let computed_margin = style.margin().percentages_relative_to(cbis); + let pb = &padding + &border; + let mode = style.writing_mode(); + // FIXME(nox): We shouldn't pretend we always have a fully known intrinsic size. + let intrinsic_size = replaced.intrinsic_size.size_to_flow_relative(mode); + // FIXME(nox): This can divide by zero. + let intrinsic_ratio = intrinsic_size.inline.px() / intrinsic_size.block.px(); + let box_size = style.box_size(); + let inline_size = box_size.inline.percentage_relative_to(cbis); + let block_size = box_size + .block + .maybe_percentage_relative_to(containing_block.block_size.non_auto()); + let (inline_size, block_size) = match (inline_size, block_size) { + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::LengthPercentage(block)) => { + (inline, block) + }, + (LengthOrAuto::LengthPercentage(inline), LengthOrAuto::Auto) => { + (inline, inline / intrinsic_ratio) + }, + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(block)) => { + (block * intrinsic_ratio, block) + }, + (LengthOrAuto::Auto, LengthOrAuto::Auto) => (intrinsic_size.inline, intrinsic_size.block), + }; + let (margin_inline_start, margin_inline_end) = solve_inline_margins_for_in_flow_block_level( + containing_block, + pb.inline_sum(), + computed_margin.inline_start, + computed_margin.inline_end, + inline_size, + ); + let margin = Sides { + inline_start: margin_inline_start, + inline_end: margin_inline_end, + block_start: computed_margin.block_start.auto_is(Length::zero), + block_end: computed_margin.block_end.auto_is(Length::zero), + }; + let containing_block_for_children = ContainingBlock { + inline_size, + block_size: LengthOrAuto::LengthPercentage(block_size), + mode, + }; + // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + assert_eq!( + containing_block.mode, containing_block_for_children.mode, + "Mixed writing modes are not supported yet" + ); + let independent_layout = replaced.layout(layout_context, style, &containing_block_for_children); + let relative_adjustement = relative_adjustement( + style, + inline_size, + LengthOrAuto::LengthPercentage(block_size), + ); + let content_rect = Rect { + start_corner: Vec2 { + block: pb.block_start + relative_adjustement.block, + inline: pb.inline_start + relative_adjustement.inline + margin.inline_start, + }, + size: Vec2 { + block: block_size, + inline: inline_size, + }, + }; + BoxFragment { + style: style.clone(), + children: independent_layout.fragments, + content_rect, + padding, + border, + block_margins_collapsed_with_children: CollapsedBlockMargins::from_margin(&margin), + margin, + } +} + +fn solve_inline_margins_for_in_flow_block_level( + containing_block: &ContainingBlock, + padding_border_inline_sum: Length, + computed_margin_inline_start: LengthOrAuto, + computed_margin_inline_end: LengthOrAuto, + inline_size: Length, +) -> (Length, Length) { + let inline_margins = containing_block.inline_size - padding_border_inline_sum - inline_size; + match (computed_margin_inline_start, computed_margin_inline_end) { + (LengthOrAuto::Auto, LengthOrAuto::Auto) => (inline_margins / 2., inline_margins / 2.), + (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => (inline_margins - end, end), + (LengthOrAuto::LengthPercentage(start), _) => (start, inline_margins - start), + } +} diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 8df332b90d6..f4cb0abafb9 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -48,7 +48,7 @@ fn construct_for_root_element<'dom>( root_element: impl NodeExt<'dom>, ) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) { let style = root_element.style(context); - let replaced = ReplacedContent::for_element(root_element, context); + let replaced = ReplacedContent::for_element(root_element); let box_style = style.get_box(); let display_inside = match Display::from(box_style.display) { @@ -63,21 +63,13 @@ fn construct_for_root_element<'dom>( Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside, }; - if let Some(replaced) = replaced { - let _box = match replaced {}; - #[allow(unreachable_code)] - { - return (ContainsFloats::No, vec![Arc::new(_box)]); - } - } - let position = box_style.position; let float = box_style.float; let contents = IndependentFormattingContext::construct( context, style, display_inside, - Contents::OfElement(root_element), + replaced.map_or(Contents::OfElement(root_element), Contents::Replaced), ); if position.is_absolutely_positioned() { ( diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 96294947720..06e863da1b1 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -83,7 +83,7 @@ impl IndependentFormattingContext { absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>, ) -> IndependentLayout { match self.as_replaced() { - Ok(replaced) => match *replaced {}, + Ok(replaced) => replaced.layout(layout_context, &self.style, containing_block), Err(ifc) => ifc.layout( layout_context, containing_block, diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index a0ea097abb6..e0c335eb3c9 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -10,12 +10,13 @@ use std::sync::Arc; use style::properties::ComputedValues; use style::values::computed::Length; use style::Zero; -use webrender_api::FontInstanceKey; +use webrender_api::{FontInstanceKey, ImageKey}; pub(crate) enum Fragment { Box(BoxFragment), Anonymous(AnonymousFragment), Text(TextFragment), + Image(ImageFragment), } pub(crate) struct BoxFragment { @@ -61,6 +62,12 @@ pub(crate) struct TextFragment { pub glyphs: Vec<Arc<GlyphStore>>, } +pub(crate) struct ImageFragment { + pub style: ServoArc<ComputedValues>, + pub content_rect: Rect<Length>, + pub image_key: ImageKey, +} + impl AnonymousFragment { pub fn no_op(mode: (WritingMode, Direction)) -> Self { Self { diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 04defc26b02..bb003f613fa 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -2,20 +2,77 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::context::LayoutContext; use crate::dom_traversal::NodeExt; +use crate::formatting_contexts::IndependentLayout; +use crate::fragments::{Fragment, ImageFragment}; +use crate::geom::{flow_relative, physical}; +use crate::positioned::AbsolutelyPositionedFragment; +use crate::ContainingBlock; +use net_traits::image::base::Image; +use servo_arc::Arc as ServoArc; +use std::sync::Arc; use style::context::SharedStyleContext; +use style::properties::ComputedValues; +use style::values::computed::Length; +use style::Zero; #[derive(Debug)] -pub(super) enum ReplacedContent { - // Not implemented yet +pub(crate) struct ReplacedContent { + pub kind: ReplacedContentKind, + pub intrinsic_size: physical::Vec2<Length>, +} + +#[derive(Debug)] +pub(crate) enum ReplacedContentKind { + Image(Option<Arc<Image>>), } impl ReplacedContent { - pub fn for_element<'dom, Node>(element: Node, _context: &SharedStyleContext) -> Option<Self> - where - Node: NodeExt<'dom>, - { - // FIXME: implement <img> etc. + pub fn for_element<'dom>(element: impl NodeExt<'dom>) -> Option<Self> { + if let Some((image, intrinsic_size)) = element.as_image() { + return Some(Self { + kind: ReplacedContentKind::Image(image), + intrinsic_size, + }); + } None } + + pub fn layout<'a>( + &'a self, + layout_context: &LayoutContext, + style: &ServoArc<ComputedValues>, + containing_block: &ContainingBlock, + ) -> IndependentLayout { + let (fragments, content_block_size) = match self.kind { + ReplacedContentKind::Image(ref image) => { + // FIXME(nox): We should not assume block size is known. + let block_size = containing_block.block_size.non_auto().unwrap(); + let fragments = image + .as_ref() + .and_then(|image| image.id) + .map(|image_key| { + Fragment::Image(ImageFragment { + style: style.clone(), + content_rect: flow_relative::Rect { + start_corner: flow_relative::Vec2::zero(), + size: flow_relative::Vec2 { + inline: containing_block.inline_size, + block: block_size, + }, + }, + image_key, + }) + }) + .into_iter() + .collect::<Vec<_>>(); + (fragments, block_size) + }, + }; + IndependentLayout { + fragments, + content_block_size, + } + } } |