Implement new Webrender PushTextShadow API

Also includes an incidental bugfix for the rendering order of text decorations.

This change depends on servo/webrender#1454 being merged and upstreamed to servo.
This commit is contained in:
Alexis Beingessner 2017-07-11 17:37:41 -04:00 committed by Martin Robinson
parent 1b6d29e319
commit 2d3eae3e3a
3 changed files with 106 additions and 67 deletions

View file

@ -605,6 +605,8 @@ pub enum DisplayItem {
RadialGradient(Box<RadialGradientDisplayItem>), RadialGradient(Box<RadialGradientDisplayItem>),
Line(Box<LineDisplayItem>), Line(Box<LineDisplayItem>),
BoxShadow(Box<BoxShadowDisplayItem>), BoxShadow(Box<BoxShadowDisplayItem>),
PushTextShadow(Box<PushTextShadowDisplayItem>),
PopTextShadow(Box<PopTextShadowDisplayItem>),
Iframe(Box<IframeDisplayItem>), Iframe(Box<IframeDisplayItem>),
PushStackingContext(Box<PushStackingContextItem>), PushStackingContext(Box<PushStackingContextItem>),
PopStackingContext(Box<PopStackingContextItem>), PopStackingContext(Box<PopStackingContextItem>),
@ -895,9 +897,6 @@ pub struct TextDisplayItem {
/// The orientation of the text: upright or sideways left/right. /// The orientation of the text: upright or sideways left/right.
pub orientation: TextOrientation, pub orientation: TextOrientation,
/// The blur radius for this text. If zero, this text is not blurred.
pub blur_radius: Au,
} }
#[derive(Clone, Eq, PartialEq, HeapSizeOf, Deserialize, Serialize)] #[derive(Clone, Eq, PartialEq, HeapSizeOf, Deserialize, Serialize)]
@ -1180,6 +1179,29 @@ pub struct BoxShadowDisplayItem {
pub clip_mode: BoxShadowClipMode, pub clip_mode: BoxShadowClipMode,
} }
/// Defines a text shadow that affects all items until the paired PopTextShadow.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct PushTextShadowDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
/// The offset of this shadow from the text.
pub offset: Vector2D<Au>,
/// The color of this shadow.
pub color: ColorF,
/// The blur radius for this shadow.
pub blur_radius: Au,
}
/// Defines a text shadow that affects all items until the next PopTextShadow.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct PopTextShadowDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
}
/// Defines a stacking context. /// Defines a stacking context.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] #[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct PushStackingContextItem { pub struct PushStackingContextItem {
@ -1233,6 +1255,8 @@ impl DisplayItem {
DisplayItem::RadialGradient(ref gradient) => &gradient.base, DisplayItem::RadialGradient(ref gradient) => &gradient.base,
DisplayItem::Line(ref line) => &line.base, DisplayItem::Line(ref line) => &line.base,
DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base, DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base,
DisplayItem::PushTextShadow(ref push_text_shadow) => &push_text_shadow.base,
DisplayItem::PopTextShadow(ref pop_text_shadow) => &pop_text_shadow.base,
DisplayItem::Iframe(ref iframe) => &iframe.base, DisplayItem::Iframe(ref iframe) => &iframe.base,
DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base, DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base,
DisplayItem::PopStackingContext(ref item) => &item.base, DisplayItem::PopStackingContext(ref item) => &item.base,
@ -1353,6 +1377,8 @@ impl fmt::Debug for DisplayItem {
DisplayItem::RadialGradient(_) => "RadialGradient".to_owned(), DisplayItem::RadialGradient(_) => "RadialGradient".to_owned(),
DisplayItem::Line(_) => "Line".to_owned(), DisplayItem::Line(_) => "Line".to_owned(),
DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(), DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(),
DisplayItem::PushTextShadow(_) => "PushTextShadow".to_owned(),
DisplayItem::PopTextShadow(_) => "PopTextShadow".to_owned(),
DisplayItem::Iframe(_) => "Iframe".to_owned(), DisplayItem::Iframe(_) => "Iframe".to_owned(),
DisplayItem::PushStackingContext(_) | DisplayItem::PushStackingContext(_) |
DisplayItem::PopStackingContext(_) | DisplayItem::PopStackingContext(_) |

View file

@ -25,10 +25,10 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDetails, B
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection}; use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageBorder, ImageDisplayItem}; use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageBorder, ImageDisplayItem};
use gfx::display_list::{LineDisplayItem, NormalBorder, OpaqueNode, RadialGradientDisplayItem}; use gfx::display_list::{LineDisplayItem, NormalBorder, OpaqueNode, PushTextShadowDisplayItem};
use gfx::display_list::{ScrollRoot, ScrollRootType, SolidColorDisplayItem, StackingContext}; use gfx::display_list::{PopTextShadowDisplayItem, RadialGradientDisplayItem, ScrollRoot};
use gfx::display_list::{StackingContextType, TextDisplayItem, TextOrientation, WebGLDisplayItem}; use gfx::display_list::{ScrollRootType, SolidColorDisplayItem, StackingContext, StackingContextType};
use gfx::display_list::WebRenderImageInfo; use gfx::display_list::{TextDisplayItem, TextOrientation, WebGLDisplayItem, WebRenderImageInfo};
use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId}; use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId};
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
use ipc_channel::ipc; use ipc_channel::ipc;
@ -525,7 +525,7 @@ pub trait FragmentDisplayListBuilding {
state: &mut DisplayListBuildState, state: &mut DisplayListBuildState,
text_fragment: &ScannedTextFragmentInfo, text_fragment: &ScannedTextFragmentInfo,
stacking_relative_content_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>,
text_shadow: Option<&SimpleShadow>, text_shadows: &[SimpleShadow],
clip: &Rect<Au>); clip: &Rect<Au>);
/// Creates the display item for a text decoration: underline, overline, or line-through. /// Creates the display item for a text decoration: underline, overline, or line-through.
@ -533,8 +533,7 @@ pub trait FragmentDisplayListBuilding {
state: &mut DisplayListBuildState, state: &mut DisplayListBuildState,
color: &RGBA, color: &RGBA,
stacking_relative_box: &LogicalRect<Au>, stacking_relative_box: &LogicalRect<Au>,
clip: &Rect<Au>, clip: &Rect<Au>);
blur: Au);
/// A helper method that `build_display_list` calls to create per-fragment-type display items. /// A helper method that `build_display_list` calls to create per-fragment-type display items.
fn build_fragment_type_specific_display_items(&mut self, fn build_fragment_type_specific_display_items(&mut self,
@ -1878,24 +1877,11 @@ impl FragmentDisplayListBuilding for Fragment {
.. ..
}) | }) |
SpecificFragmentInfo::ScannedText(box ref text_fragment) => { SpecificFragmentInfo::ScannedText(box ref text_fragment) => {
// Create items for shadows.
//
// NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
// to back).
for text_shadow in self.style.get_inheritedtext().text_shadow.0.iter().rev() {
self.build_display_list_for_text_fragment(state,
&*text_fragment,
&stacking_relative_content_box,
Some(text_shadow),
clip);
}
// Create the main text display item. // Create the main text display item.
self.build_display_list_for_text_fragment(state, self.build_display_list_for_text_fragment(state,
&*text_fragment, &*text_fragment,
&stacking_relative_content_box, &stacking_relative_content_box,
None, &self.style.get_inheritedtext().text_shadow.0,
clip); clip);
if opts::get().show_debug_fragment_borders { if opts::get().show_debug_fragment_borders {
@ -2077,22 +2063,20 @@ impl FragmentDisplayListBuilding for Fragment {
state: &mut DisplayListBuildState, state: &mut DisplayListBuildState,
text_fragment: &ScannedTextFragmentInfo, text_fragment: &ScannedTextFragmentInfo,
stacking_relative_content_box: &Rect<Au>, stacking_relative_content_box: &Rect<Au>,
text_shadow: Option<&SimpleShadow>, text_shadows: &[SimpleShadow],
clip: &Rect<Au>) { clip: &Rect<Au>) {
// NB: The order for painting text components (CSS Text Decoration Module Level 3) is:
// shadows, underline, overline, text, text-emphasis, and then line-through.
// TODO(emilio): Allow changing more properties by ::selection // TODO(emilio): Allow changing more properties by ::selection
let text_color = if let Some(shadow) = text_shadow { // Paint the text with the color as described in its styling.
// If we're painting a shadow, paint the text the same color as the shadow. let text_color = if text_fragment.selected() {
self.style().resolve_color(shadow.color)
} else if text_fragment.selected() {
// Otherwise, paint the text with the color as described in its styling.
self.selected_style().get_color().color self.selected_style().get_color().color
} else { } else {
self.style().get_color().color self.style().get_color().color
}; };
let offset = text_shadow.map_or(Vector2D::zero(), |s| {
Vector2D::new(s.horizontal, s.vertical)
});
let shadow_blur_radius = text_shadow.map(|s| s.blur).unwrap_or(Au(0));
// Determine the orientation and cursor to use. // Determine the orientation and cursor to use.
let (orientation, cursor) = if self.style.writing_mode.is_vertical() { let (orientation, cursor) = if self.style.writing_mode.is_vertical() {
@ -2108,28 +2092,32 @@ impl FragmentDisplayListBuilding for Fragment {
// FIXME(pcwalton): Get the real container size. // FIXME(pcwalton): Get the real container size.
let container_size = Size2D::zero(); let container_size = Size2D::zero();
let metrics = &text_fragment.run.font_metrics; let metrics = &text_fragment.run.font_metrics;
let stacking_relative_content_box = stacking_relative_content_box.translate(&offset);
let baseline_origin = stacking_relative_content_box.origin + let baseline_origin = stacking_relative_content_box.origin +
LogicalPoint::new(self.style.writing_mode, LogicalPoint::new(self.style.writing_mode,
Au(0), Au(0),
metrics.ascent).to_physical(self.style.writing_mode, metrics.ascent).to_physical(self.style.writing_mode,
container_size).to_vector(); container_size).to_vector();
// Create the text display item. // Base item for all text/shadows
let base = state.create_base_display_item(&stacking_relative_content_box, let base = state.create_base_display_item(&stacking_relative_content_box,
LocalClip::from(clip.to_rectf()), LocalClip::from(clip.to_rectf()),
self.node, self.node,
self.style().get_cursor(cursor), self.style().get_cursor(cursor),
DisplayListSection::Content); DisplayListSection::Content);
state.add_display_item(DisplayItem::Text(box TextDisplayItem {
base: base, // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front
text_run: text_fragment.run.clone(), // to back).
range: text_fragment.range,
text_color: text_color.to_gfx_color(), // Shadows
orientation: orientation, for shadow in text_shadows.iter().rev() {
baseline_origin: baseline_origin, state.add_display_item(DisplayItem::PushTextShadow(box PushTextShadowDisplayItem {
blur_radius: shadow_blur_radius, base: base.clone(),
})); blur_radius: shadow.blur,
offset: Vector2D::new(shadow.horizontal, shadow.vertical),
color: self.style().resolve_color(shadow.color).to_gfx_color(),
}));
}
// Create display items for text decorations. // Create display items for text decorations.
let mut text_decorations = self.style() let mut text_decorations = self.style()
@ -2142,8 +2130,10 @@ impl FragmentDisplayListBuilding for Fragment {
let stacking_relative_content_box = let stacking_relative_content_box =
LogicalRect::from_physical(self.style.writing_mode, LogicalRect::from_physical(self.style.writing_mode,
stacking_relative_content_box, *stacking_relative_content_box,
container_size); container_size);
// Underline
if let Some(ref underline_color) = text_decorations.underline { if let Some(ref underline_color) = text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_content_box.start.b + stacking_relative_box.start.b = stacking_relative_content_box.start.b +
@ -2152,20 +2142,35 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_decoration(state, self.build_display_list_for_text_decoration(state,
underline_color, underline_color,
&stacking_relative_box, &stacking_relative_box,
clip, clip);
shadow_blur_radius);
} }
// Overline
if let Some(ref overline_color) = text_decorations.overline { if let Some(ref overline_color) = text_decorations.overline {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.size.block = metrics.underline_size; stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(state, self.build_display_list_for_text_decoration(state,
overline_color, overline_color,
&stacking_relative_box, &stacking_relative_box,
clip, clip);
shadow_blur_radius);
} }
// Text
state.add_display_item(DisplayItem::Text(box TextDisplayItem {
base: base.clone(),
text_run: text_fragment.run.clone(),
range: text_fragment.range,
text_color: text_color.to_gfx_color(),
orientation: orientation,
baseline_origin: baseline_origin,
}));
// TODO(#17715): emit text-emphasis marks here.
// (just push another TextDisplayItem?)
// Line-Through
if let Some(ref line_through_color) = text_decorations.line_through { if let Some(ref line_through_color) = text_decorations.line_through {
let mut stacking_relative_box = stacking_relative_content_box; let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent - stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent -
@ -2174,8 +2179,14 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_decoration(state, self.build_display_list_for_text_decoration(state,
line_through_color, line_through_color,
&stacking_relative_box, &stacking_relative_box,
clip, clip);
shadow_blur_radius); }
// Pair all the PushTextShadows
for _ in text_shadows {
state.add_display_item(DisplayItem::PopTextShadow(box PopTextShadowDisplayItem {
base: base.clone(),
}));
} }
} }
@ -2183,31 +2194,21 @@ impl FragmentDisplayListBuilding for Fragment {
state: &mut DisplayListBuildState, state: &mut DisplayListBuildState,
color: &RGBA, color: &RGBA,
stacking_relative_box: &LogicalRect<Au>, stacking_relative_box: &LogicalRect<Au>,
clip: &Rect<Au>, clip: &Rect<Au>) {
blur_radius: Au) {
// Perhaps surprisingly, text decorations are box shadows. This is because they may need
// to have blur in the case of `text-shadow`, and this doesn't hurt performance because box
// shadows are optimized into essentially solid colors if there is no need for the blur.
//
// FIXME(pcwalton, #2795): Get the real container size. // FIXME(pcwalton, #2795): Get the real container size.
let container_size = Size2D::zero(); let container_size = Size2D::zero();
let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode, let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode,
container_size); container_size);
let base = state.create_base_display_item( let base = state.create_base_display_item(
&shadow_bounds(&stacking_relative_box, blur_radius, Au(0)), &stacking_relative_box,
LocalClip::from(clip.to_rectf()), LocalClip::from(clip.to_rectf()),
self.node, self.node,
self.style.get_cursor(Cursor::Default), self.style.get_cursor(Cursor::Default),
DisplayListSection::Content); DisplayListSection::Content);
state.add_display_item(DisplayItem::BoxShadow(box BoxShadowDisplayItem {
state.add_display_item(DisplayItem::SolidColor(box SolidColorDisplayItem {
base: base, base: base,
box_bounds: stacking_relative_box,
color: color.to_gfx_color(), color: color.to_gfx_color(),
offset: Vector2D::zero(),
blur_radius: blur_radius,
spread_radius: Au(0),
border_radius: Au(0),
clip_mode: BoxShadowClipMode::None,
})); }));
} }

View file

@ -289,7 +289,6 @@ impl WebRenderDisplayItemConverter for DisplayItem {
item.text_run.font_key, item.text_run.font_key,
item.text_color, item.text_color,
item.text_run.actual_pt_size, item.text_run.actual_pt_size,
item.blur_radius.to_f32_px(),
None); None);
} }
} }
@ -449,6 +448,19 @@ impl WebRenderDisplayItemConverter for DisplayItem {
item.border_radius.to_f32_px(), item.border_radius.to_f32_px(),
item.clip_mode.to_clip_mode()); item.clip_mode.to_clip_mode());
} }
DisplayItem::PushTextShadow(ref item) => {
let rect = item.base.bounds.to_rectf();
builder.push_text_shadow(rect,
Some(item.base.local_clip),
webrender_api::TextShadow {
blur_radius: item.blur_radius.to_f32_px(),
offset: item.offset.to_vectorf(),
color: item.color,
});
}
DisplayItem::PopTextShadow(_) => {
builder.pop_text_shadow();
}
DisplayItem::Iframe(ref item) => { DisplayItem::Iframe(ref item) => {
let rect = item.base.bounds.to_rectf(); let rect = item.base.bounds.to_rectf();
let pipeline_id = item.iframe.to_webrender(); let pipeline_id = item.iframe.to_webrender();