Layout: stylistic changes

Additionally if an image border can't be displayed show solid border.
Introduce build_display_list_for_border_image to display border images.
This commit is contained in:
Pyfisch 2018-09-27 22:58:09 +02:00
parent fabb70f874
commit 60d0c8cd76
4 changed files with 175 additions and 183 deletions

View file

@ -37,8 +37,8 @@ use style::values::generics::image::GradientItem as GenericGradientItem;
use style::values::specified::background::BackgroundRepeatKeyword;
use style::values::specified::position::{X, Y};
use webrender_api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF};
use webrender_api::{ExtendMode, Gradient, GradientStop, LayoutSize, NinePatchBorder};
use webrender_api::{NinePatchBorderSource, NormalBorder, RadialGradient};
use webrender_api::{ExtendMode, Gradient, GradientStop, LayoutSize};
use webrender_api::{NormalBorder, RadialGradient};
/// A helper data structure for gradients.
#[derive(Clone, Copy)]
@ -70,7 +70,7 @@ pub struct BackgroundPlacement {
pub fixed: bool,
}
trait ResolvePercentage {
pub trait ResolvePercentage {
fn resolve(&self, length: u32) -> u32;
}
@ -801,65 +801,6 @@ pub fn calculate_inner_border_radii(
radii
}
/// Create the image border details.
///
/// For a fragment with a given with a given "bounds" that has a "border_width"
/// for all four sides calculate the outset for the image border. Call the
/// "build_source" function to get the image which is either a gradient
/// or an image. The supplied "fallback_size" is used to size gradients
/// and other images without an intrinsic size. The actual size of the image
/// is also returned. Apply all other attributes for border images and return
/// the "NinePatchBorder".
pub fn build_border_image_details<F>(
bounds: Rect<Au>,
border_width: SideOffsets2D<Au>,
border_style_struct: &style_structs::Border,
build_source: F,
) -> Option<(NinePatchBorder, BorderWidths)>
where
F: FnOnce(Size2D<Au>) -> Option<(NinePatchBorderSource, Size2D<u32>)>,
{
let border_image_outset =
calculate_border_image_outset(border_style_struct.border_image_outset, border_width);
let border_image_area = bounds.outer_rect(border_image_outset).size;
let border_image_width = calculate_border_image_width(
&border_style_struct.border_image_width,
border_width.to_layout(),
border_image_area,
);
let border_image_repeat = &border_style_struct.border_image_repeat;
let border_image_fill = border_style_struct.border_image_slice.fill;
let border_image_slice = &border_style_struct.border_image_slice.offsets;
if let Some((source, size)) = build_source(border_image_area) {
Some((
NinePatchBorder {
source: source,
width: size.width,
height: size.height,
slice: SideOffsets2D::new(
border_image_slice.0.resolve(size.height),
border_image_slice.1.resolve(size.width),
border_image_slice.2.resolve(size.height),
border_image_slice.3.resolve(size.width),
),
fill: border_image_fill,
repeat_horizontal: border_image_repeat.0.to_layout(),
repeat_vertical: border_image_repeat.1.to_layout(),
outset: SideOffsets2D::new(
border_image_outset.top.to_f32_px(),
border_image_outset.right.to_f32_px(),
border_image_outset.bottom.to_f32_px(),
border_image_outset.left.to_f32_px(),
),
},
border_image_width,
))
} else {
None
}
}
fn calculate_border_image_outset_side(outset: LengthOrNumber, border_width: Au) -> Au {
match outset {
Either::First(length) => length.into(),
@ -867,7 +808,7 @@ fn calculate_border_image_outset_side(outset: LengthOrNumber, border_width: Au)
}
}
fn calculate_border_image_outset(
pub fn calculate_border_image_outset(
outset: BorderImageOutset,
border: SideOffsets2D<Au>,
) -> SideOffsets2D<Au> {
@ -891,7 +832,7 @@ fn calculate_border_image_width_side(
}
}
fn calculate_border_image_width(
pub fn calculate_border_image_width(
width: &BorderImageWidth,
border: BorderWidths,
border_area: Size2D<Au>,

View file

@ -15,11 +15,11 @@ use block::BlockFlow;
use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg};
use context::LayoutContext;
use display_list::ToLayout;
use display_list::background::{build_border_radius, build_border_image_details};
use display_list::background::{calculate_inner_border_radii, compute_background_clip};
use display_list::background::{compute_background_placement, convert_linear_gradient};
use display_list::background::{convert_radial_gradient, get_cyclic};
use display_list::background::simple_normal_border;
use display_list::background::{build_border_radius, calculate_inner_border_radii};
use display_list::background::{calculate_border_image_outset, calculate_border_image_width};
use display_list::background::{compute_background_clip, compute_background_placement};
use display_list::background::{convert_linear_gradient, convert_radial_gradient, get_cyclic};
use display_list::background::{simple_normal_border, ResolvePercentage};
use display_list::items::{BaseDisplayItem, BLUR_INFLATION_FACTOR, ClipScrollNode};
use display_list::items::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
use display_list::items::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
@ -62,6 +62,7 @@ use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::{Either, RGBA};
use style::values::computed::Gradient;
use style::values::computed::effects::SimpleShadow;
use style::values::computed::image::Image as ComputedImage;
use style::values::generics::background::BackgroundSize;
use style::values::generics::image::{GradientKind, Image, PaintWorklet};
use style::values::generics::ui::Cursor;
@ -71,7 +72,7 @@ use style_traits::cursor::CursorKind;
use table_cell::CollapsedBordersForCell;
use webrender_api::{self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF};
use webrender_api::{ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LayoutRect};
use webrender_api::{LayoutSize, LayoutTransform, LayoutVector2D, LineStyle};
use webrender_api::{LayoutSize, LayoutTransform, LayoutVector2D, LineStyle, NinePatchBorder};
use webrender_api::{NinePatchBorderSource, NormalBorder, StickyOffsetBounds, ScrollSensitivity};
fn establishes_containing_block_for_absolute(
@ -666,6 +667,19 @@ pub trait FragmentDisplayListBuilding {
clip: Rect<Au>,
);
/// Add display item for image border.
///
/// Returns `Some` if the addition was successful.
fn build_display_list_for_border_image(
&self,
state: &mut DisplayListBuildState,
style: &ComputedValues,
base: BaseDisplayItem,
bounds: Rect<Au>,
image: &ComputedImage,
border_width: SideOffsets2D<Au>,
) -> Option<()>;
/// Adds the display items necessary to paint the outline of this fragment to the display list
/// if necessary.
fn build_display_list_for_outline_if_applicable(
@ -1263,21 +1277,6 @@ impl FragmentDisplayListBuilding for Fragment {
display_list_section: DisplayListSection,
clip: Rect<Au>,
) {
fn convert_image_to_border(
image: WebRenderImageInfo,
) -> Option<(NinePatchBorderSource, Size2D<u32>)> {
image.key.map(|key| {
(
NinePatchBorderSource::Image(key),
Size2D::new(image.width, image.height),
)
})
}
fn convert_size_au_to_u32(size: Size2D<Au>) -> Size2D<u32> {
Size2D::new(size.width.to_px() as u32, size.height.to_px() as u32)
}
let mut border = style.logical_border_width();
if let Some(inline_info) = inline_info {
@ -1330,107 +1329,159 @@ impl FragmentDisplayListBuilding for Fragment {
let border_radius = build_border_radius(bounds, border_style_struct);
let border_widths = border.to_physical(style.writing_mode);
let mut layout_border_width = border_widths.to_layout();
if let Either::Second(ref image) = border_style_struct.border_image_source {
if self
.build_display_list_for_border_image(
state,
style,
base.clone(),
bounds,
image,
border_widths,
).is_some()
{
return;
}
// Fallback to rendering a solid border.
}
if border_widths == SideOffsets2D::zero() {
return;
}
let details = BorderDetails::Normal(NormalBorder {
left: BorderSide {
color: style.resolve_color(colors.left).to_layout(),
style: border_style.left.to_layout(),
},
right: BorderSide {
color: style.resolve_color(colors.right).to_layout(),
style: border_style.right.to_layout(),
},
top: BorderSide {
color: style.resolve_color(colors.top).to_layout(),
style: border_style.top.to_layout(),
},
bottom: BorderSide {
color: style.resolve_color(colors.bottom).to_layout(),
style: border_style.bottom.to_layout(),
},
radius: border_radius,
});
state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
base,
webrender_api::BorderDisplayItem {
widths: border_widths.to_layout(),
details,
},
Vec::new(),
)));
}
fn build_display_list_for_border_image(
&self,
state: &mut DisplayListBuildState,
style: &ComputedValues,
base: BaseDisplayItem,
bounds: Rect<Au>,
image: &ComputedImage,
border_width: SideOffsets2D<Au>,
) -> Option<()> {
let border_style_struct = style.get_border();
let border_image_outset =
calculate_border_image_outset(border_style_struct.border_image_outset, border_width);
let border_image_area = bounds.outer_rect(border_image_outset).size;
let border_image_width = calculate_border_image_width(
&border_style_struct.border_image_width,
border_width.to_layout(),
border_image_area,
);
let border_image_repeat = &border_style_struct.border_image_repeat;
let border_image_fill = border_style_struct.border_image_slice.fill;
let border_image_slice = &border_style_struct.border_image_slice.offsets;
let mut stops = Vec::new();
let details = match border_style_struct.border_image_source {
Either::First(_) => {
if border_widths == SideOffsets2D::zero() {
None
} else {
Some(BorderDetails::Normal(NormalBorder {
left: BorderSide {
color: style.resolve_color(colors.left).to_layout(),
style: border_style.left.to_layout(),
},
right: BorderSide {
color: style.resolve_color(colors.right).to_layout(),
style: border_style.right.to_layout(),
},
top: BorderSide {
color: style.resolve_color(colors.top).to_layout(),
style: border_style.top.to_layout(),
},
bottom: BorderSide {
color: style.resolve_color(colors.bottom).to_layout(),
style: border_style.bottom.to_layout(),
},
radius: border_radius,
}))
}
let mut width = border_image_area.width.to_px() as u32;
let mut height = border_image_area.height.to_px() as u32;
let source = match image {
Image::Url(ref image_url) => {
let url = image_url.url()?;
let image = state.layout_context.get_webrender_image_for_url(
self.node,
url.clone(),
UsePlaceholder::No,
)?;
width = image.width;
height = image.height;
NinePatchBorderSource::Image(image.key?)
},
Either::Second(ref image) => {
build_border_image_details(
bounds,
border_widths,
border_style_struct,
|fallback_size| match image {
Image::Url(ref image_url) => image_url
.url()
.and_then(|url| {
state.layout_context.get_webrender_image_for_url(
self.node,
url.clone(),
UsePlaceholder::No,
)
}).and_then(convert_image_to_border),
Image::PaintWorklet(ref paint_worklet) => self
.get_webrender_image_for_paint_worklet(
state,
style,
paint_worklet,
fallback_size,
).and_then(convert_image_to_border),
Image::Gradient(ref gradient) => match gradient.kind {
GradientKind::Linear(angle_or_corner) => {
let (wr_gradient, linear_stops) = convert_linear_gradient(
style,
fallback_size,
&gradient.items[..],
angle_or_corner,
gradient.repeating,
);
stops = linear_stops;
Some((
NinePatchBorderSource::Gradient(wr_gradient),
convert_size_au_to_u32(fallback_size),
))
},
GradientKind::Radial(shape, center, _angle) => {
let (wr_gradient, radial_stops) = convert_radial_gradient(
style,
fallback_size,
&gradient.items[..],
shape,
center,
gradient.repeating,
);
stops = radial_stops;
Some((
NinePatchBorderSource::RadialGradient(wr_gradient),
convert_size_au_to_u32(fallback_size),
))
},
},
_ => None,
},
).map(|(details, border)| {
// The image's border width can differ from the "simple" color border width.
layout_border_width = border;
BorderDetails::NinePatch(details)
})
Image::PaintWorklet(ref paint_worklet) => {
let image = self.get_webrender_image_for_paint_worklet(
state,
style,
paint_worklet,
border_image_area,
)?;
width = image.width;
height = image.height;
NinePatchBorderSource::Image(image.key?)
},
};
if let Some(details) = details {
state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
base,
webrender_api::BorderDisplayItem {
widths: layout_border_width,
details,
Image::Gradient(ref gradient) => match gradient.kind {
GradientKind::Linear(angle_or_corner) => {
let (wr_gradient, linear_stops) = convert_linear_gradient(
style,
border_image_area,
&gradient.items[..],
angle_or_corner,
gradient.repeating,
);
stops = linear_stops;
NinePatchBorderSource::Gradient(wr_gradient)
},
stops,
)));
}
GradientKind::Radial(shape, center, _angle) => {
let (wr_gradient, radial_stops) = convert_radial_gradient(
style,
border_image_area,
&gradient.items[..],
shape,
center,
gradient.repeating,
);
stops = radial_stops;
NinePatchBorderSource::RadialGradient(wr_gradient)
},
},
_ => return None,
};
let details = BorderDetails::NinePatch(NinePatchBorder {
source,
width,
height,
slice: SideOffsets2D::new(
border_image_slice.0.resolve(height),
border_image_slice.1.resolve(width),
border_image_slice.2.resolve(height),
border_image_slice.3.resolve(width),
),
fill: border_image_fill,
repeat_horizontal: border_image_repeat.0.to_layout(),
repeat_vertical: border_image_repeat.1.to_layout(),
outset: SideOffsets2D::new(
border_image_outset.top.to_f32_px(),
border_image_outset.right.to_f32_px(),
border_image_outset.bottom.to_f32_px(),
border_image_outset.left.to_f32_px(),
),
});
state.add_display_item(DisplayItem::Border(CommonDisplayItem::with_data(
base,
webrender_api::BorderDisplayItem {
widths: border_image_width,
details,
},
stops,
)));
Some(())
}
fn build_display_list_for_outline_if_applicable(