tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

border.rs (50375B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU};
      6 use api::{NormalBorder as ApiNormalBorder, RepeatMode};
      7 use api::units::*;
      8 use crate::clip::ClipNodeId;
      9 use crate::ellipse::Ellipse;
     10 use euclid::vec2;
     11 use crate::scene_building::SceneBuilder;
     12 use crate::spatial_tree::SpatialNodeIndex;
     13 use crate::gpu_types::{BorderInstance, BorderSegment, BrushFlags};
     14 use crate::prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
     15 use crate::prim_store::borders::{NormalBorderPrim, NormalBorderData};
     16 use crate::util::{lerp, RectHelpers};
     17 use crate::internal_types::LayoutPrimitiveInfo;
     18 use crate::segment::EdgeAaSegmentMask;
     19 
     20 // Using 2048 as the maximum radius in device space before which we
     21 // start stretching is up for debate.
     22 // the value must be chosen so that the corners will not use an
     23 // unreasonable amount of memory but should allow crisp corners in the
     24 // common cases.
     25 
     26 /// Maximum resolution in device pixels at which borders are rasterized.
     27 pub const MAX_BORDER_RESOLUTION: u32 = 2048;
     28 /// Maximum number of dots or dashes per segment to avoid freezing and filling up
     29 /// memory with unreasonable inputs. It would be better to address this by not building
     30 /// a list of per-dot information in the first place.
     31 pub const MAX_DASH_COUNT: u32 = 2048;
     32 
     33 // TODO(gw): Perhaps there is a better way to store
     34 //           the border cache key than duplicating
     35 //           all the border structs with hashable
     36 //           variants...
     37 
     38 #[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
     39 #[cfg_attr(feature = "capture", derive(Serialize))]
     40 #[cfg_attr(feature = "replay", derive(Deserialize))]
     41 pub struct BorderRadiusAu {
     42    pub top_left: LayoutSizeAu,
     43    pub top_right: LayoutSizeAu,
     44    pub bottom_left: LayoutSizeAu,
     45    pub bottom_right: LayoutSizeAu,
     46 }
     47 
     48 impl From<BorderRadius> for BorderRadiusAu {
     49    fn from(radius: BorderRadius) -> BorderRadiusAu {
     50        BorderRadiusAu {
     51            top_left: radius.top_left.to_au(),
     52            top_right: radius.top_right.to_au(),
     53            bottom_right: radius.bottom_right.to_au(),
     54            bottom_left: radius.bottom_left.to_au(),
     55        }
     56    }
     57 }
     58 
     59 impl From<BorderRadiusAu> for BorderRadius {
     60    fn from(radius: BorderRadiusAu) -> Self {
     61        BorderRadius {
     62            top_left: LayoutSize::from_au(radius.top_left),
     63            top_right: LayoutSize::from_au(radius.top_right),
     64            bottom_right: LayoutSize::from_au(radius.bottom_right),
     65            bottom_left: LayoutSize::from_au(radius.bottom_left),
     66        }
     67    }
     68 }
     69 
     70 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
     71 #[cfg_attr(feature = "capture", derive(Serialize))]
     72 #[cfg_attr(feature = "replay", derive(Deserialize))]
     73 pub struct BorderSideAu {
     74    pub color: ColorU,
     75    pub style: BorderStyle,
     76 }
     77 
     78 impl From<BorderSide> for BorderSideAu {
     79    fn from(side: BorderSide) -> Self {
     80        BorderSideAu {
     81            color: side.color.into(),
     82            style: side.style,
     83        }
     84    }
     85 }
     86 
     87 impl From<BorderSideAu> for BorderSide {
     88    fn from(side: BorderSideAu) -> Self {
     89        BorderSide {
     90            color: side.color.into(),
     91            style: side.style,
     92        }
     93    }
     94 }
     95 
     96 #[cfg_attr(feature = "capture", derive(Serialize))]
     97 #[cfg_attr(feature = "replay", derive(Deserialize))]
     98 #[derive(Debug, Clone, Hash, Eq, MallocSizeOf, PartialEq)]
     99 pub struct NormalBorderAu {
    100    pub left: BorderSideAu,
    101    pub right: BorderSideAu,
    102    pub top: BorderSideAu,
    103    pub bottom: BorderSideAu,
    104    pub radius: BorderRadiusAu,
    105    /// Whether to apply anti-aliasing on the border corners.
    106    ///
    107    /// Note that for this to be `false` and work, this requires the borders to
    108    /// be solid, and no border-radius.
    109    pub do_aa: bool,
    110 }
    111 
    112 impl NormalBorderAu {
    113    // Construct a border based upon self with color
    114    pub fn with_color(&self, color: ColorU) -> Self {
    115        let mut b = self.clone();
    116        b.left.color = color;
    117        b.right.color = color;
    118        b.top.color = color;
    119        b.bottom.color = color;
    120        b
    121    }
    122 }
    123 
    124 impl From<ApiNormalBorder> for NormalBorderAu {
    125    fn from(border: ApiNormalBorder) -> Self {
    126        NormalBorderAu {
    127            left: border.left.into(),
    128            right: border.right.into(),
    129            top: border.top.into(),
    130            bottom: border.bottom.into(),
    131            radius: border.radius.into(),
    132            do_aa: border.do_aa,
    133        }
    134    }
    135 }
    136 
    137 impl From<NormalBorderAu> for ApiNormalBorder {
    138    fn from(border: NormalBorderAu) -> Self {
    139        ApiNormalBorder {
    140            left: border.left.into(),
    141            right: border.right.into(),
    142            top: border.top.into(),
    143            bottom: border.bottom.into(),
    144            radius: border.radius.into(),
    145            do_aa: border.do_aa,
    146        }
    147    }
    148 }
    149 
    150 /// Cache key that uniquely identifies a border
    151 /// segment in the render task cache.
    152 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
    153 #[cfg_attr(feature = "capture", derive(Serialize))]
    154 #[cfg_attr(feature = "replay", derive(Deserialize))]
    155 pub struct BorderSegmentCacheKey {
    156    pub size: LayoutSizeAu,
    157    pub radius: LayoutSizeAu,
    158    pub side0: BorderSideAu,
    159    pub side1: BorderSideAu,
    160    pub segment: BorderSegment,
    161    pub do_aa: bool,
    162    pub h_adjacent_corner_outer: LayoutPointAu,
    163    pub h_adjacent_corner_radius: LayoutSizeAu,
    164    pub v_adjacent_corner_outer: LayoutPointAu,
    165    pub v_adjacent_corner_radius: LayoutSizeAu,
    166 }
    167 
    168 pub fn ensure_no_corner_overlap(
    169    radius: &mut BorderRadius,
    170    size: LayoutSize,
    171 ) {
    172    let mut ratio = 1.0;
    173    let top_left_radius = &mut radius.top_left;
    174    let top_right_radius = &mut radius.top_right;
    175    let bottom_right_radius = &mut radius.bottom_right;
    176    let bottom_left_radius = &mut radius.bottom_left;
    177 
    178    if size.width > 0.0 {
    179        let sum = top_left_radius.width + top_right_radius.width;
    180        if size.width < sum {
    181            ratio = f32::min(ratio, size.width / sum);
    182        }
    183 
    184        let sum = bottom_left_radius.width + bottom_right_radius.width;
    185        if size.width < sum {
    186            ratio = f32::min(ratio, size.width / sum);
    187        }
    188    }
    189 
    190    if size.height > 0.0 {
    191        let sum = top_left_radius.height + bottom_left_radius.height;
    192        if size.height < sum {
    193            ratio = f32::min(ratio, size.height / sum);
    194        }
    195 
    196        let sum = top_right_radius.height + bottom_right_radius.height;
    197        if size.height < sum {
    198            ratio = f32::min(ratio, size.height / sum);
    199        }
    200    }
    201 
    202    if ratio < 1. {
    203        top_left_radius.width *= ratio;
    204        top_left_radius.height *= ratio;
    205 
    206        top_right_radius.width *= ratio;
    207        top_right_radius.height *= ratio;
    208 
    209        bottom_left_radius.width *= ratio;
    210        bottom_left_radius.height *= ratio;
    211 
    212        bottom_right_radius.width *= ratio;
    213        bottom_right_radius.height *= ratio;
    214    }
    215 }
    216 
    217 impl<'a> SceneBuilder<'a> {
    218    pub fn add_normal_border(
    219        &mut self,
    220        info: &LayoutPrimitiveInfo,
    221        border: &ApiNormalBorder,
    222        widths: LayoutSideOffsets,
    223        spatial_node_index: SpatialNodeIndex,
    224        clip_node_id: ClipNodeId,
    225    ) {
    226        let mut border = *border;
    227        ensure_no_corner_overlap(&mut border.radius, info.rect.size());
    228 
    229        self.add_primitive(
    230            spatial_node_index,
    231            clip_node_id,
    232            info,
    233            Vec::new(),
    234            NormalBorderPrim {
    235                border: border.into(),
    236                widths: widths.to_au(),
    237            },
    238        );
    239    }
    240 }
    241 
    242 pub trait BorderSideHelpers {
    243    fn border_color(&self, is_inner_border: bool) -> ColorF;
    244 }
    245 
    246 impl BorderSideHelpers for BorderSide {
    247    fn border_color(&self, is_inner_border: bool) -> ColorF {
    248        let lighter = match self.style {
    249            BorderStyle::Inset => is_inner_border,
    250            BorderStyle::Outset => !is_inner_border,
    251            _ => return self.color,
    252        };
    253 
    254        // The modulate colors below are not part of the specification. They are
    255        // derived from the Gecko source code and experimentation, and used to
    256        // modulate the colors in order to generate colors for the inset/outset
    257        // and groove/ridge border styles.
    258        //
    259        // NOTE(emilio): Gecko at least takes the background color into
    260        // account, should we do the same? Looks a bit annoying for this.
    261        //
    262        // NOTE(emilio): If you change this algorithm, do the same change on
    263        // get_colors_for_side in cs_border_segment.glsl.
    264        if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
    265            let scale = if lighter { 1.0 } else { 2.0 / 3.0 };
    266            return self.color.scale_rgb(scale)
    267        }
    268 
    269        let black = if lighter { 0.7 } else { 0.3 };
    270        ColorF::new(black, black, black, self.color.a)
    271    }
    272 }
    273 
    274 /// The kind of border corner clip.
    275 #[repr(C)]
    276 #[derive(Copy, Debug, Clone, PartialEq)]
    277 pub enum BorderClipKind {
    278    DashCorner = 1,
    279    DashEdge = 2,
    280    Dot = 3,
    281 }
    282 
    283 fn compute_outer_and_clip_sign(
    284    corner_segment: BorderSegment,
    285    radius: DeviceSize,
    286 ) -> (DevicePoint, DeviceVector2D) {
    287    let outer_scale = match corner_segment {
    288        BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
    289        BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
    290        BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
    291        BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
    292        _ => panic!("bug: expected a corner segment"),
    293    };
    294    let outer = DevicePoint::new(
    295        outer_scale.x * radius.width,
    296        outer_scale.y * radius.height,
    297    );
    298 
    299    let clip_sign = DeviceVector2D::new(
    300        1.0 - 2.0 * outer_scale.x,
    301        1.0 - 2.0 * outer_scale.y,
    302    );
    303 
    304    (outer, clip_sign)
    305 }
    306 
    307 fn write_dashed_corner_instances(
    308    corner_radius: DeviceSize,
    309    widths: DeviceSize,
    310    segment: BorderSegment,
    311    base_instance: &BorderInstance,
    312    instances: &mut Vec<BorderInstance>,
    313 ) -> Result<(), ()> {
    314    let ellipse = Ellipse::new(corner_radius);
    315 
    316    let average_border_width = 0.5 * (widths.width + widths.height);
    317 
    318    let (_half_dash, num_half_dashes) =
    319        compute_half_dash(average_border_width, ellipse.total_arc_length);
    320 
    321    if num_half_dashes == 0 {
    322        return Err(());
    323    }
    324 
    325    let num_half_dashes = num_half_dashes.min(MAX_DASH_COUNT);
    326 
    327    let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
    328 
    329    let instance_count = num_half_dashes / 4 + 1;
    330    instances.reserve(instance_count as usize);
    331 
    332    let half_dash_arc_length =
    333        ellipse.total_arc_length / num_half_dashes as f32;
    334    let dash_length = 2. * half_dash_arc_length;
    335 
    336    let mut current_length = 0.;
    337    for i in 0..instance_count {
    338        let arc_length0 = current_length;
    339        current_length += if i == 0 {
    340            half_dash_arc_length
    341        } else {
    342            dash_length
    343        };
    344 
    345        let arc_length1 = current_length;
    346        current_length += dash_length;
    347 
    348        let alpha = ellipse.find_angle_for_arc_length(arc_length0);
    349        let beta = ellipse.find_angle_for_arc_length(arc_length1);
    350 
    351        let (point0, tangent0) = ellipse.get_point_and_tangent(alpha);
    352        let (point1, tangent1) = ellipse.get_point_and_tangent(beta);
    353 
    354        let point0 = DevicePoint::new(
    355            outer.x + clip_sign.x * (corner_radius.width - point0.x),
    356            outer.y + clip_sign.y * (corner_radius.height - point0.y),
    357        );
    358 
    359        let tangent0 = DeviceVector2D::new(
    360            -tangent0.x * clip_sign.x,
    361            -tangent0.y * clip_sign.y,
    362        );
    363 
    364        let point1 = DevicePoint::new(
    365            outer.x + clip_sign.x * (corner_radius.width - point1.x),
    366            outer.y + clip_sign.y * (corner_radius.height - point1.y),
    367        );
    368 
    369        let tangent1 = DeviceVector2D::new(
    370            -tangent1.x * clip_sign.x,
    371            -tangent1.y * clip_sign.y,
    372        );
    373 
    374        instances.push(BorderInstance {
    375            flags: base_instance.flags | ((BorderClipKind::DashCorner as i32) << 24),
    376            clip_params: [
    377                point0.x,
    378                point0.y,
    379                tangent0.x,
    380                tangent0.y,
    381                point1.x,
    382                point1.y,
    383                tangent1.x,
    384                tangent1.y,
    385            ],
    386            .. *base_instance
    387        });
    388    }
    389 
    390    Ok(())
    391 }
    392 
    393 fn write_dotted_corner_instances(
    394    corner_radius: DeviceSize,
    395    widths: DeviceSize,
    396    segment: BorderSegment,
    397    base_instance: &BorderInstance,
    398    instances: &mut Vec<BorderInstance>,
    399 ) -> Result<(), ()> {
    400    let mut corner_radius = corner_radius;
    401    if corner_radius.width < (widths.width / 2.0) {
    402        corner_radius.width = 0.0;
    403    }
    404    if corner_radius.height < (widths.height / 2.0) {
    405        corner_radius.height = 0.0;
    406    }
    407 
    408    let (ellipse, max_dot_count) =
    409        if corner_radius.width == 0. && corner_radius.height == 0. {
    410            (Ellipse::new(corner_radius), 1)
    411        } else {
    412            // The centers of dots follow an ellipse along the middle of the
    413            // border radius.
    414            let inner_radius = (corner_radius - widths * 0.5).abs();
    415            let ellipse = Ellipse::new(inner_radius);
    416 
    417            // Allocate a "worst case" number of dot clips. This can be
    418            // calculated by taking the minimum edge radius, since that
    419            // will result in the maximum number of dots along the path.
    420            let min_diameter = widths.width.min(widths.height);
    421 
    422            // Get the number of circles (assuming spacing of one diameter
    423            // between dots).
    424            let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
    425 
    426            // Add space for one extra dot since they are centered at the
    427            // start of the arc.
    428            (ellipse, max_dot_count.ceil() as usize)
    429        };
    430 
    431    if max_dot_count == 0 {
    432        return Err(());
    433    }
    434 
    435    if max_dot_count == 1 {
    436        let dot_diameter = lerp(widths.width, widths.height, 0.5);
    437        instances.push(BorderInstance {
    438            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
    439            clip_params: [
    440                widths.width / 2.0, widths.height / 2.0, 0.5 * dot_diameter, 0.,
    441                0., 0., 0., 0.,
    442            ],
    443            .. *base_instance
    444        });
    445        return Ok(());
    446    }
    447 
    448    let max_dot_count = max_dot_count.min(MAX_DASH_COUNT as usize);
    449 
    450    // FIXME(emilio): Should probably use SmallVec.
    451    let mut forward_dots = Vec::with_capacity(max_dot_count / 2 + 1);
    452    let mut back_dots = Vec::with_capacity(max_dot_count / 2 + 1);
    453    let mut leftover_arc_length = 0.0;
    454 
    455    // Alternate between adding dots at the start and end of the
    456    // ellipse arc. This ensures that we always end up with an exact
    457    // half dot at each end of the arc, to match up with the edges.
    458    forward_dots.push(DotInfo::new(widths.width, widths.width));
    459    back_dots.push(DotInfo::new(
    460        ellipse.total_arc_length - widths.height,
    461        widths.height,
    462    ));
    463 
    464    let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
    465    for dot_index in 0 .. max_dot_count {
    466        let prev_forward_pos = *forward_dots.last().unwrap();
    467        let prev_back_pos = *back_dots.last().unwrap();
    468 
    469        // Select which end of the arc to place a dot from.
    470        // This just alternates between the start and end of
    471        // the arc, which ensures that there is always an
    472        // exact half-dot at each end of the ellipse.
    473        let going_forward = dot_index & 1 == 0;
    474 
    475        let (next_dot_pos, leftover) = if going_forward {
    476            let next_dot_pos =
    477                prev_forward_pos.arc_pos + 2.0 * prev_forward_pos.diameter;
    478            (next_dot_pos, prev_back_pos.arc_pos - next_dot_pos)
    479        } else {
    480            let next_dot_pos = prev_back_pos.arc_pos - 2.0 * prev_back_pos.diameter;
    481            (next_dot_pos, next_dot_pos - prev_forward_pos.arc_pos)
    482        };
    483 
    484        // Use a lerp between each edge's dot
    485        // diameter, based on the linear distance
    486        // along the arc to get the diameter of the
    487        // dot at this arc position.
    488        let t = next_dot_pos / ellipse.total_arc_length;
    489        let dot_diameter = lerp(widths.width, widths.height, t);
    490 
    491        // If we can't fit a dot, bail out.
    492        if leftover < dot_diameter {
    493            leftover_arc_length = leftover;
    494            break;
    495        }
    496 
    497        // We can place a dot!
    498        let dot = DotInfo::new(next_dot_pos, dot_diameter);
    499        if going_forward {
    500            forward_dots.push(dot);
    501        } else {
    502            back_dots.push(dot);
    503        }
    504    }
    505 
    506    // Now step through the dots, and distribute any extra
    507    // leftover space on the arc between them evenly. Once
    508    // the final arc position is determined, generate the correct
    509    // arc positions and angles that get passed to the clip shader.
    510    let number_of_dots = forward_dots.len() + back_dots.len();
    511    let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32;
    512 
    513    let create_dot_data = |arc_length: f32, dot_radius: f32| -> [f32; 8] {
    514        // Represents the GPU data for drawing a single dot to a clip mask. The order
    515        // these are specified must stay in sync with the way this data is read in the
    516        // dot clip shader.
    517        let theta = ellipse.find_angle_for_arc_length(arc_length);
    518        let (center, _) = ellipse.get_point_and_tangent(theta);
    519 
    520        let center = DevicePoint::new(
    521            outer.x + clip_sign.x * (corner_radius.width - center.x),
    522            outer.y + clip_sign.y * (corner_radius.height - center.y),
    523        );
    524 
    525        [center.x, center.y, dot_radius, 0.0, 0.0, 0.0, 0.0, 0.0]
    526    };
    527 
    528    instances.reserve(number_of_dots);
    529    for (i, dot) in forward_dots.iter().enumerate() {
    530        let extra_dist = i as f32 * extra_space_per_dot;
    531        instances.push(BorderInstance {
    532            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
    533            clip_params: create_dot_data(dot.arc_pos + extra_dist, 0.5 * dot.diameter),
    534            .. *base_instance
    535        });
    536    }
    537 
    538    for (i, dot) in back_dots.iter().enumerate() {
    539        let extra_dist = i as f32 * extra_space_per_dot;
    540        instances.push(BorderInstance {
    541            flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
    542            clip_params: create_dot_data(dot.arc_pos - extra_dist, 0.5 * dot.diameter),
    543            .. *base_instance
    544        });
    545    }
    546 
    547    Ok(())
    548 }
    549 
    550 #[derive(Copy, Clone, Debug)]
    551 struct DotInfo {
    552    arc_pos: f32,
    553    diameter: f32,
    554 }
    555 
    556 impl DotInfo {
    557    fn new(arc_pos: f32, diameter: f32) -> DotInfo {
    558        DotInfo { arc_pos, diameter }
    559    }
    560 }
    561 
    562 /// Information needed to place and draw a border edge.
    563 #[derive(Debug)]
    564 struct EdgeInfo {
    565    /// Offset in local space to place the edge from origin.
    566    local_offset: f32,
    567    /// Size of the edge in local space.
    568    local_size: f32,
    569    /// Local stretch size for this edge (repeat past this).
    570    stretch_size: f32,
    571 }
    572 
    573 impl EdgeInfo {
    574    fn new(
    575        local_offset: f32,
    576        local_size: f32,
    577        stretch_size: f32,
    578    ) -> Self {
    579        Self {
    580            local_offset,
    581            local_size,
    582            stretch_size,
    583        }
    584    }
    585 }
    586 
    587 // Given a side width and the available space, compute the half-dash (half of
    588 // the 'on' segment) and the count of them for a given segment.
    589 fn compute_half_dash(side_width: f32, total_size: f32) -> (f32, u32) {
    590    let half_dash = side_width * 1.5;
    591    // 16k dashes should be enough for anyone
    592    let num_half_dashes = (total_size / half_dash).ceil().min(16.0 * 1024.0) as u32;
    593 
    594    if num_half_dashes == 0 {
    595        return (0., 0);
    596    }
    597 
    598    // TODO(emilio): Gecko has some other heuristics here to start with a full
    599    // dash when the border side is zero, for example. We might consider those
    600    // in the future.
    601    let num_half_dashes = if num_half_dashes % 4 != 0 {
    602        num_half_dashes + 4 - num_half_dashes % 4
    603    } else {
    604        num_half_dashes
    605    };
    606 
    607    let half_dash = total_size / num_half_dashes as f32;
    608    (half_dash, num_half_dashes)
    609 }
    610 
    611 
    612 // Get the needed size in device pixels for an edge,
    613 // based on the border style of that edge. This is used
    614 // to determine how big the render task should be.
    615 fn get_edge_info(
    616    style: BorderStyle,
    617    side_width: f32,
    618    avail_size: f32,
    619 ) -> EdgeInfo {
    620    // To avoid division by zero below.
    621    if side_width <= 0.0 || avail_size <= 0.0 {
    622        return EdgeInfo::new(0.0, 0.0, 0.0);
    623    }
    624 
    625    match style {
    626        BorderStyle::Dashed => {
    627            // Basically, two times the dash size.
    628            let (half_dash, _num_half_dashes) =
    629                compute_half_dash(side_width, avail_size);
    630            let stretch_size = 2.0 * 2.0 * half_dash;
    631            EdgeInfo::new(0., avail_size, stretch_size)
    632        }
    633        BorderStyle::Dotted => {
    634            let dot_and_space_size = 2.0 * side_width;
    635            if avail_size < dot_and_space_size * 0.75 {
    636                return EdgeInfo::new(0.0, 0.0, 0.0);
    637            }
    638            let approx_dot_count = avail_size / dot_and_space_size;
    639            let dot_count = approx_dot_count.floor().max(1.0);
    640            let used_size = dot_count * dot_and_space_size;
    641            let extra_space = avail_size - used_size;
    642            let stretch_size = dot_and_space_size;
    643            let offset = (extra_space * 0.5).round();
    644            EdgeInfo::new(offset, used_size, stretch_size)
    645        }
    646        _ => {
    647            EdgeInfo::new(0.0, avail_size, 8.0)
    648        }
    649    }
    650 }
    651 
    652 /// Create the set of border segments and render task
    653 /// cache keys for a given CSS border.
    654 pub fn create_border_segments(
    655    size: LayoutSize,
    656    border: &ApiNormalBorder,
    657    widths: &LayoutSideOffsets,
    658    border_segments: &mut Vec<BorderSegmentInfo>,
    659    brush_segments: &mut Vec<BrushSegment>,
    660 ) {
    661    let rect = LayoutRect::from_size(size);
    662 
    663    let overlap = LayoutSize::new(
    664        (widths.left + widths.right - size.width).max(0.0),
    665        (widths.top + widths.bottom - size.height).max(0.0),
    666    );
    667    let non_overlapping_widths = LayoutSideOffsets::new(
    668        widths.top - overlap.height / 2.0,
    669        widths.right - overlap.width / 2.0,
    670        widths.bottom - overlap.height / 2.0,
    671        widths.left - overlap.width / 2.0,
    672    );
    673 
    674    let local_size_tl = LayoutSize::new(
    675        border.radius.top_left.width.max(widths.left),
    676        border.radius.top_left.height.max(widths.top),
    677    );
    678    let local_size_tr = LayoutSize::new(
    679        border.radius.top_right.width.max(widths.right),
    680        border.radius.top_right.height.max(widths.top),
    681    );
    682    let local_size_br = LayoutSize::new(
    683        border.radius.bottom_right.width.max(widths.right),
    684        border.radius.bottom_right.height.max(widths.bottom),
    685    );
    686    let local_size_bl = LayoutSize::new(
    687        border.radius.bottom_left.width.max(widths.left),
    688        border.radius.bottom_left.height.max(widths.bottom),
    689    );
    690 
    691    let top_edge_info = get_edge_info(
    692        border.top.style,
    693        widths.top,
    694        rect.width() - local_size_tl.width - local_size_tr.width,
    695    );
    696    let bottom_edge_info = get_edge_info(
    697        border.bottom.style,
    698        widths.bottom,
    699        rect.width() - local_size_bl.width - local_size_br.width,
    700    );
    701 
    702    let left_edge_info = get_edge_info(
    703        border.left.style,
    704        widths.left,
    705        rect.height() - local_size_tl.height - local_size_bl.height,
    706    );
    707    let right_edge_info = get_edge_info(
    708        border.right.style,
    709        widths.right,
    710        rect.height() - local_size_tr.height - local_size_br.height,
    711    );
    712 
    713    add_edge_segment(
    714        LayoutRect::from_floats(
    715            rect.min.x,
    716            rect.min.y + local_size_tl.height + left_edge_info.local_offset,
    717            rect.min.x + non_overlapping_widths.left,
    718            rect.min.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size,
    719        ),
    720        &left_edge_info,
    721        border.left,
    722        non_overlapping_widths.left,
    723        BorderSegment::Left,
    724        EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
    725        brush_segments,
    726        border_segments,
    727        border.do_aa,
    728    );
    729    add_edge_segment(
    730        LayoutRect::from_floats(
    731            rect.min.x + local_size_tl.width + top_edge_info.local_offset,
    732            rect.min.y,
    733            rect.min.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size,
    734            rect.min.y + non_overlapping_widths.top,
    735        ),
    736        &top_edge_info,
    737        border.top,
    738        non_overlapping_widths.top,
    739        BorderSegment::Top,
    740        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
    741        brush_segments,
    742        border_segments,
    743        border.do_aa,
    744    );
    745    add_edge_segment(
    746        LayoutRect::from_floats(
    747            rect.min.x + rect.width() - non_overlapping_widths.right,
    748            rect.min.y + local_size_tr.height + right_edge_info.local_offset,
    749            rect.min.x + rect.width(),
    750            rect.min.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size,
    751        ),
    752        &right_edge_info,
    753        border.right,
    754        non_overlapping_widths.right,
    755        BorderSegment::Right,
    756        EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
    757        brush_segments,
    758        border_segments,
    759        border.do_aa,
    760    );
    761    add_edge_segment(
    762        LayoutRect::from_floats(
    763            rect.min.x + local_size_bl.width + bottom_edge_info.local_offset,
    764            rect.min.y + rect.height() - non_overlapping_widths.bottom,
    765            rect.min.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size,
    766            rect.min.y + rect.height(),
    767        ),
    768        &bottom_edge_info,
    769        border.bottom,
    770        non_overlapping_widths.bottom,
    771        BorderSegment::Bottom,
    772        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
    773        brush_segments,
    774        border_segments,
    775        border.do_aa,
    776    );
    777 
    778    add_corner_segment(
    779        LayoutRect::from_floats(
    780            rect.min.x,
    781            rect.min.y,
    782            rect.min.x + local_size_tl.width,
    783            rect.min.y + local_size_tl.height,
    784        ),
    785        LayoutRect::from_floats(
    786            rect.min.x,
    787            rect.min.y,
    788            rect.max.x - non_overlapping_widths.right,
    789            rect.max.y - non_overlapping_widths.bottom
    790        ),
    791        border.left,
    792        border.top,
    793        LayoutSize::new(widths.left, widths.top),
    794        border.radius.top_left,
    795        BorderSegment::TopLeft,
    796        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
    797        rect.top_right(),
    798        border.radius.top_right,
    799        rect.bottom_left(),
    800        border.radius.bottom_left,
    801        brush_segments,
    802        border_segments,
    803        border.do_aa,
    804    );
    805    add_corner_segment(
    806        LayoutRect::from_floats(
    807            rect.min.x + rect.width() - local_size_tr.width,
    808            rect.min.y,
    809            rect.min.x + rect.width(),
    810            rect.min.y + local_size_tr.height,
    811        ),
    812        LayoutRect::from_floats(
    813            rect.min.x + non_overlapping_widths.left,
    814            rect.min.y,
    815            rect.max.x,
    816            rect.max.y - non_overlapping_widths.bottom,
    817        ),
    818        border.top,
    819        border.right,
    820        LayoutSize::new(widths.right, widths.top),
    821        border.radius.top_right,
    822        BorderSegment::TopRight,
    823        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
    824        rect.min,
    825        border.radius.top_left,
    826        rect.max,
    827        border.radius.bottom_right,
    828        brush_segments,
    829        border_segments,
    830        border.do_aa,
    831    );
    832    add_corner_segment(
    833        LayoutRect::from_floats(
    834            rect.min.x + rect.width() - local_size_br.width,
    835            rect.min.y + rect.height() - local_size_br.height,
    836            rect.min.x + rect.width(),
    837            rect.min.y + rect.height(),
    838        ),
    839        LayoutRect::from_floats(
    840            rect.min.x + non_overlapping_widths.left,
    841            rect.min.y + non_overlapping_widths.top,
    842            rect.max.x,
    843            rect.max.y,
    844        ),
    845        border.right,
    846        border.bottom,
    847        LayoutSize::new(widths.right, widths.bottom),
    848        border.radius.bottom_right,
    849        BorderSegment::BottomRight,
    850        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
    851        rect.bottom_left(),
    852        border.radius.bottom_left,
    853        rect.top_right(),
    854        border.radius.top_right,
    855        brush_segments,
    856        border_segments,
    857        border.do_aa,
    858    );
    859    add_corner_segment(
    860        LayoutRect::from_floats(
    861            rect.min.x,
    862            rect.min.y + rect.height() - local_size_bl.height,
    863            rect.min.x + local_size_bl.width,
    864            rect.min.y + rect.height(),
    865        ),
    866        LayoutRect::from_floats(
    867            rect.min.x,
    868            rect.min.y + non_overlapping_widths.top,
    869            rect.max.x - non_overlapping_widths.right,
    870            rect.max.y,
    871        ),
    872        border.bottom,
    873        border.left,
    874        LayoutSize::new(widths.left, widths.bottom),
    875        border.radius.bottom_left,
    876        BorderSegment::BottomLeft,
    877        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
    878        rect.max,
    879        border.radius.bottom_right,
    880        rect.min,
    881        border.radius.top_left,
    882        brush_segments,
    883        border_segments,
    884        border.do_aa,
    885    );
    886 }
    887 
    888 /// Computes the maximum scale that we allow for this set of border parameters.
    889 /// capping the scale will result in rendering very large corners at a lower
    890 /// resolution and stretching them, so they will have the right shape, but
    891 /// blurrier.
    892 pub fn get_max_scale_for_border(
    893    border_data: &NormalBorderData,
    894 ) -> LayoutToDeviceScale {
    895    let mut r = 1.0;
    896    for segment in &border_data.border_segments {
    897        let size = segment.local_task_size;
    898        r = size.width.max(size.height.max(r));
    899    }
    900 
    901    LayoutToDeviceScale::new(MAX_BORDER_RESOLUTION as f32 / r)
    902 }
    903 
    904 fn add_segment(
    905    task_rect: DeviceRect,
    906    style0: BorderStyle,
    907    style1: BorderStyle,
    908    color0: ColorF,
    909    color1: ColorF,
    910    segment: BorderSegment,
    911    instances: &mut Vec<BorderInstance>,
    912    widths: DeviceSize,
    913    radius: DeviceSize,
    914    do_aa: bool,
    915    h_adjacent_corner_outer: DevicePoint,
    916    h_adjacent_corner_radius: DeviceSize,
    917    v_adjacent_corner_outer: DevicePoint,
    918    v_adjacent_corner_radius: DeviceSize,
    919 ) {
    920    let base_flags = (segment as i32) |
    921                     ((style0 as i32) << 8) |
    922                     ((style1 as i32) << 16) |
    923                     ((do_aa as i32) << 28);
    924 
    925    let base_instance = BorderInstance {
    926        task_origin: DevicePoint::zero(),
    927        local_rect: task_rect,
    928        flags: base_flags,
    929        color0: color0.premultiplied(),
    930        color1: color1.premultiplied(),
    931        widths,
    932        radius,
    933        clip_params: [0.0; 8],
    934    };
    935 
    936    match segment {
    937        BorderSegment::TopLeft |
    938        BorderSegment::TopRight |
    939        BorderSegment::BottomLeft |
    940        BorderSegment::BottomRight => {
    941            // TODO(gw): Similarly to the old border code, we don't correctly handle a a corner
    942            //           that is dashed on one edge, and dotted on another. We can handle this
    943            //           in the future by submitting two instances, each one with one side
    944            //           color set to have an alpha of 0.
    945            if (style0 == BorderStyle::Dotted && style1 == BorderStyle::Dashed) ||
    946               (style0 == BorderStyle::Dashed && style0 == BorderStyle::Dotted) {
    947                warn!("TODO: Handle a corner with dotted / dashed transition.");
    948            }
    949 
    950            let dashed_or_dotted_corner = match style0 {
    951                BorderStyle::Dashed => {
    952                    write_dashed_corner_instances(
    953                        radius,
    954                        widths,
    955                        segment,
    956                        &base_instance,
    957                        instances,
    958                    )
    959                }
    960                BorderStyle::Dotted => {
    961                    write_dotted_corner_instances(
    962                        radius,
    963                        widths,
    964                        segment,
    965                        &base_instance,
    966                        instances,
    967                    )
    968                }
    969                _ => Err(()),
    970            };
    971 
    972            if dashed_or_dotted_corner.is_err() {
    973                let clip_params = [
    974                    h_adjacent_corner_outer.x,
    975                    h_adjacent_corner_outer.y,
    976                    h_adjacent_corner_radius.width,
    977                    h_adjacent_corner_radius.height,
    978                    v_adjacent_corner_outer.x,
    979                    v_adjacent_corner_outer.y,
    980                    v_adjacent_corner_radius.width,
    981                    v_adjacent_corner_radius.height,
    982                ];
    983 
    984                instances.push(BorderInstance {
    985                    clip_params,
    986                    ..base_instance
    987                });
    988            }
    989        }
    990        BorderSegment::Top |
    991        BorderSegment::Bottom |
    992        BorderSegment::Right |
    993        BorderSegment::Left => {
    994            let is_vertical = segment == BorderSegment::Left ||
    995                              segment == BorderSegment::Right;
    996 
    997            match style0 {
    998                BorderStyle::Dashed => {
    999                    let (x, y) = if is_vertical {
   1000                        let half_dash_size = task_rect.height() * 0.25;
   1001                        (0., half_dash_size)
   1002                    } else {
   1003                        let half_dash_size = task_rect.width() * 0.25;
   1004                        (half_dash_size, 0.)
   1005                    };
   1006 
   1007                    instances.push(BorderInstance {
   1008                        flags: base_flags | ((BorderClipKind::DashEdge as i32) << 24),
   1009                        clip_params: [
   1010                            x, y, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
   1011                        ],
   1012                        ..base_instance
   1013                    });
   1014                }
   1015                BorderStyle::Dotted => {
   1016                    let (x, y, r) = if is_vertical {
   1017                        (widths.width * 0.5,
   1018                         widths.width,
   1019                         widths.width * 0.5)
   1020                    } else {
   1021                        (widths.height,
   1022                         widths.height * 0.5,
   1023                         widths.height * 0.5)
   1024                    };
   1025 
   1026                    instances.push(BorderInstance {
   1027                        flags: base_flags | ((BorderClipKind::Dot as i32) << 24),
   1028                        clip_params: [
   1029                            x, y, r, 0.0, 0.0, 0.0, 0.0, 0.0,
   1030                        ],
   1031                        ..base_instance
   1032                    });
   1033                }
   1034                _ => {
   1035                    instances.push(base_instance);
   1036                }
   1037            }
   1038        }
   1039    }
   1040 }
   1041 
   1042 /// Add a corner segment (if valid) to the list of
   1043 /// border segments for this primitive.
   1044 fn add_corner_segment(
   1045    image_rect: LayoutRect,
   1046    non_overlapping_rect: LayoutRect,
   1047    side0: BorderSide,
   1048    side1: BorderSide,
   1049    widths: LayoutSize,
   1050    radius: LayoutSize,
   1051    segment: BorderSegment,
   1052    edge_flags: EdgeAaSegmentMask,
   1053    h_adjacent_corner_outer: LayoutPoint,
   1054    h_adjacent_corner_radius: LayoutSize,
   1055    v_adjacent_corner_outer: LayoutPoint,
   1056    v_adjacent_corner_radius: LayoutSize,
   1057    brush_segments: &mut Vec<BrushSegment>,
   1058    border_segments: &mut Vec<BorderSegmentInfo>,
   1059    do_aa: bool,
   1060 ) {
   1061    if side0.color.a <= 0.0 && side1.color.a <= 0.0 {
   1062        return;
   1063    }
   1064 
   1065    if widths.width <= 0.0 && widths.height <= 0.0 {
   1066        return;
   1067    }
   1068 
   1069    if side0.style.is_hidden() && side1.style.is_hidden() {
   1070        return;
   1071    }
   1072 
   1073    let segment_rect = match image_rect.intersection(&non_overlapping_rect) {
   1074        Some(rect) => rect,
   1075        None => {
   1076            return;
   1077        }
   1078    };
   1079 
   1080    let texture_rect = segment_rect
   1081        .translate(-image_rect.min.to_vector())
   1082        .scale(1.0 / image_rect.width(), 1.0 / image_rect.height());
   1083 
   1084    brush_segments.push(
   1085        BrushSegment::new(
   1086            segment_rect,
   1087            /* may_need_clip_mask = */ true,
   1088            edge_flags,
   1089            [texture_rect.min.x, texture_rect.min.y, texture_rect.max.x, texture_rect.max.y],
   1090            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_TEXEL_RECT,
   1091        )
   1092    );
   1093 
   1094    // If the radii of the adjacent corners do not overlap with this segment,
   1095    // then set the outer position to this segment's corner and the radii to zero.
   1096    // That way the cache key is unaffected by non-overlapping corners, resulting
   1097    // in fewer misses.
   1098    let (h_corner_outer, h_corner_radius) = match segment {
   1099        BorderSegment::TopLeft => {
   1100            if h_adjacent_corner_outer.x - h_adjacent_corner_radius.width < image_rect.max.x {
   1101                (h_adjacent_corner_outer, h_adjacent_corner_radius)
   1102            } else {
   1103                (LayoutPoint::new(image_rect.max.x, image_rect.min.y), LayoutSize::zero())
   1104            }
   1105        }
   1106        BorderSegment::TopRight => {
   1107            if h_adjacent_corner_outer.x + h_adjacent_corner_radius.width > image_rect.min.x {
   1108                (h_adjacent_corner_outer, h_adjacent_corner_radius)
   1109            } else {
   1110                (LayoutPoint::new(image_rect.min.x, image_rect.min.y), LayoutSize::zero())
   1111            }
   1112        }
   1113        BorderSegment::BottomRight => {
   1114            if h_adjacent_corner_outer.x + h_adjacent_corner_radius.width > image_rect.min.x {
   1115                (h_adjacent_corner_outer, h_adjacent_corner_radius)
   1116            } else {
   1117                (LayoutPoint::new(image_rect.min.x, image_rect.max.y), LayoutSize::zero())
   1118            }
   1119        }
   1120        BorderSegment::BottomLeft => {
   1121            if h_adjacent_corner_outer.x - h_adjacent_corner_radius.width < image_rect.max.x {
   1122                (h_adjacent_corner_outer, h_adjacent_corner_radius)
   1123            } else {
   1124                (image_rect.max, LayoutSize::zero())
   1125            }
   1126        }
   1127        _ => unreachable!()
   1128    };
   1129 
   1130    let (v_corner_outer, v_corner_radius) = match segment {
   1131        BorderSegment::TopLeft => {
   1132            if v_adjacent_corner_outer.y - v_adjacent_corner_radius.height < image_rect.max.y {
   1133                (v_adjacent_corner_outer, v_adjacent_corner_radius)
   1134            } else {
   1135                (LayoutPoint::new(image_rect.min.x, image_rect.max.y), LayoutSize::zero())
   1136            }
   1137        }
   1138        BorderSegment::TopRight => {
   1139            if v_adjacent_corner_outer.y - v_adjacent_corner_radius.height < image_rect.max.y {
   1140                (v_adjacent_corner_outer, v_adjacent_corner_radius)
   1141            } else {
   1142                (image_rect.max, LayoutSize::zero())
   1143            }
   1144        }
   1145        BorderSegment::BottomRight => {
   1146            if v_adjacent_corner_outer.y + v_adjacent_corner_radius.height > image_rect.min.y {
   1147                (v_adjacent_corner_outer, v_adjacent_corner_radius)
   1148            } else {
   1149                (LayoutPoint::new(image_rect.max.x, image_rect.min.y), LayoutSize::zero())
   1150            }
   1151        }
   1152        BorderSegment::BottomLeft => {
   1153            if v_adjacent_corner_outer.y + v_adjacent_corner_radius.height > image_rect.min.y {
   1154                (v_adjacent_corner_outer, v_adjacent_corner_radius)
   1155            } else {
   1156                (LayoutPoint::new(image_rect.min.x, image_rect.min.y), LayoutSize::zero())
   1157            }
   1158        }
   1159        _ => unreachable!()
   1160    };
   1161 
   1162    border_segments.push(BorderSegmentInfo {
   1163        local_task_size: image_rect.size(),
   1164        cache_key: BorderSegmentCacheKey {
   1165            do_aa,
   1166            side0: side0.into(),
   1167            side1: side1.into(),
   1168            segment,
   1169            radius: radius.to_au(),
   1170            size: widths.to_au(),
   1171            h_adjacent_corner_outer: (h_corner_outer - image_rect.min).to_point().to_au(),
   1172            h_adjacent_corner_radius: h_corner_radius.to_au(),
   1173            v_adjacent_corner_outer: (v_corner_outer - image_rect.min).to_point().to_au(),
   1174            v_adjacent_corner_radius: v_corner_radius.to_au(),
   1175        },
   1176    });
   1177 }
   1178 
   1179 /// Add an edge segment (if valid) to the list of
   1180 /// border segments for this primitive.
   1181 fn add_edge_segment(
   1182    image_rect: LayoutRect,
   1183    edge_info: &EdgeInfo,
   1184    side: BorderSide,
   1185    width: f32,
   1186    segment: BorderSegment,
   1187    edge_flags: EdgeAaSegmentMask,
   1188    brush_segments: &mut Vec<BrushSegment>,
   1189    border_segments: &mut Vec<BorderSegmentInfo>,
   1190    do_aa: bool,
   1191 ) {
   1192    if side.color.a <= 0.0 {
   1193        return;
   1194    }
   1195 
   1196    if side.style.is_hidden() {
   1197        return;
   1198    }
   1199 
   1200    let (size, brush_flags) = match segment {
   1201        BorderSegment::Left | BorderSegment::Right => {
   1202            (LayoutSize::new(width, edge_info.stretch_size), BrushFlags::SEGMENT_REPEAT_Y)
   1203        }
   1204        BorderSegment::Top | BorderSegment::Bottom => {
   1205            (LayoutSize::new(edge_info.stretch_size, width), BrushFlags::SEGMENT_REPEAT_X)
   1206        }
   1207        _ => {
   1208            unreachable!();
   1209        }
   1210    };
   1211 
   1212    if image_rect.width() <= 0. || image_rect.height() <= 0. {
   1213        return;
   1214    }
   1215 
   1216    brush_segments.push(
   1217        BrushSegment::new(
   1218            image_rect,
   1219            /* may_need_clip_mask = */ true,
   1220            edge_flags,
   1221            [0.0, 0.0, size.width, size.height],
   1222            BrushFlags::SEGMENT_RELATIVE | brush_flags,
   1223        )
   1224    );
   1225 
   1226    border_segments.push(BorderSegmentInfo {
   1227        local_task_size: size,
   1228        cache_key: BorderSegmentCacheKey {
   1229            do_aa,
   1230            side0: side.into(),
   1231            side1: side.into(),
   1232            radius: LayoutSizeAu::zero(),
   1233            size: size.to_au(),
   1234            segment,
   1235            h_adjacent_corner_outer: LayoutPointAu::zero(),
   1236            h_adjacent_corner_radius: LayoutSizeAu::zero(),
   1237            v_adjacent_corner_outer: LayoutPointAu::zero(),
   1238            v_adjacent_corner_radius: LayoutSizeAu::zero(),
   1239        },
   1240    });
   1241 }
   1242 
   1243 /// Build the set of border instances needed to draw a border
   1244 /// segment into the render task cache.
   1245 pub fn build_border_instances(
   1246    cache_key: &BorderSegmentCacheKey,
   1247    cache_size: DeviceIntSize,
   1248    border: &ApiNormalBorder,
   1249    scale: LayoutToDeviceScale,
   1250 ) -> Vec<BorderInstance> {
   1251    let mut instances = Vec::new();
   1252 
   1253    let (side0, side1, flip0, flip1) = match cache_key.segment {
   1254        BorderSegment::Left => (&border.left, &border.left, false, false),
   1255        BorderSegment::Top => (&border.top, &border.top, false, false),
   1256        BorderSegment::Right => (&border.right, &border.right, true, true),
   1257        BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true),
   1258        BorderSegment::TopLeft => (&border.left, &border.top, false, false),
   1259        BorderSegment::TopRight => (&border.top, &border.right, false, true),
   1260        BorderSegment::BottomRight => (&border.right, &border.bottom, true, true),
   1261        BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false),
   1262    };
   1263 
   1264    let style0 = if side0.style.is_hidden() {
   1265        side1.style
   1266    } else {
   1267        side0.style
   1268    };
   1269    let style1 = if side1.style.is_hidden() {
   1270        side0.style
   1271    } else {
   1272        side1.style
   1273    };
   1274 
   1275    let color0 = side0.border_color(flip0);
   1276    let color1 = side1.border_color(flip1);
   1277 
   1278    let widths = (LayoutSize::from_au(cache_key.size) * scale).ceil();
   1279    let radius = (LayoutSize::from_au(cache_key.radius) * scale).ceil();
   1280 
   1281    let h_corner_outer = (LayoutPoint::from_au(cache_key.h_adjacent_corner_outer) * scale).round();
   1282    let h_corner_radius = (LayoutSize::from_au(cache_key.h_adjacent_corner_radius) * scale).ceil();
   1283    let v_corner_outer = (LayoutPoint::from_au(cache_key.v_adjacent_corner_outer) * scale).round();
   1284    let v_corner_radius = (LayoutSize::from_au(cache_key.v_adjacent_corner_radius) * scale).ceil();
   1285 
   1286    add_segment(
   1287        DeviceRect::from_size(cache_size.to_f32()),
   1288        style0,
   1289        style1,
   1290        color0,
   1291        color1,
   1292        cache_key.segment,
   1293        &mut instances,
   1294        widths,
   1295        radius,
   1296        border.do_aa,
   1297        h_corner_outer,
   1298        h_corner_radius,
   1299        v_corner_outer,
   1300        v_corner_radius,
   1301    );
   1302 
   1303    instances
   1304 }
   1305 
   1306 impl NinePatchDescriptor {
   1307    pub fn create_segments(
   1308        &self,
   1309        size: LayoutSize,
   1310    ) -> Vec<BrushSegment> {
   1311        let rect = LayoutRect::from_size(size);
   1312 
   1313        // Calculate the local texel coords of the slices.
   1314        let px0 = 0.0;
   1315        let px1 = self.slice.left as f32 / self.width as f32;
   1316        let px2 = (self.width as f32 - self.slice.right as f32) / self.width as f32;
   1317        let px3 = 1.0;
   1318 
   1319        let py0 = 0.0;
   1320        let py1 = self.slice.top as f32 / self.height as f32;
   1321        let py2 = (self.height as f32 - self.slice.bottom as f32) / self.height as f32;
   1322        let py3 = 1.0;
   1323 
   1324        let tl_outer = LayoutPoint::new(rect.min.x, rect.min.y);
   1325        let tl_inner = tl_outer + vec2(self.widths.left, self.widths.top);
   1326 
   1327        let tr_outer = LayoutPoint::new(rect.min.x + rect.width(), rect.min.y);
   1328        let tr_inner = tr_outer + vec2(-self.widths.right, self.widths.top);
   1329 
   1330        let bl_outer = LayoutPoint::new(rect.min.x, rect.min.y + rect.height());
   1331        let bl_inner = bl_outer + vec2(self.widths.left, -self.widths.bottom);
   1332 
   1333        let br_outer = rect.max;
   1334 
   1335        let br_inner = br_outer - vec2(self.widths.right, self.widths.bottom);
   1336 
   1337        fn add_segment(
   1338            segments: &mut Vec<BrushSegment>,
   1339            rect: LayoutRect,
   1340            uv_rect: TexelRect,
   1341            repeat_horizontal: RepeatMode,
   1342            repeat_vertical: RepeatMode,
   1343            extra_flags: BrushFlags,
   1344        ) {
   1345            if uv_rect.uv1.x <= uv_rect.uv0.x || uv_rect.uv1.y <= uv_rect.uv0.y {
   1346                return;
   1347            }
   1348 
   1349            // Use segment relative interpolation for all
   1350            // instances in this primitive.
   1351            let mut brush_flags =
   1352                BrushFlags::SEGMENT_RELATIVE |
   1353                BrushFlags::SEGMENT_TEXEL_RECT |
   1354                extra_flags;
   1355 
   1356            // Enable repeat modes on the segment.
   1357            if repeat_horizontal == RepeatMode::Repeat {
   1358                brush_flags |= BrushFlags::SEGMENT_REPEAT_X | BrushFlags::SEGMENT_REPEAT_X_CENTERED;
   1359            } else if repeat_horizontal == RepeatMode::Round {
   1360                brush_flags |= BrushFlags::SEGMENT_REPEAT_X | BrushFlags::SEGMENT_REPEAT_X_ROUND;
   1361            }
   1362 
   1363            if repeat_vertical == RepeatMode::Repeat {
   1364                brush_flags |= BrushFlags::SEGMENT_REPEAT_Y | BrushFlags::SEGMENT_REPEAT_Y_CENTERED;
   1365            } else if repeat_vertical == RepeatMode::Round {
   1366                brush_flags |= BrushFlags::SEGMENT_REPEAT_Y | BrushFlags::SEGMENT_REPEAT_Y_ROUND;
   1367            }
   1368 
   1369            let segment = BrushSegment::new(
   1370                rect,
   1371                true,
   1372                EdgeAaSegmentMask::empty(),
   1373                [
   1374                    uv_rect.uv0.x,
   1375                    uv_rect.uv0.y,
   1376                    uv_rect.uv1.x,
   1377                    uv_rect.uv1.y,
   1378                ],
   1379                brush_flags,
   1380            );
   1381 
   1382            segments.push(segment);
   1383        }
   1384 
   1385        // Build the list of image segments
   1386        let mut segments = Vec::new();
   1387 
   1388        // Top left
   1389        add_segment(
   1390            &mut segments,
   1391            LayoutRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
   1392            TexelRect::new(px0, py0, px1, py1),
   1393            RepeatMode::Stretch,
   1394            RepeatMode::Stretch,
   1395            BrushFlags::empty(),
   1396        );
   1397        // Top right
   1398        add_segment(
   1399            &mut segments,
   1400            LayoutRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
   1401            TexelRect::new(px2, py0, px3, py1),
   1402            RepeatMode::Stretch,
   1403            RepeatMode::Stretch,
   1404            BrushFlags::empty(),
   1405        );
   1406        // Bottom right
   1407        add_segment(
   1408            &mut segments,
   1409            LayoutRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
   1410            TexelRect::new(px2, py2, px3, py3),
   1411            RepeatMode::Stretch,
   1412            RepeatMode::Stretch,
   1413            BrushFlags::empty(),
   1414        );
   1415        // Bottom left
   1416        add_segment(
   1417            &mut segments,
   1418            LayoutRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
   1419            TexelRect::new(px0, py2, px1, py3),
   1420            RepeatMode::Stretch,
   1421            RepeatMode::Stretch,
   1422            BrushFlags::empty(),
   1423        );
   1424 
   1425        // Center
   1426        if self.fill {
   1427            add_segment(
   1428                &mut segments,
   1429                LayoutRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
   1430                TexelRect::new(px1, py1, px2, py2),
   1431                self.repeat_horizontal,
   1432                self.repeat_vertical,
   1433                BrushFlags::SEGMENT_NINEPATCH_MIDDLE,
   1434            );
   1435        }
   1436 
   1437        // Add edge segments.
   1438 
   1439        // Top
   1440        add_segment(
   1441            &mut segments,
   1442            LayoutRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
   1443            TexelRect::new(px1, py0, px2, py1),
   1444            self.repeat_horizontal,
   1445            RepeatMode::Stretch,
   1446            BrushFlags::empty(),
   1447        );
   1448        // Bottom
   1449        add_segment(
   1450            &mut segments,
   1451            LayoutRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
   1452            TexelRect::new(px1, py2, px2, py3),
   1453            self.repeat_horizontal,
   1454            RepeatMode::Stretch,
   1455            BrushFlags::empty(),
   1456        );
   1457        // Left
   1458        add_segment(
   1459            &mut segments,
   1460            LayoutRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
   1461            TexelRect::new(px0, py1, px1, py2),
   1462            RepeatMode::Stretch,
   1463            self.repeat_vertical,
   1464            BrushFlags::empty(),
   1465        );
   1466        // Right
   1467        add_segment(
   1468            &mut segments,
   1469            LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
   1470            TexelRect::new(px2, py1, px3, py2),
   1471            RepeatMode::Stretch,
   1472            self.repeat_vertical,
   1473            BrushFlags::empty(),
   1474        );
   1475 
   1476        segments
   1477    }
   1478 }