From 9c5a59504460cd291f21fc5db747bbf8cefe84eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Dec 2019 22:11:54 +0100 Subject: [PATCH] Add intrinsic size computation for inline formatting contexts --- components/layout_2020/flow/construct.rs | 1 + components/layout_2020/flow/inline.rs | 172 +++++++++++++++--- components/layout_2020/intrinsic.rs | 48 ++--- components/layout_2020/lib.rs | 1 + components/style/values/computed/length.rs | 6 + .../style/values/computed/percentage.rs | 6 + 6 files changed, 188 insertions(+), 46 deletions(-) diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index e7599bc843a..5ac85dd47d9 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -127,6 +127,7 @@ impl BlockContainer { context: &SharedStyleContext<'style>, block_container_style: &Arc, contents: NonReplacedContents>, + // intrinsic_sizes_requested: bool, ) -> (BlockContainer, ContainsFloats) { let mut builder = BlockContainerBuilder { context, diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 33d1d8a0ed9..ccb87ff876a 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -9,13 +9,17 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::CollapsedBlockMargins; use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; +use crate::intrinsic::{outer_intrinsic_inline_sizes, IntrinsicSizes}; use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::{relative_adjustement, ContainingBlock}; +use app_units::Au; +use gfx::text::text_run::GlyphRun; use servo_arc::Arc; use style::properties::ComputedValues; -use style::values::computed::Length; +use style::values::computed::{Length, Percentage}; use style::Zero; +use webrender_api::FontInstanceKey; #[derive(Debug, Default)] pub(crate) struct InlineFormattingContext { @@ -77,6 +81,119 @@ struct LinesBoxes { } impl InlineFormattingContext { + // This works on an already-constructed `InlineFormattingContext`, + // Which would have to change if/when + // `BlockContainer::construct` parallelize their construction. + #[allow(unused)] + pub(super) fn intrinsic_sizes(&self, layout_context: &LayoutContext) -> IntrinsicSizes { + struct Computation { + paragraph: IntrinsicSizes, + current_line: IntrinsicSizes, + current_line_percentages: Percentage, + } + impl Computation { + fn traverse( + &mut self, + layout_context: &LayoutContext, + inline_level_boxes: &[Arc], + ) { + for inline_level_box in inline_level_boxes { + match &**inline_level_box { + InlineLevelBox::InlineBox(inline_box) => { + let padding = inline_box.style.padding(); + let border = inline_box.style.border_width(); + let margin = inline_box.style.margin(); + macro_rules! add_length { + ($x: expr) => {{ + let length = $x; + self.current_line.min_content += length; + self.current_line.max_content += length; + }}; + } + macro_rules! add_lengthpercentage { + ($x: expr) => {{ + add_length!($x.length_component()); + self.current_line_percentages += $x.percentage_component(); + }}; + } + macro_rules! add { + ($condition: ident, $side: ident) => { + if inline_box.$condition { + add_lengthpercentage!(padding.$side); + add_length!(border.$side); + margin.$side.non_auto().map(|x| add_lengthpercentage!(x)); + } + }; + } + + add!(first_fragment, inline_start); + self.traverse(layout_context, &inline_box.children); + add!(last_fragment, inline_end); + }, + InlineLevelBox::TextRun(text_run) => { + let (_, _, _, runs, break_at_start) = + text_run.break_and_shape(layout_context); + if break_at_start { + self.line_break_opportunity() + } + for run in &runs { + let advance = Length::from(run.glyph_store.total_advance()); + if run.glyph_store.is_whitespace() { + self.line_break_opportunity() + } else { + self.current_line.min_content += advance + } + self.current_line.max_content += advance + } + }, + InlineLevelBox::Atomic(atomic) => { + let inner = || { + // atomic + // .intrinsic_inline_sizes + // .as_ref() + // .expect("Accessing intrinsic size that was not requested") + // .clone() + todo!() + }; + let (outer, pc) = outer_intrinsic_inline_sizes(&atomic.style, &inner); + self.current_line.min_content += outer.min_content; + self.current_line.max_content += outer.max_content; + self.current_line_percentages += pc; + }, + InlineLevelBox::OutOfFlowFloatBox(_) | + InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {}, + } + } + } + + fn line_break_opportunity(&mut self) { + self.paragraph + .min_content + .max_assign(take(&mut self.current_line.min_content)); + } + + fn forced_line_break(&mut self) { + self.line_break_opportunity(); + self.current_line + .adjust_for_pbm_percentages(take(&mut self.current_line_percentages)); + self.paragraph + .max_content + .max_assign(take(&mut self.current_line.max_content)); + } + } + fn take(x: &mut T) -> T { + std::mem::replace(x, T::zero()) + } + let mut computation = Computation { + paragraph: IntrinsicSizes::zero(), + current_line: IntrinsicSizes::zero(), + current_line_percentages: Percentage::zero(), + }; + computation.traverse(layout_context, &self.inline_level_boxes); + computation.forced_line_break(); + computation.paragraph + } + pub(super) fn layout<'a>( &'a self, layout_context: &LayoutContext, @@ -283,11 +400,13 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { } impl TextRun { - fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { + fn break_and_shape( + &self, + layout_context: &LayoutContext, + ) -> (Au, Au, FontInstanceKey, Vec, bool) { use gfx::font::ShapingFlags; use style::computed_values::text_rendering::T as TextRendering; use style::computed_values::word_break::T as WordBreak; - use style::values::generics::text::LineHeight; let font_style = self.parent_style.clone_font(); let inherited_text_style = self.parent_style.get_inherited_text(); @@ -316,30 +435,35 @@ impl TextRun { flags, }; - let (font_ascent, font_line_gap, font_key, runs) = - crate::context::with_thread_local_font_context(layout_context, |font_context| { - let font_group = font_context.font_group(font_style); - let font = font_group - .borrow_mut() - .first(font_context) - .expect("could not find font"); - let mut font = font.borrow_mut(); + crate::context::with_thread_local_font_context(layout_context, |font_context| { + let font_group = font_context.font_group(font_style); + let font = font_group + .borrow_mut() + .first(font_context) + .expect("could not find font"); + let mut font = font.borrow_mut(); - let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape( - &mut font, - &self.text, - &shaping_options, - &mut None, - ); + let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape( + &mut font, + &self.text, + &shaping_options, + &mut None, + ); - ( - font.metrics.ascent, - font.metrics.line_gap, - font.font_key, - runs, - ) - }); + ( + font.metrics.ascent, + font.metrics.line_gap, + font.font_key, + runs, + break_at_start, + ) + }) + } + fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) { + use style::values::generics::text::LineHeight; + + let (font_ascent, font_line_gap, font_key, runs, _) = self.break_and_shape(layout_context); let font_size = self.parent_style.get_font().font_size.size.0; let mut runs = runs.iter(); loop { diff --git a/components/layout_2020/intrinsic.rs b/components/layout_2020/intrinsic.rs index 1b811928f86..e8df63ba630 100644 --- a/components/layout_2020/intrinsic.rs +++ b/components/layout_2020/intrinsic.rs @@ -1,7 +1,7 @@ use crate::style_ext::ComputedValuesExt; -use style::Zero; use style::properties::ComputedValues; -use style::values::computed::{Length, LengthPercentage}; +use style::values::computed::{Length, LengthPercentage, Percentage}; +use style::Zero; #[derive(Debug, Clone)] pub(crate) struct IntrinsicSizes { @@ -16,13 +16,26 @@ impl IntrinsicSizes { max_content: Length::zero(), } } + + /// Relevant to outer intrinsic inline sizes, for percentages from padding and margin. + pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) { + // " Note that this may yield an infinite result, but undefined results + // (zero divided by zero) must be treated as zero. " + if self.max_content.px() == 0. { + // Avoid a potential `NaN`. + // Zero is already the result we want regardless of `denominator`. + } else { + let denominator = (1. - percentages.0).max(0.); + self.max_content = Length::new(self.max_content.px() / denominator); + } + } } /// https://dbaron.org/css/intrinsic/#outer-intrinsic pub(crate) fn outer_intrinsic_inline_sizes( style: &ComputedValues, get_inner_intrinsic_sizes: &dyn Fn() -> IntrinsicSizes, -) -> IntrinsicSizes { +) -> (IntrinsicSizes, Percentage) { // FIXME: account for 'min-width', 'max-width', 'box-sizing' let specified = style.box_size().inline; @@ -34,36 +47,27 @@ pub(crate) fn outer_intrinsic_inline_sizes( Some(length) => IntrinsicSizes { min_content: length, max_content: length, - } + }, }; - let mut bpm_lengths = Length::zero(); - let mut bpm_percentages = 0.; + let mut pbm_lengths = Length::zero(); + let mut pbm_percentages = Percentage::zero(); let padding = style.padding(); let border = style.border_width(); let margin = style.margin(); - bpm_lengths += border.inline_start; - bpm_lengths += border.inline_end; + pbm_lengths += border.inline_start; + pbm_lengths += border.inline_end; let mut add = |x: LengthPercentage| { - bpm_lengths += x.length_component(); - bpm_percentages += x.percentage(); + pbm_lengths += x.length_component(); + pbm_percentages += x.percentage_component(); }; add(padding.inline_start); add(padding.inline_end); margin.inline_start.non_auto().map(&mut add); margin.inline_end.non_auto().map(&mut add); - outer.min_content += bpm_lengths; - outer.max_content += bpm_lengths; - // " Note that this may yield an infinite result, but undefined results - // (zero divided by zero) must be treated as zero. " - if outer.max_content.px() == 0. { - // Avoid a potential `NaN`. - // Zero is already the result we want regardless of `denominator`. - } else { - let denominator = (1. - bpm_percentages).max(0.); - outer.max_content = Length::new(outer.max_content.px() / denominator); - } + outer.min_content += pbm_lengths; + outer.max_content += pbm_lengths; - outer + (outer, pbm_percentages) } diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 80254421e66..cb0b75a59f1 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -14,6 +14,7 @@ mod flow; mod formatting_contexts; mod fragments; mod geom; +mod intrinsic; mod opaque_node; mod positioned; pub mod query; diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 59a3131142c..06631a354df 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -170,6 +170,12 @@ impl LengthPercentage { self.length } + /// Returns the percentage component of this `calc()` + #[inline] + pub fn percentage_component(&self) -> Percentage { + Percentage(self.clamping_mode.clamp(self.percentage.0)) + } + /// Return the percentage value as CSSFloat. #[inline] pub fn percentage(&self) -> CSSFloat { diff --git a/components/style/values/computed/percentage.rs b/components/style/values/computed/percentage.rs index b5a734367b2..7430d82d471 100644 --- a/components/style/values/computed/percentage.rs +++ b/components/style/values/computed/percentage.rs @@ -64,6 +64,12 @@ impl Zero for Percentage { } } +impl std::ops::AddAssign for Percentage { + fn add_assign(&mut self, other: Self) { + self.0 += other.0 + } +} + impl ToCss for Percentage { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where