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

Start implementing text in layout 2020 #24822

Merged
merged 6 commits into from Nov 25, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Some generated files are not rendered by default. Learn more.

@@ -452,6 +452,11 @@ impl<'a> GlyphStore {
}
}

#[inline]
pub fn total_advance(&self) -> Au {
This conversation was marked as resolved by SimonSapin

This comment has been minimized.

Copy link
@SimonSapin

SimonSapin Nov 22, 2019

Member

What does 2013 do to not need this?

This comment has been minimized.

Copy link
@nox

nox Nov 25, 2019

Author Member

2013 does not use GlyphStore directly at all, it uses gfx::text::text_run::TextRun and gfx::text::text_run::GlyphRun, but that uses even more thread-local caches, so I opted out of that stuff.

self.total_advance
}

#[inline]
pub fn len(&self) -> ByteIndex {
ByteIndex(self.entry_buffer.len() as isize)
@@ -18,15 +18,19 @@ atomic_refcell = "0.1"
cssparser = "0.27"
euclid = "0.20"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
ipc-channel = "0.12"
libc = "0.2"
msg = {path = "../msg"}
range = {path = "../range"}
rayon = "1"
rayon_croissant = "0.1.1"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
serde = "1.0"
servo_arc = { path = "../servo_arc" }
servo_geometry = {path = "../geometry"}
style = {path = "../style", features = ["servo", "servo-layout-2020"]}
style_traits = {path = "../style_traits"}
unicode-script = {version = "0.3", features = ["harfbuzz"]}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
@@ -3,7 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use msg::constellation_msg::PipelineId;
use std::cell::{RefCell, RefMut};
use std::sync::Mutex;
use style::context::SharedStyleContext;

@@ -19,3 +21,18 @@ impl<'a> LayoutContext<'a> {
&self.style_context
}
}

pub(crate) type LayoutFontContext = FontContext<FontCacheThread>;

thread_local!(static FONT_CONTEXT: RefCell<Option<LayoutFontContext>> = RefCell::new(None));

pub(crate) fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R
where
F: FnOnce(&mut LayoutFontContext) -> R,
{
FONT_CONTEXT.with(|font_context| {
f(font_context.borrow_mut().get_or_insert_with(|| {
FontContext::new(layout_context.font_cache_thread.lock().unwrap().clone())
}))
})
}
@@ -6,7 +6,10 @@ use crate::fragments::{BoxFragment, Fragment};
use crate::geom::physical::{Rect, Vec2};
use crate::style_ext::ComputedValuesExt;
use app_units::Au;
use euclid::{self, SideOffsets2D};
use euclid::{Point2D, SideOffsets2D};
use gfx::text::glyph::GlyphStore;
use servo_geometry::MaxRect;
use std::sync::Arc;
use style::values::computed::{BorderStyle, Length};
use webrender_api::{self as wr, units, CommonItemProperties, PrimitiveFlags};

@@ -50,9 +53,30 @@ impl Fragment {
child.build_display_list(builder, is_contentful, &rect)
}
},
Fragment::Text(_) => {
Fragment::Text(t) => {
is_contentful.0 = true;
// FIXME
let rect = t
.content_rect
.to_physical(t.parent_style.writing_mode(), containing_block)
.translate(&containing_block.top_left);
let mut baseline_origin = rect.top_left.clone();
baseline_origin.y += t.ascent;
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(),
};
let glyphs = glyphs(&t.glyphs, baseline_origin);
if glyphs.is_empty() {
return;
}
let color = t.parent_style.clone_color();
builder
.wr
.push_text(&common, rect.into(), &glyphs, t.font_key, rgba(color), None);
},
}
}
@@ -154,3 +178,28 @@ fn rgba(rgba: cssparser::RGBA) -> wr::ColorF {
rgba.alpha_f32(),
)
}

