Auto merge of #19931 - pyfisch:float-bounds, r=mrobinson

Use LayoutRects for bounds and overflow in display lists

Convert text runs to glyphs in display list builder.
Remove ComplexClippingRegion and use the WebRender type.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19931)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-02-07 07:11:41 -05:00 committed by GitHub
commit 03a1a014ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 223 additions and 325 deletions

View file

@ -17,8 +17,7 @@ use context::LayoutContext;
use display_list::ToLayout;
use display_list::background::{compute_background_image_size, tile_image_axis};
use display_list::background::{convert_linear_gradient, convert_radial_gradient};
use display_list::webrender_helpers::ToBorderRadius;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedRect, TypedSize2D, Vector2D};
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, TypedSize2D, Vector2D, rect};
use flex::FlexFlow;
use flow::{BaseFlow, Flow, FlowFlags};
use flow_ref::FlowRef;
@ -27,7 +26,7 @@ use fragment::{CanvasFragmentSource, CoordinateSystem, Fragment, ScannedTextFrag
use fragment::SpecificFragmentInfo;
use gfx::display_list;
use gfx::display_list::{BaseDisplayItem, BorderDetails, BorderDisplayItem, BLUR_INFLATION_FACTOR};
use gfx::display_list::{BorderRadii, BoxShadowDisplayItem, ClipScrollNode};
use gfx::display_list::{BoxShadowDisplayItem, ClipScrollNode};
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
use gfx::display_list::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem};
@ -36,6 +35,8 @@ use gfx::display_list::{PopAllTextShadowsDisplayItem, PushTextShadowDisplayItem}
use gfx::display_list::{RadialGradientDisplayItem, SolidColorDisplayItem, StackingContext};
use gfx::display_list::{StackingContextType, StickyFrameData, TextDisplayItem, TextOrientation};
use gfx::display_list::WebRenderImageInfo;
use gfx::text::TextRun;
use gfx::text::glyph::ByteIndex;
use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId};
use inline::{InlineFlow, InlineFragmentNodeFlags};
use ipc_channel::ipc;
@ -47,8 +48,8 @@ use net_traits::image_cache::UsePlaceholder;
use range::Range;
use servo_config::opts;
use servo_geometry::MaxRect;
use std::{cmp, f32};
use std::default::Default;
use std::f32;
use std::mem;
use std::sync::Arc;
use style::computed_values::background_attachment::single_value::T as BackgroundAttachment;
@ -73,10 +74,11 @@ use style_traits::CSSPixel;
use style_traits::ToCss;
use style_traits::cursor::CursorKind;
use table_cell::CollapsedBordersForCell;
use webrender_api::{self, BorderSide, BoxShadowClipMode, ClipMode, ColorF, ComplexClipRegion};
use webrender_api::{ExternalScrollId, FilterOp, ImageBorder, ImageRendering, LayoutRect};
use webrender_api::{LayoutSize, LayoutVector2D, LineStyle, LocalClip, NinePatchDescriptor};
use webrender_api::{NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
use webrender_api::{self, BorderRadius, BorderSide, BoxShadowClipMode, ClipMode, ColorF};
use webrender_api::{ComplexClipRegion, ExternalScrollId, FilterOp, GlyphInstance, ImageBorder};
use webrender_api::{ImageRendering, LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip};
use webrender_api::{NinePatchDescriptor, NormalBorder, ScrollPolicy, ScrollSensitivity};
use webrender_api::StickyOffsetBounds;
trait ResolvePercentage {
fn resolve(&self, length: u32) -> u32;
@ -244,7 +246,7 @@ impl StackingContextCollectionState {
// takes care of adding this root node and it can be ignored during DL conversion.
let root_node = ClipScrollNode {
parent_index: ClipScrollNodeIndex(0),
clip: ClippingRegion::from_rect(&TypedRect::zero()),
clip: ClippingRegion::from_rect(LayoutRect::zero()),
content_rect: LayoutRect::zero(),
node_type: ClipScrollNodeType::ScrollFrame(
ScrollSensitivity::ScriptAndInputEvents,
@ -389,7 +391,7 @@ impl<'a> DisplayListBuildState<'a> {
};
BaseDisplayItem::new(
*bounds,
bounds.to_layout(),
DisplayItemMetadata {
node,
// Store cursor id in display list.
@ -708,16 +710,25 @@ pub trait FragmentDisplayListBuilding {
fn fragment_type(&self) -> FragmentType;
}
fn handle_overlapping_radii(size: &Size2D<Au>, radii: &BorderRadii<Au>) -> BorderRadii<Au> {
fn scale_border_radii(radii: BorderRadius, factor: f32) -> BorderRadius {
BorderRadius {
top_left: radii.top_left * factor,
top_right: radii.top_right * factor,
bottom_left: radii.bottom_left * factor,
bottom_right: radii.bottom_right * factor,
}
}
fn handle_overlapping_radii(size: LayoutSize, radii: BorderRadius) -> BorderRadius {
// No two corners' border radii may add up to more than the length of the edge
// between them. To prevent that, all radii are scaled down uniformly.
fn scale_factor(radius_a: Au, radius_b: Au, edge_length: Au) -> f32 {
fn scale_factor(radius_a: f32, radius_b: f32, edge_length: f32) -> f32 {
let required = radius_a + radius_b;
if required <= edge_length {
1.0
} else {
edge_length.to_f32_px() / required.to_f32_px()
edge_length / required
}
}
@ -738,39 +749,39 @@ fn handle_overlapping_radii(size: &Size2D<Au>, radii: &BorderRadii<Au>) -> Borde
.min(left_factor)
.min(right_factor);
if min_factor < 1.0 {
radii.scale_by(min_factor)
scale_border_radii(radii, min_factor)
} else {
*radii
radii
}
}
fn build_border_radius(
abs_bounds: &Rect<Au>,
border_style: &style_structs::Border,
) -> BorderRadii<Au> {
) -> BorderRadius {
// TODO(cgaebel): Support border radii even in the case of multiple border widths.
// This is an extension of supporting elliptical radii. For now, all percentage
// radii will be relative to the width.
handle_overlapping_radii(
&abs_bounds.size,
&BorderRadii {
abs_bounds.size.to_layout(),
BorderRadius {
top_left: model::specified_border_radius(
border_style.border_top_left_radius,
abs_bounds.size,
),
).to_layout(),
top_right: model::specified_border_radius(
border_style.border_top_right_radius,
abs_bounds.size,
),
).to_layout(),
bottom_right: model::specified_border_radius(
border_style.border_bottom_right_radius,
abs_bounds.size,
),
).to_layout(),
bottom_left: model::specified_border_radius(
border_style.border_bottom_left_radius,
abs_bounds.size,
),
).to_layout(),
},
)
}
@ -780,9 +791,9 @@ fn build_border_radius(
fn build_border_radius_for_inner_rect(
outer_rect: &Rect<Au>,
style: &ComputedValues,
) -> BorderRadii<Au> {
) -> BorderRadius {
let radii = build_border_radius(&outer_rect, style.get_border());
if radii.is_square() {
if radii.is_zero() {
return radii;
}
@ -830,20 +841,23 @@ fn simple_normal_border(color: ColorF, style: webrender_api::BorderStyle) -> Nor
}
fn calculate_inner_border_radii(
mut radii: BorderRadii<Au>,
mut radii: BorderRadius,
offsets: SideOffsets2D<Au>,
) -> BorderRadii<Au> {
radii.top_left.width = cmp::max(Au(0), radii.top_left.width - offsets.left);
radii.bottom_left.width = cmp::max(Au(0), radii.bottom_left.width - offsets.left);
) -> BorderRadius {
fn inner_length(x: f32, offset: Au) -> f32 {
0.0_f32.max(x - offset.to_f32_px())
}
radii.top_left.width = inner_length(radii.top_left.width, offsets.left);
radii.bottom_left.width = inner_length(radii.bottom_left.width, offsets.left);
radii.top_right.width = cmp::max(Au(0), radii.top_right.width - offsets.right);
radii.bottom_right.width = cmp::max(Au(0), radii.bottom_right.width - offsets.right);
radii.top_right.width = inner_length(radii.top_right.width, offsets.right);
radii.bottom_right.width = inner_length(radii.bottom_right.width, offsets.right);
radii.top_left.height = cmp::max(Au(0), radii.top_left.height - offsets.top);
radii.top_right.height = cmp::max(Au(0), radii.top_right.height - offsets.top);
radii.top_left.height = inner_length(radii.top_left.height, offsets.top);
radii.top_right.height = inner_length(radii.top_right.height, offsets.top);
radii.bottom_left.height = cmp::max(Au(0), radii.bottom_left.height - offsets.bottom);
radii.bottom_right.height = cmp::max(Au(0), radii.bottom_right.height - offsets.bottom);
radii.bottom_left.height = inner_length(radii.bottom_left.height, offsets.bottom);
radii.bottom_right.height = inner_length(radii.bottom_right.height, offsets.bottom);
radii
}
@ -877,6 +891,35 @@ fn build_image_border_details(
}
}
fn convert_text_run_to_glyphs(
text_run: Arc<TextRun>,
range: Range<ByteIndex>,
mut origin: Point2D<Au>,
) -> Vec<GlyphInstance> {
let mut glyphs = vec![];
for slice in text_run.natural_word_slices_in_visual_order(&range) {
for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) {
let glyph_advance = if glyph.char_is_space() {
glyph.advance() + text_run.extra_word_spacing
} else {
glyph.advance()
};
if !slice.glyphs.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let point = origin + glyph_offset.to_vector();
let glyph = GlyphInstance {
index: glyph.id(),
point: point.to_layout(),
};
glyphs.push(glyph);
}
origin.x += glyph_advance;
}
}
return glyphs;
}
impl FragmentDisplayListBuilding for Fragment {
fn collect_stacking_contexts_for_blocklike_fragment(
&mut self,
@ -946,14 +989,10 @@ impl FragmentDisplayListBuilding for Fragment {
},
}
let clip = if !border_radii.is_square() {
let clip = if !border_radii.is_zero() {
LocalClip::RoundedRect(
bounds.to_layout(),
ComplexClipRegion::new(
bounds.to_layout(),
border_radii.to_border_radius(),
ClipMode::Clip,
),
ComplexClipRegion::new(bounds.to_layout(), border_radii, ClipMode::Clip),
)
} else {
LocalClip::Rect(bounds.to_layout())
@ -1325,7 +1364,7 @@ impl FragmentDisplayListBuilding for Fragment {
),
blur_radius: box_shadow.base.blur.px(),
spread_radius: box_shadow.spread.px(),
border_radius: border_radius.to_border_radius(),
border_radius: border_radius,
clip_mode: if box_shadow.inset {
BoxShadowClipMode::Inset
} else {
@ -1420,7 +1459,7 @@ impl FragmentDisplayListBuilding for Fragment {
color: style.resolve_color(colors.bottom).to_layout(),
style: border_style.bottom.to_layout(),
},
radius: border_radius.to_border_radius(),
radius: border_radius,
})),
Either::Second(Image::Gradient(ref gradient)) => {
Some(match gradient.kind {
@ -1653,27 +1692,19 @@ impl FragmentDisplayListBuilding for Fragment {
let insertion_point_bounds;
let cursor;
if !self.style.writing_mode.is_vertical() {
insertion_point_bounds = Rect::new(
Point2D::new(
stacking_relative_border_box.origin.x + advance,
stacking_relative_border_box.origin.y,
),
Size2D::new(
INSERTION_POINT_LOGICAL_WIDTH,
stacking_relative_border_box.size.height,
),
insertion_point_bounds = rect(
stacking_relative_border_box.origin.x + advance,
stacking_relative_border_box.origin.y,
INSERTION_POINT_LOGICAL_WIDTH,
stacking_relative_border_box.size.height,
);
cursor = CursorKind::Text;
} else {
insertion_point_bounds = Rect::new(
Point2D::new(
stacking_relative_border_box.origin.x,
stacking_relative_border_box.origin.y + advance,
),
Size2D::new(
stacking_relative_border_box.size.width,
INSERTION_POINT_LOGICAL_WIDTH,
),
insertion_point_bounds = rect(
stacking_relative_border_box.origin.x,
stacking_relative_border_box.origin.y + advance,
stacking_relative_border_box.size.width,
INSERTION_POINT_LOGICAL_WIDTH,
);
cursor = CursorKind::VerticalText;
};
@ -1842,12 +1873,12 @@ impl FragmentDisplayListBuilding for Fragment {
// Adjust the clipping region as necessary to account for `border-radius`.
let build_local_clip = |style: &ComputedValues| {
let radii = build_border_radius_for_inner_rect(&stacking_relative_border_box, style);
if !radii.is_square() {
if !radii.is_zero() {
LocalClip::RoundedRect(
stacking_relative_border_box.to_layout(),
ComplexClipRegion::new(
stacking_relative_content_box.to_layout(),
radii.to_border_radius(),
radii,
ClipMode::Clip,
),
)
@ -1946,10 +1977,7 @@ impl FragmentDisplayListBuilding for Fragment {
iframe: pipeline_id,
}));
let size = Size2D::new(
item.bounds().size.width.to_f32_px(),
item.bounds().size.height.to_f32_px(),
);
let size = Size2D::new(item.bounds().size.width, item.bounds().size.height);
state
.iframe_sizes
.push((browsing_context_id, TypedSize2D::from_untyped(&size)));
@ -2067,8 +2095,8 @@ impl FragmentDisplayListBuilding for Fragment {
StackingContext::new(
id,
context_type,
&border_box,
&overflow,
border_box.to_layout(),
overflow.to_layout(),
self.effective_z_index(),
filters,
self.style().get_effects().mix_blend_mode.to_layout(),
@ -2100,7 +2128,7 @@ impl FragmentDisplayListBuilding for Fragment {
};
// 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() {
// TODO: Distinguish between 'sideways-lr' and 'sideways-rl' writing modes in CSS
// Writing Modes Level 4.
(TextOrientation::SidewaysRight, CursorKind::VerticalText)
@ -2181,14 +2209,22 @@ impl FragmentDisplayListBuilding for Fragment {
}
// Text
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
text_run: text_fragment.run.clone(),
range: text_fragment.range,
text_color: text_color.to_layout(),
orientation: orientation,
baseline_origin: baseline_origin,
})));
let glyphs = convert_text_run_to_glyphs(
text_fragment.run.clone(),
text_fragment.range,
baseline_origin,
);
if !glyphs.is_empty() {
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
text_run: text_fragment.run.clone(),
range: text_fragment.range,
baseline_origin: baseline_origin.to_layout(),
glyphs: glyphs,
font_key: text_fragment.run.font_key,
text_color: text_color.to_layout(),
})));
}
// TODO(#17715): emit text-emphasis marks here.
// (just push another TextDisplayItem?)
@ -2435,25 +2471,20 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
Rect::max_rect()
},
Some(transform) => {
let clip = Rect::new(
Point2D::new(
(clip.origin.x - origin.x).to_f32_px(),
(clip.origin.y - origin.y).to_f32_px(),
),
Size2D::new(clip.size.width.to_f32_px(), clip.size.height.to_f32_px()),
let clip = rect(
(clip.origin.x - origin.x).to_f32_px(),
(clip.origin.y - origin.y).to_f32_px(),
clip.size.width.to_f32_px(),
clip.size.height.to_f32_px(),
);
let clip = transform.transform_rect(&clip);
Rect::new(
Point2D::new(
Au::from_f32_px(clip.origin.x),
Au::from_f32_px(clip.origin.y),
),
Size2D::new(
Au::from_f32_px(clip.size.width),
Au::from_f32_px(clip.size.height),
),
rect(
Au::from_f32_px(clip.origin.x),
Au::from_f32_px(clip.origin.y),
Au::from_f32_px(clip.size.width),
Au::from_f32_px(clip.size.height),
)
},
None => Rect::zero(),
@ -2669,7 +2700,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let new_clip_scroll_index = state.add_clip_scroll_node(ClipScrollNode {
parent_index: self.clipping_and_scrolling().scrolling,
clip: ClippingRegion::from_rect(border_box),
clip: ClippingRegion::from_rect(border_box.to_layout()),
content_rect: LayoutRect::zero(),
node_type: ClipScrollNodeType::StickyFrame(sticky_frame_data),
});
@ -2709,10 +2740,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
};
let clip_rect = build_inner_border_box_for_border_rect(&border_box, &self.fragment.style);
let mut clip = ClippingRegion::from_rect(&clip_rect);
let mut clip = ClippingRegion::from_rect(clip_rect.to_layout());
let radii = build_border_radius_for_inner_rect(&border_box, &self.fragment.style);
if !radii.is_square() {
clip.intersect_with_rounded_rect(&clip_rect, &radii)
if !radii.is_zero() {
clip.intersect_with_rounded_rect(clip_rect.to_layout(), radii)
}
let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
@ -2776,7 +2807,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let new_index = state.add_clip_scroll_node(ClipScrollNode {
parent_index: self.clipping_and_scrolling().scrolling,
clip: ClippingRegion::from_rect(&clip_rect),
clip: ClippingRegion::from_rect(clip_rect.to_layout()),
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
node_type: ClipScrollNodeType::Clip,
});

View file

@ -7,14 +7,11 @@
// This might be achieved by sharing types between WR and Servo display lists, or
// completely converting layout to directly generate WebRender display lists, for example.
use app_units::Au;
use display_list::ToLayout;
use euclid::Point2D;
use gfx::display_list::{BorderDetails, BorderRadii, ClipScrollNode};
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingRegion, DisplayItem};
use gfx::display_list::{BorderDetails, ClipScrollNode};
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, DisplayItem};
use gfx::display_list::{DisplayList, StackingContextType};
use msg::constellation_msg::PipelineId;
use webrender_api::{self, ClipAndScrollInfo, ClipId, ClipMode, ComplexClipRegion};
use webrender_api::{self, ClipAndScrollInfo, ClipId};
use webrender_api::{DisplayListBuilder, LayoutTransform};
pub trait WebRenderDisplayListConverter {
@ -32,26 +29,11 @@ trait WebRenderDisplayItemConverter {
);
}
pub trait ToBorderRadius {
fn to_border_radius(&self) -> webrender_api::BorderRadius;
}
impl ToBorderRadius for BorderRadii<Au> {
fn to_border_radius(&self) -> webrender_api::BorderRadius {
webrender_api::BorderRadius {
top_left: self.top_left.to_layout(),
top_right: self.top_right.to_layout(),
bottom_left: self.bottom_left.to_layout(),
bottom_right: self.bottom_right.to_layout(),
}
}
}
impl WebRenderDisplayListConverter for DisplayList {
fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder {
let mut builder = DisplayListBuilder::with_capacity(
pipeline_id.to_webrender(),
self.bounds().size.to_layout(),
self.bounds().size,
1024 * 1024,
); // 1 MB of space
@ -81,7 +63,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
None => None,
};
webrender_api::LayoutPrimitiveInfo {
rect: self.base().bounds.to_layout(),
rect: self.base().bounds,
local_clip: self.base().local_clip,
// TODO(gw): Make use of the WR backface visibility functionality.
is_backface_visible: true,
@ -121,42 +103,13 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_rect(&self.prim_info(), item.color);
},
DisplayItem::Text(ref item) => {
let mut origin = item.baseline_origin.clone();
let mut glyphs = vec![];
for slice in item.text_run
.natural_word_slices_in_visual_order(&item.range)
{
for glyph in slice.glyphs.iter_glyphs_for_byte_range(&slice.range) {
let glyph_advance = if glyph.char_is_space() {
glyph.advance() + item.text_run.extra_word_spacing
} else {
glyph.advance()
};
if !slice.glyphs.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let x = (origin.x + glyph_offset.x).to_f32_px();
let y = (origin.y + glyph_offset.y).to_f32_px();
let point = webrender_api::LayoutPoint::new(x, y);
let glyph = webrender_api::GlyphInstance {
index: glyph.id(),
point: point,
};
glyphs.push(glyph);
}
origin.x = origin.x + glyph_advance;
}
}
if glyphs.len() > 0 {
builder.push_text(
&self.prim_info(),
&glyphs,
item.text_run.font_key,
item.text_color,
None,
);
}
builder.push_text(
&self.prim_info(),
&item.glyphs,
item.font_key,
item.text_color,
None,
);
},
DisplayItem::Image(ref item) => {
if let Some(id) = item.webrender_image.key {
@ -233,7 +186,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_line(
&self.prim_info(),
// TODO(gw): Use a better estimate for wavy line thickness.
(0.33 * item.base.bounds.size.height.to_f32_px()).ceil(),
(0.33 * item.base.bounds.size.height).ceil(),
webrender_api::LineOrientation::Horizontal,
&item.color,
item.style,
@ -279,7 +232,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
.map(|perspective| LayoutTransform::from_untyped(&perspective));
builder.push_stacking_context(
&webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds.to_layout()),
&webrender_api::LayoutPrimitiveInfo::new(stacking_context.bounds),
stacking_context.scroll_policy,
transform,
stacking_context.transform_style,
@ -292,13 +245,13 @@ impl WebRenderDisplayItemConverter for DisplayItem {
DisplayItem::DefineClipScrollNode(ref item) => {
let node = &clip_scroll_nodes[item.node_index.0];
let parent_id = get_id(clip_ids, node.parent_index);
let item_rect = node.clip.main.to_layout();
let item_rect = node.clip.main;
let webrender_id = match node.node_type {
ClipScrollNodeType::Clip => builder.define_clip_with_parent(
parent_id,
item_rect,
node.clip.get_complex_clips(),
node.clip.complex.clone(),
None,
),
ClipScrollNodeType::ScrollFrame(scroll_sensitivity, external_id) => builder
@ -306,8 +259,8 @@ impl WebRenderDisplayItemConverter for DisplayItem {
parent_id,
Some(external_id),
node.content_rect,
node.clip.main.to_layout(),
node.clip.get_complex_clips(),
node.clip.main,
node.clip.complex.clone(),
None,
scroll_sensitivity,
),
@ -331,22 +284,3 @@ impl WebRenderDisplayItemConverter for DisplayItem {
}
}
}
trait ToWebRenderClip {
fn get_complex_clips(&self) -> Vec<ComplexClipRegion>;
}
impl ToWebRenderClip for ClippingRegion {
fn get_complex_clips(&self) -> Vec<ComplexClipRegion> {
self.complex
.iter()
.map(|complex_clipping_region| {
ComplexClipRegion::new(
complex_clipping_region.rect.to_layout(),
complex_clipping_region.radii.to_border_radius(),
ClipMode::Clip,
)
})
.collect()
}
}