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 }