fn glyphs(glyph_runs: &[Arc<GlyphStore>], mut origin: Vec2<Length>) -> Vec<wr::GlyphInstance> {
use gfx_traits::ByteIndex;
use range::Range;

let mut glyphs = vec![];
for run in glyph_runs {
for glyph in run.iter_glyphs_for_byte_range(&Range::new(ByteIndex(0), run.len())) {
if !run.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let point = units::LayoutPoint::new(
origin.x.px() + glyph_offset.x.to_f32_px(),
origin.y.px() + glyph_offset.y.to_f32_px(),
);
let glyph = wr::GlyphInstance {
index: glyph.id(),
point,
};
glyphs.push(glyph);
}
origin.x += Length::from(glyph.advance());
}
}
glyphs
}
@@ -2,9 +2,12 @@
* 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::flow::float::FloatBox;
use crate::flow::FlowChildren;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment, TextFragment,
};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
use crate::replaced::ReplacedContent;
@@ -81,6 +84,7 @@ struct LinesBoxes {
impl InlineFormattingContext {
pub(super) fn layout<'a>(
&'a self,
layout_context: &LayoutContext,
containing_block: &ContainingBlock,
tree_rank: usize,
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
@@ -107,7 +111,7 @@ impl InlineFormattingContext {
let partial = inline.start_layout(&mut ifc);
ifc.partial_inline_boxes_stack.push(partial)
},
InlineLevelBox::TextRun(run) => run.layout(&mut ifc),
InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc),
InlineLevelBox::Atomic { style: _, contents } => {
// FIXME
match *contents {}
@@ -284,7 +288,141 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}

impl TextRun {
fn layout(&self, _ifc: &mut InlineFormattingContextState) {
// TODO
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
use gfx::font::{ShapingFlags, ShapingOptions};
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();
let letter_spacing = if inherited_text_style.letter_spacing.0.px() != 0. {
Some(app_units::Au::from(inherited_text_style.letter_spacing.0))
} else {
None
};

let mut flags = ShapingFlags::empty();
if letter_spacing.is_some() {
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
}
if inherited_text_style.text_rendering == TextRendering::Optimizespeed {
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
flags.insert(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
}
if inherited_text_style.word_break == WordBreak::KeepAll {
flags.insert(ShapingFlags::KEEP_ALL_FLAG);
}

let shaping_options = gfx::font::ShapingOptions {
letter_spacing,
word_spacing: inherited_text_style.word_spacing.to_hash_key(),
script: unicode_script::Script::Common,
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();

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,
)
});

let font_size = self.parent_style.get_font().font_size.size.0;
let mut runs = runs.iter();
loop {
let mut glyphs = vec![];
let mut advance_width = Length::zero();
let mut last_break_opportunity = None;
loop {
let next = runs.next();
if next
.as_ref()
.map_or(true, |run| run.glyph_store.is_whitespace())
{
if advance_width > ifc.containing_block.inline_size - ifc.inline_position {
if let Some((len, width, iter)) = last_break_opportunity.take() {
glyphs.truncate(len);
advance_width = width;
runs = iter;
}
break;
}
}
if let Some(run) = next {
if run.glyph_store.is_whitespace() {
last_break_opportunity = Some((glyphs.len(), advance_width, runs.clone()));
}
glyphs.push(run.glyph_store.clone());
advance_width += Length::from(run.glyph_store.total_advance());
} else {
break;
}
}
let line_height = match self.parent_style.get_inherited_text().line_height {
LineHeight::Normal => font_line_gap.into(),
LineHeight::Number(n) => font_size * n.0,
LineHeight::Length(l) => l.0,
};
let content_rect = Rect {
start_corner: Vec2 {
block: Length::zero(),
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
},
size: Vec2 {
block: line_height,
inline: advance_width,
},
};
ifc.inline_position += advance_width;
ifc.current_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(line_height);
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
parent_style: self.parent_style.clone(),
content_rect,
ascent: font_ascent.into(),
font_key,
glyphs,
}));
if runs.is_empty() {
break;
} else {
// New line
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
partial.margin.inline_start = Length::zero();
partial.parent_nesting_level.inline_start = Length::zero();
nesting_level = &mut partial.parent_nesting_level;
}
ifc.line_boxes
.finish_line(nesting_level, ifc.containing_block);
ifc.inline_position = Length::zero();
}
}
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.