Add intrinsic size computation for inline formatting contexts

This commit is contained in:
Simon Sapin 2019-12-02 22:11:54 +01:00
parent 8fe37f3ed6
commit 9c5a595044
6 changed files with 188 additions and 46 deletions

View file

@ -127,6 +127,7 @@ impl BlockContainer {
context: &SharedStyleContext<'style>,
block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
// intrinsic_sizes_requested: bool,
) -> (BlockContainer, ContainsFloats) {
let mut builder = BlockContainerBuilder {
context,

View file

@ -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<InlineLevelBox>],
) {
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<T: Zero>(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<GlyphRun>, 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 {

View file

@ -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)
}

View file

@ -14,6 +14,7 @@ mod flow;
mod formatting_contexts;
mod fragments;
mod geom;
mod intrinsic;
mod opaque_node;
mod positioned;
pub mod query;

View file

@ -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 {

View file

@ -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<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where