segment.rs (46363B)
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 //! Primitive segmentation 6 //! 7 //! # Overview 8 //! 9 //! Segmenting is the process of breaking rectangular primitives into smaller rectangular 10 //! primitives in order to extract parts that could benefit from a fast paths. 11 //! 12 //! Typically this is used to allow fully opaque segments to be rendered in the opaque 13 //! pass. For example when an opaque rectangle has a non-axis-aligned transform applied, 14 //! we usually have to apply some anti-aliasing around the edges which requires alpha 15 //! blending. By segmenting the edges out of the center of the primitive, we can keep a 16 //! large amount of pixels in the opaque pass. 17 //! Segmenting also lets us avoids rasterizing parts of clip masks that we know to have 18 //! no effect or to be fully masking. For example by segmenting the corners of a rounded 19 //! rectangle clip, we can optimize both rendering the mask and the primitive by only 20 //! rasterize the corners in the mask and not applying any clipping to the segments of 21 //! the primitive that don't overlap the borders. 22 //! 23 //! It is a flexible system in the sense that different sources of segmentation (for 24 //! example two rounded rectangle clips) can affect the segmentation, and the possibility 25 //! to segment some effects such as specific clip kinds does not necessarily mean the 26 //! primitive will actually be segmented. 27 //! 28 //! ## Segments and clipping 29 //! 30 //! Segments of a primitive can be either not clipped, fully clipped, or partially clipped. 31 //! In the first two case we don't need a clip mask. For each partially masked segments, a 32 //! mask is rasterized using a render task. All of the interesting steps happen during frame 33 //! building. 34 //! 35 //! - The first step is to determine the segmentation and write the associated GPU data. 36 //! See `PrimitiveInstance::build_segments_if_needed` and `write_brush_segment_description` 37 //! in `prim_store/mod.rs` which uses the segment builder of this module. 38 //! - The second step is to generate the mask render tasks. 39 //! See `BrushSegment::update_clip_task` and `RenderTask::new_mask`. For each segment that 40 //! needs a mask, the contribution of all clips that affect the segment is added to the 41 //! mask's render task. 42 //! - Segments are assigned to batches (See `batch.rs`). Segments of a given primitive can 43 //! be assigned to different batches. 44 //! 45 //! See also the [`clip` module documentation][clip.rs] for details about how clipping 46 //! information is represented. 47 //! 48 //! 49 //! [clip.rs]: ../clip/index.html 50 //! 51 52 use api::{BorderRadius, ClipMode}; 53 use api::units::*; 54 use std::{cmp, usize}; 55 use crate::util::extract_inner_rect_safe; 56 use smallvec::SmallVec; 57 58 // We don't want to generate too many segments in edge cases, as it will result in a lot of 59 // clip mask overhead, and possibly exceeding the maximum row size of the GPU cache. 60 const MAX_SEGMENTS: usize = 64; 61 62 // Note: This can use up to 4 bits due to how it will be packed in 63 // the instance data. 64 65 /// Each bit of the edge AA mask is: 66 /// 0, when the edge of the primitive needs to be considered for AA 67 /// 1, when the edge of the segment needs to be considered for AA 68 /// 69 /// *Note*: the bit values have to match the shader logic in 70 /// `write_transform_vertex()` function. 71 #[cfg_attr(feature = "capture", derive(Serialize))] 72 #[cfg_attr(feature = "replay", derive(Deserialize))] 73 #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, MallocSizeOf)] 74 pub struct EdgeAaSegmentMask(u8); 75 76 bitflags! { 77 impl EdgeAaSegmentMask: u8 { 78 /// 79 const LEFT = 0x1; 80 /// 81 const TOP = 0x2; 82 /// 83 const RIGHT = 0x4; 84 /// 85 const BOTTOM = 0x8; 86 } 87 } 88 89 impl core::fmt::Debug for EdgeAaSegmentMask { 90 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 91 if self.is_empty() { 92 write!(f, "{:#x}", Self::empty().bits()) 93 } else { 94 bitflags::parser::to_writer(self, f) 95 } 96 } 97 } 98 99 bitflags! { 100 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] 101 pub struct ItemFlags: u8 { 102 const X_ACTIVE = 0x1; 103 const Y_ACTIVE = 0x2; 104 const HAS_MASK = 0x4; 105 } 106 } 107 108 // The segment builder outputs a list of these segments. 109 #[derive(Debug, PartialEq)] 110 pub struct Segment { 111 pub rect: LayoutRect, 112 pub has_mask: bool, 113 pub edge_flags: EdgeAaSegmentMask, 114 pub region_x: usize, 115 pub region_y: usize, 116 } 117 118 // The segment builder creates a list of x/y axis events 119 // that are used to build a segment list. Right now, we 120 // don't bother providing a list of *which* clip regions 121 // are active for a given segment. Instead, if there is 122 // any clip mask present in a segment, we will just end 123 // up drawing each of the masks to that segment clip. 124 // This is a fairly rare case, but we can detect this 125 // in the future and only apply clip masks that are 126 // relevant to each segment region. 127 // TODO(gw): Provide clip region info with each segment. 128 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)] 129 enum EventKind { 130 // Beginning of a clip (rounded) rect. 131 BeginClip, 132 // End of a clip (rounded) rect. 133 EndClip, 134 // Begin the next region in the primitive. 135 BeginRegion, 136 } 137 138 // Events must be ordered such that when the coordinates 139 // of two events are the same, the end events are processed 140 // before the begin events. This ensures that we're able 141 // to detect which regions are active for a given segment. 142 impl Ord for EventKind { 143 fn cmp(&self, other: &EventKind) -> cmp::Ordering { 144 match (*self, *other) { 145 (EventKind::BeginRegion, EventKind::BeginRegion) => { 146 panic!("bug: regions must be non-overlapping") 147 } 148 (EventKind::EndClip, EventKind::BeginRegion) | 149 (EventKind::BeginRegion, EventKind::BeginClip) => { 150 cmp::Ordering::Less 151 } 152 (EventKind::BeginClip, EventKind::BeginRegion) | 153 (EventKind::BeginRegion, EventKind::EndClip) => { 154 cmp::Ordering::Greater 155 } 156 (EventKind::BeginClip, EventKind::BeginClip) | 157 (EventKind::EndClip, EventKind::EndClip) => { 158 cmp::Ordering::Equal 159 } 160 (EventKind::BeginClip, EventKind::EndClip) => { 161 cmp::Ordering::Greater 162 } 163 (EventKind::EndClip, EventKind::BeginClip) => { 164 cmp::Ordering::Less 165 } 166 } 167 } 168 } 169 170 // A x/y event where we will create a vertex in the 171 // segment builder. 172 #[derive(Debug, Eq, PartialEq, PartialOrd)] 173 struct Event { 174 value: Au, 175 item_index: ItemIndex, 176 kind: EventKind, 177 } 178 179 impl Ord for Event { 180 fn cmp(&self, other: &Event) -> cmp::Ordering { 181 self.value 182 .cmp(&other.value) 183 .then(self.kind.cmp(&other.kind)) 184 } 185 } 186 187 impl Event { 188 fn begin(value: f32, index: usize) -> Event { 189 Event { 190 value: Au::from_f32_px(value), 191 item_index: ItemIndex(index), 192 kind: EventKind::BeginClip, 193 } 194 } 195 196 fn end(value: f32, index: usize) -> Event { 197 Event { 198 value: Au::from_f32_px(value), 199 item_index: ItemIndex(index), 200 kind: EventKind::EndClip, 201 } 202 } 203 204 fn region(value: f32) -> Event { 205 Event { 206 value: Au::from_f32_px(value), 207 kind: EventKind::BeginRegion, 208 item_index: ItemIndex(usize::MAX), 209 } 210 } 211 212 fn update( 213 &self, 214 flag: ItemFlags, 215 items: &mut [Item], 216 region: &mut usize, 217 ) { 218 let is_active = match self.kind { 219 EventKind::BeginClip => true, 220 EventKind::EndClip => false, 221 EventKind::BeginRegion => { 222 *region += 1; 223 return; 224 } 225 }; 226 227 items[self.item_index.0].flags.set(flag, is_active); 228 } 229 } 230 231 // An item that provides some kind of clip region (either 232 // a clip in/out rect, or a mask region). 233 #[derive(Debug)] 234 struct Item { 235 rect: LayoutRect, 236 mode: Option<ClipMode>, 237 flags: ItemFlags, 238 } 239 240 impl Item { 241 fn new( 242 rect: LayoutRect, 243 mode: Option<ClipMode>, 244 has_mask: bool, 245 ) -> Item { 246 let flags = if has_mask { 247 ItemFlags::HAS_MASK 248 } else { 249 ItemFlags::empty() 250 }; 251 252 Item { 253 rect, 254 mode, 255 flags, 256 } 257 } 258 } 259 260 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)] 261 struct ItemIndex(usize); 262 263 // The main public interface to the segment module. 264 pub struct SegmentBuilder { 265 items: Vec<Item>, 266 inner_rect: Option<LayoutRect>, 267 bounding_rect: Option<LayoutRect>, 268 has_interesting_clips: bool, 269 270 #[cfg(debug_assertions)] 271 initialized: bool, 272 } 273 274 impl SegmentBuilder { 275 // Create a new segment builder, supplying the primitive 276 // local rect and associated local clip rect. 277 pub fn new() -> SegmentBuilder { 278 SegmentBuilder { 279 items: Vec::with_capacity(4), 280 bounding_rect: None, 281 inner_rect: None, 282 has_interesting_clips: false, 283 #[cfg(debug_assertions)] 284 initialized: false, 285 } 286 } 287 288 pub fn initialize( 289 &mut self, 290 local_rect: LayoutRect, 291 inner_rect: Option<LayoutRect>, 292 local_clip_rect: LayoutRect, 293 ) { 294 self.items.clear(); 295 self.inner_rect = inner_rect; 296 self.bounding_rect = Some(local_rect); 297 298 self.push_clip_rect(local_rect, None, ClipMode::Clip); 299 self.push_clip_rect(local_clip_rect, None, ClipMode::Clip); 300 301 // This must be set after the push_clip_rect calls above, since we 302 // want to skip segment building if those are the only clips. 303 self.has_interesting_clips = false; 304 305 #[cfg(debug_assertions)] 306 { 307 self.initialized = true; 308 } 309 } 310 311 // Push a region defined by an inner and outer rect where there 312 // is a mask required. This ensures that segments which intersect 313 // with these areas will get a clip mask task allocated. This 314 // is currently used to mark where a box-shadow region can affect 315 // the pixels of a clip-mask. It might be useful for other types 316 // such as dashed and dotted borders in the future. 317 pub fn push_mask_region( 318 &mut self, 319 outer_rect: LayoutRect, 320 inner_rect: LayoutRect, 321 inner_clip_mode: Option<ClipMode>, 322 ) { 323 self.has_interesting_clips = true; 324 325 if inner_rect.is_empty() { 326 self.items.push(Item::new( 327 outer_rect, 328 None, 329 true 330 )); 331 return; 332 } 333 334 debug_assert!(outer_rect.contains_box(&inner_rect)); 335 336 let p0 = outer_rect.min; 337 let p1 = inner_rect.min; 338 let p2 = inner_rect.max; 339 let p3 = outer_rect.max; 340 341 let segments = &[ 342 LayoutRect { 343 min: LayoutPoint::new(p0.x, p0.y), 344 max: LayoutPoint::new(p1.x, p1.y), 345 }, 346 LayoutRect { 347 min: LayoutPoint::new(p2.x, p0.y), 348 max: LayoutPoint::new(p3.x, p1.y), 349 }, 350 LayoutRect { 351 min: LayoutPoint::new(p2.x, p2.y), 352 max: LayoutPoint::new(p3.x, p3.y), 353 }, 354 LayoutRect { 355 min: LayoutPoint::new(p0.x, p2.y), 356 max: LayoutPoint::new(p1.x, p3.y), 357 }, 358 LayoutRect { 359 min: LayoutPoint::new(p1.x, p0.y), 360 max: LayoutPoint::new(p2.x, p1.y), 361 }, 362 LayoutRect { 363 min: LayoutPoint::new(p2.x, p1.y), 364 max: LayoutPoint::new(p3.x, p2.y), 365 }, 366 LayoutRect { 367 min: LayoutPoint::new(p1.x, p2.y), 368 max: LayoutPoint::new(p2.x, p3.y), 369 }, 370 LayoutRect { 371 min: LayoutPoint::new(p0.x, p1.y), 372 max: LayoutPoint::new(p1.x, p2.y), 373 }, 374 ]; 375 376 self.items.reserve(segments.len() + 1); 377 378 for segment in segments { 379 self.items.push(Item::new( 380 *segment, 381 None, 382 true 383 )); 384 } 385 386 if inner_clip_mode.is_some() { 387 self.items.push(Item::new( 388 inner_rect, 389 inner_clip_mode, 390 false, 391 )); 392 } 393 } 394 395 // Push some kind of clipping region into the segment builder. 396 // If radius is None, it's a simple rect. 397 pub fn push_clip_rect( 398 &mut self, 399 rect: LayoutRect, 400 radius: Option<BorderRadius>, 401 mode: ClipMode, 402 ) { 403 self.has_interesting_clips = true; 404 405 // Keep track of a minimal bounding rect for the set of 406 // segments that will be generated. 407 if mode == ClipMode::Clip { 408 self.bounding_rect = self.bounding_rect.and_then(|bounding_rect| { 409 bounding_rect.intersection(&rect) 410 }); 411 } 412 let mode = Some(mode); 413 414 match radius { 415 Some(radius) => { 416 // For a rounded rect, try to create a nine-patch where there 417 // is a clip item for each corner, inner and edge region. 418 match extract_inner_rect_safe(&rect, &radius) { 419 Some(inner) => { 420 let p0 = rect.min; 421 let p1 = inner.min; 422 let p2 = inner.max; 423 let p3 = rect.max; 424 425 self.items.reserve(9); 426 427 let corner_segments = &[ 428 LayoutRect { 429 min: LayoutPoint::new(p0.x, p0.y), 430 max: LayoutPoint::new(p1.x, p1.y), 431 }, 432 LayoutRect { 433 min: LayoutPoint::new(p2.x, p0.y), 434 max: LayoutPoint::new(p3.x, p1.y), 435 }, 436 LayoutRect { 437 min: LayoutPoint::new(p2.x, p2.y), 438 max: LayoutPoint::new(p3.x, p3.y), 439 }, 440 LayoutRect { 441 min: LayoutPoint::new(p0.x, p2.y), 442 max: LayoutPoint::new(p1.x, p3.y), 443 }, 444 ]; 445 446 for segment in corner_segments { 447 self.items.push(Item::new( 448 *segment, 449 mode, 450 true 451 )); 452 } 453 454 let other_segments = &[ 455 LayoutRect { 456 min: LayoutPoint::new(p1.x, p0.y), 457 max: LayoutPoint::new(p2.x, p1.y), 458 }, 459 LayoutRect { 460 min: LayoutPoint::new(p2.x, p1.y), 461 max: LayoutPoint::new(p3.x, p2.y), 462 }, 463 LayoutRect { 464 min: LayoutPoint::new(p1.x, p2.y), 465 max: LayoutPoint::new(p2.x, p3.y), 466 }, 467 LayoutRect { 468 min: LayoutPoint::new(p0.x, p1.y), 469 max: LayoutPoint::new(p1.x, p2.y), 470 }, 471 LayoutRect { 472 min: LayoutPoint::new(p1.x, p1.y), 473 max: LayoutPoint::new(p2.x, p2.y), 474 }, 475 ]; 476 477 for segment in other_segments { 478 self.items.push(Item::new( 479 *segment, 480 mode, 481 false, 482 )); 483 } 484 } 485 None => { 486 // If we get here, we could not extract an inner rectangle 487 // for this clip region. This can occur in cases such as 488 // a rounded rect where the top-left and bottom-left radii 489 // result in overlapping rects. In that case, just create 490 // a single clip region for the entire rounded rect. 491 self.items.push(Item::new( 492 rect, 493 mode, 494 true, 495 )) 496 } 497 } 498 } 499 None => { 500 // For a simple rect, just create one clipping item. 501 self.items.push(Item::new( 502 rect, 503 mode, 504 false, 505 )) 506 } 507 } 508 } 509 510 // Consume this segment builder and produce a list of segments. 511 pub fn build<F>(&mut self, mut f: F) where F: FnMut(&Segment) { 512 #[cfg(debug_assertions)] 513 debug_assert!(self.initialized); 514 515 #[cfg(debug_assertions)] 516 { 517 self.initialized = false; 518 } 519 520 let bounding_rect = match self.bounding_rect { 521 Some(bounding_rect) => bounding_rect, 522 None => return, 523 }; 524 525 if !self.has_interesting_clips { 526 // There were no additional clips added, so don't bother building segments. 527 // Just emit a single segment for the bounding rect of the primitive. 528 f(&Segment { 529 edge_flags: EdgeAaSegmentMask::all(), 530 region_x: 0, 531 region_y: 0, 532 has_mask: false, 533 rect: bounding_rect, 534 }); 535 return 536 } 537 538 // First, filter out any items that don't intersect 539 // with the visible bounding rect. 540 self.items.retain(|item| item.rect.intersects(&bounding_rect)); 541 542 // Create events for each item 543 let mut x_events : SmallVec<[Event; 4]> = SmallVec::new(); 544 let mut y_events : SmallVec<[Event; 4]> = SmallVec::new(); 545 546 for (item_index, item) in self.items.iter().enumerate() { 547 let p0 = item.rect.min; 548 let p1 = item.rect.max; 549 550 x_events.push(Event::begin(p0.x, item_index)); 551 x_events.push(Event::end(p1.x, item_index)); 552 y_events.push(Event::begin(p0.y, item_index)); 553 y_events.push(Event::end(p1.y, item_index)); 554 } 555 556 // Add the region events, if provided. 557 if let Some(inner_rect) = self.inner_rect { 558 x_events.push(Event::region(inner_rect.min.x)); 559 x_events.push(Event::region(inner_rect.max.x)); 560 561 y_events.push(Event::region(inner_rect.min.y)); 562 y_events.push(Event::region(inner_rect.max.y)); 563 } 564 565 // Get the minimal bounding rect in app units. We will 566 // work in fixed point in order to avoid float precision 567 // error while handling events. 568 let p0 = LayoutPointAu::new( 569 Au::from_f32_px(bounding_rect.min.x), 570 Au::from_f32_px(bounding_rect.min.y), 571 ); 572 573 let p1 = LayoutPointAu::new( 574 Au::from_f32_px(bounding_rect.max.x), 575 Au::from_f32_px(bounding_rect.max.y), 576 ); 577 578 // Sort the events in ascending order. 579 x_events.sort(); 580 y_events.sort(); 581 582 // Generate segments from the event lists, by sweeping the y-axis 583 // and then the x-axis for each event. This can generate a significant 584 // number of segments, but most importantly, it ensures that there are 585 // no t-junctions in the generated segments. It's probably possible 586 // to come up with more efficient segmentation algorithms, at least 587 // for simple / common cases. 588 589 // Each coordinate is clamped to the bounds of the minimal 590 // bounding rect. This ensures that we don't generate segments 591 // outside that bounding rect, but does allow correctly handling 592 // clips where the clip region starts outside the minimal 593 // rect but still intersects with it. 594 595 let mut prev_y = clamp(p0.y, y_events[0].value, p1.y); 596 let mut region_y = 0; 597 let mut segments : SmallVec<[_; 16]> = SmallVec::new(); 598 let mut x_count = 0; 599 let mut y_count = 0; 600 601 for ey in &y_events { 602 let cur_y = clamp(p0.y, ey.value, p1.y); 603 604 if cur_y != prev_y { 605 let mut prev_x = clamp(p0.x, x_events[0].value, p1.x); 606 let mut region_x = 0; 607 608 for ex in &x_events { 609 let cur_x = clamp(p0.x, ex.value, p1.x); 610 611 if cur_x != prev_x { 612 segments.push(emit_segment_if_needed( 613 prev_x, 614 prev_y, 615 cur_x, 616 cur_y, 617 region_x, 618 region_y, 619 &self.items, 620 )); 621 622 prev_x = cur_x; 623 if y_count == 0 { 624 x_count += 1; 625 } 626 } 627 628 ex.update( 629 ItemFlags::X_ACTIVE, 630 &mut self.items, 631 &mut region_x, 632 ); 633 } 634 635 prev_y = cur_y; 636 y_count += 1; 637 } 638 639 ey.update( 640 ItemFlags::Y_ACTIVE, 641 &mut self.items, 642 &mut region_y, 643 ); 644 } 645 646 // If we created more than 64 segments, just bail out and draw it as a single primitive 647 // with a single mask, to avoid overhead of excessive amounts of segments. This can only 648 // happen in pathological cases, for example a cascade of a dozen or more overlapping 649 // and intersecting rounded clips. 650 if segments.len() > MAX_SEGMENTS { 651 f(&Segment { 652 edge_flags: EdgeAaSegmentMask::all(), 653 region_x: 0, 654 region_y: 0, 655 has_mask: true, 656 rect: bounding_rect, 657 }); 658 return 659 } 660 661 // Run user supplied closure for each valid segment. 662 debug_assert_eq!(segments.len(), x_count * y_count); 663 for y in 0 .. y_count { 664 for x in 0 .. x_count { 665 let mut edge_flags = EdgeAaSegmentMask::empty(); 666 667 if x == 0 || segments[y * x_count + x - 1].is_none() { 668 edge_flags |= EdgeAaSegmentMask::LEFT; 669 } 670 if x == x_count-1 || segments[y * x_count + x + 1].is_none() { 671 edge_flags |= EdgeAaSegmentMask::RIGHT; 672 } 673 if y == 0 || segments[(y-1) * x_count + x].is_none() { 674 edge_flags |= EdgeAaSegmentMask::TOP; 675 } 676 if y == y_count-1 || segments[(y+1) * x_count + x].is_none() { 677 edge_flags |= EdgeAaSegmentMask::BOTTOM; 678 } 679 680 if let Some(ref mut segment) = segments[y * x_count + x] { 681 segment.edge_flags = edge_flags; 682 f(segment); 683 } 684 } 685 } 686 } 687 } 688 689 fn clamp(low: Au, value: Au, high: Au) -> Au { 690 value.max(low).min(high) 691 } 692 693 fn emit_segment_if_needed( 694 x0: Au, 695 y0: Au, 696 x1: Au, 697 y1: Au, 698 region_x: usize, 699 region_y: usize, 700 items: &[Item], 701 ) -> Option<Segment> { 702 debug_assert!(x1 > x0); 703 debug_assert!(y1 > y0); 704 705 // TODO(gw): Don't scan the whole list of items for 706 // each segment rect. Store active list 707 // in a hash set or similar if this ever 708 // shows up in a profile. 709 let mut has_clip_mask = false; 710 711 for item in items { 712 if item.flags.contains(ItemFlags::X_ACTIVE | ItemFlags::Y_ACTIVE) { 713 has_clip_mask |= item.flags.contains(ItemFlags::HAS_MASK); 714 715 if item.mode == Some(ClipMode::ClipOut) && !item.flags.contains(ItemFlags::HAS_MASK) { 716 return None; 717 } 718 } 719 } 720 721 let segment_rect = LayoutRect { 722 min: LayoutPoint::new( 723 x0.to_f32_px(), 724 y0.to_f32_px(), 725 ), 726 max: LayoutPoint::new( 727 x1.to_f32_px(), 728 y1.to_f32_px(), 729 ), 730 }; 731 732 Some(Segment { 733 rect: segment_rect, 734 has_mask: has_clip_mask, 735 edge_flags: EdgeAaSegmentMask::empty(), 736 region_x, 737 region_y, 738 }) 739 } 740 741 #[cfg(test)] 742 mod test { 743 use api::{BorderRadius, ClipMode}; 744 use api::units::{LayoutPoint, LayoutRect}; 745 use super::{Segment, SegmentBuilder, EdgeAaSegmentMask}; 746 use std::cmp; 747 748 fn rect(x0: f32, y0: f32, x1: f32, y1: f32) -> LayoutRect { 749 LayoutRect { 750 min: LayoutPoint::new(x0, y0), 751 max: LayoutPoint::new(x1, y1), 752 } 753 } 754 755 fn seg( 756 x0: f32, 757 y0: f32, 758 x1: f32, 759 y1: f32, 760 has_mask: bool, 761 edge_flags: Option<EdgeAaSegmentMask>, 762 ) -> Segment { 763 seg_region(x0, y0, x1, y1, 0, 0, has_mask, edge_flags) 764 } 765 766 fn seg_region( 767 x0: f32, 768 y0: f32, 769 x1: f32, 770 y1: f32, 771 region_x: usize, 772 region_y: usize, 773 has_mask: bool, 774 edge_flags: Option<EdgeAaSegmentMask>, 775 ) -> Segment { 776 Segment { 777 rect: LayoutRect { 778 min: LayoutPoint::new(x0, y0), 779 max: LayoutPoint::new(x1, y1), 780 }, 781 has_mask, 782 edge_flags: edge_flags.unwrap_or(EdgeAaSegmentMask::empty()), 783 region_x, 784 region_y, 785 } 786 } 787 788 fn segment_sorter(s0: &Segment, s1: &Segment) -> cmp::Ordering { 789 let r0 = &s0.rect; 790 let r1 = &s1.rect; 791 792 ( 793 (r0.min.x, r0.min.y, r0.max.x, r0.max.y) 794 ).partial_cmp(& 795 (r1.min.x, r1.min.y, r1.max.x, r1.max.y) 796 ).unwrap() 797 } 798 799 fn seg_test( 800 local_rect: LayoutRect, 801 inner_rect: Option<LayoutRect>, 802 local_clip_rect: LayoutRect, 803 clips: &[(LayoutRect, Option<BorderRadius>, ClipMode)], 804 expected_segments: &mut [Segment] 805 ) { 806 let mut sb = SegmentBuilder::new(); 807 sb.initialize( 808 local_rect, 809 inner_rect, 810 local_clip_rect, 811 ); 812 sb.push_clip_rect(local_rect, None, ClipMode::Clip); 813 sb.push_clip_rect(local_clip_rect, None, ClipMode::Clip); 814 let mut segments = Vec::new(); 815 for &(rect, radius, mode) in clips { 816 sb.push_clip_rect(rect, radius, mode); 817 } 818 sb.build(|segment| { 819 segments.push(Segment { 820 ..*segment 821 }); 822 }); 823 segments.sort_by(segment_sorter); 824 expected_segments.sort_by(segment_sorter); 825 assert_eq!( 826 segments.len(), 827 expected_segments.len(), 828 "segments\n{:?}\nexpected\n{:?}\n", 829 segments, 830 expected_segments 831 ); 832 for (segment, expected) in segments.iter().zip(expected_segments.iter()) { 833 assert_eq!(segment, expected); 834 } 835 } 836 837 #[test] 838 fn segment_empty() { 839 seg_test( 840 rect(0.0, 0.0, 0.0, 0.0), 841 None, 842 rect(0.0, 0.0, 0.0, 0.0), 843 &[], 844 &mut [], 845 ); 846 } 847 848 #[test] 849 fn segment_single() { 850 seg_test( 851 rect(10.0, 20.0, 30.0, 40.0), 852 None, 853 rect(10.0, 20.0, 30.0, 40.0), 854 &[], 855 &mut [ 856 seg(10.0, 20.0, 30.0, 40.0, false, 857 Some(EdgeAaSegmentMask::LEFT | 858 EdgeAaSegmentMask::TOP | 859 EdgeAaSegmentMask::RIGHT | 860 EdgeAaSegmentMask::BOTTOM 861 ) 862 ), 863 ], 864 ); 865 } 866 867 #[test] 868 fn segment_single_clip() { 869 seg_test( 870 rect(10.0, 20.0, 30.0, 40.0), 871 None, 872 rect(10.0, 20.0, 25.0, 35.0), 873 &[], 874 &mut [ 875 seg(10.0, 20.0, 25.0, 35.0, false, 876 Some(EdgeAaSegmentMask::LEFT | 877 EdgeAaSegmentMask::TOP | 878 EdgeAaSegmentMask::RIGHT | 879 EdgeAaSegmentMask::BOTTOM 880 ) 881 ), 882 ], 883 ); 884 } 885 886 #[test] 887 fn segment_inner_clip() { 888 seg_test( 889 rect(10.0, 20.0, 30.0, 40.0), 890 None, 891 rect(15.0, 25.0, 25.0, 35.0), 892 &[], 893 &mut [ 894 seg(15.0, 25.0, 25.0, 35.0, false, 895 Some(EdgeAaSegmentMask::LEFT | 896 EdgeAaSegmentMask::TOP | 897 EdgeAaSegmentMask::RIGHT | 898 EdgeAaSegmentMask::BOTTOM 899 ) 900 ), 901 ], 902 ); 903 } 904 905 #[test] 906 fn segment_outer_clip() { 907 seg_test( 908 rect(15.0, 25.0, 25.0, 35.0), 909 None, 910 rect(10.0, 20.0, 30.0, 40.0), 911 &[], 912 &mut [ 913 seg(15.0, 25.0, 25.0, 35.0, false, 914 Some(EdgeAaSegmentMask::LEFT | 915 EdgeAaSegmentMask::TOP | 916 EdgeAaSegmentMask::RIGHT | 917 EdgeAaSegmentMask::BOTTOM 918 ) 919 ), 920 ], 921 ); 922 } 923 924 #[test] 925 fn segment_clip_int() { 926 seg_test( 927 rect(10.0, 20.0, 30.0, 40.0), 928 None, 929 rect(20.0, 10.0, 40.0, 30.0), 930 &[], 931 &mut [ 932 seg(20.0, 20.0, 30.0, 30.0, false, 933 Some(EdgeAaSegmentMask::LEFT | 934 EdgeAaSegmentMask::TOP | 935 EdgeAaSegmentMask::RIGHT | 936 EdgeAaSegmentMask::BOTTOM 937 ) 938 ), 939 ], 940 ); 941 } 942 943 #[test] 944 fn segment_clip_disjoint() { 945 seg_test( 946 rect(10.0, 20.0, 30.0, 40.0), 947 None, 948 rect(30.0, 20.0, 50.0, 40.0), 949 &[], 950 &mut [], 951 ); 952 } 953 954 #[test] 955 fn segment_clips() { 956 seg_test( 957 rect(0.0, 0.0, 100.0, 100.0), 958 None, 959 rect(-1000.0, -1000.0, 1000.0, 1000.0), 960 &[ 961 (rect(20.0, 20.0, 40.0, 40.0), None, ClipMode::Clip), 962 (rect(40.0, 20.0, 60.0, 40.0), None, ClipMode::Clip), 963 ], 964 &mut [ 965 ], 966 ); 967 } 968 969 #[test] 970 fn segment_rounded_clip() { 971 seg_test( 972 rect(0.0, 0.0, 100.0, 100.0), 973 None, 974 rect(-1000.0, -1000.0, 1000.0, 1000.0), 975 &[ 976 (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), 977 ], 978 &mut [ 979 // corners 980 seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)), 981 seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)), 982 seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)), 983 seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)), 984 985 // inner 986 seg(30.0, 30.0, 50.0, 50.0, false, None), 987 988 // edges 989 seg(30.0, 20.0, 50.0, 30.0, false, Some(EdgeAaSegmentMask::TOP)), 990 seg(30.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::BOTTOM)), 991 seg(20.0, 30.0, 30.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT)), 992 seg(50.0, 30.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT)), 993 ], 994 ); 995 } 996 997 #[test] 998 fn segment_clip_out() { 999 seg_test( 1000 rect(0.0, 0.0, 100.0, 100.0), 1001 None, 1002 rect(-1000.0, -1000.0, 2000.0, 2000.0), 1003 &[ 1004 (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::ClipOut), 1005 ], 1006 &mut [ 1007 seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)), 1008 seg(20.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM)), 1009 seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)), 1010 1011 seg(0.0, 20.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT)), 1012 seg(60.0, 20.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT)), 1013 1014 seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)), 1015 seg(20.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)), 1016 seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)), 1017 ], 1018 ); 1019 } 1020 1021 #[test] 1022 fn segment_rounded_clip_out() { 1023 seg_test( 1024 rect(0.0, 0.0, 100.0, 100.0), 1025 None, 1026 rect(-1000.0, -1000.0, 2000.0, 2000.0), 1027 &[ 1028 (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::ClipOut), 1029 ], 1030 &mut [ 1031 // top row 1032 seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)), 1033 seg(20.0, 0.0, 30.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)), 1034 seg(30.0, 0.0, 50.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM)), 1035 seg(50.0, 0.0, 60.0, 20.0, false, Some(EdgeAaSegmentMask::TOP)), 1036 seg(60.0, 0.0, 100.0, 20.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)), 1037 1038 // left 1039 seg(0.0, 20.0, 20.0, 30.0, false, Some(EdgeAaSegmentMask::LEFT)), 1040 seg(0.0, 30.0, 20.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT)), 1041 seg(0.0, 50.0, 20.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT)), 1042 1043 // right 1044 seg(60.0, 20.0, 100.0, 30.0, false, Some(EdgeAaSegmentMask::RIGHT)), 1045 seg(60.0, 30.0, 100.0, 50.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT)), 1046 seg(60.0, 50.0, 100.0, 60.0, false, Some(EdgeAaSegmentMask::RIGHT)), 1047 1048 // bottom row 1049 seg(0.0, 60.0, 20.0, 100.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)), 1050 seg(20.0, 60.0, 30.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)), 1051 seg(30.0, 60.0, 50.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP)), 1052 seg(50.0, 60.0, 60.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)), 1053 seg(60.0, 60.0, 100.0, 100.0, false, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)), 1054 1055 // inner corners 1056 seg(20.0, 20.0, 30.0, 30.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)), 1057 seg(20.0, 50.0, 30.0, 60.0, true, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT)), 1058 seg(50.0, 20.0, 60.0, 30.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)), 1059 seg(50.0, 50.0, 60.0, 60.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)), 1060 ], 1061 ); 1062 } 1063 1064 #[test] 1065 fn segment_clip_in_clip_out() { 1066 seg_test( 1067 rect(0.0, 0.0, 100.0, 100.0), 1068 None, 1069 rect(-1000.0, -1000.0, 2000.0, 2000.0), 1070 &[ 1071 (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::Clip), 1072 (rect(50.0, 50.0, 80.0, 80.0), None, ClipMode::ClipOut), 1073 ], 1074 &mut [ 1075 seg(20.0, 20.0, 50.0, 50.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)), 1076 seg(50.0, 20.0, 60.0, 50.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)), 1077 seg(20.0, 50.0, 50.0, 60.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT)), 1078 ], 1079 ); 1080 } 1081 1082 #[test] 1083 fn segment_rounded_clip_overlap() { 1084 seg_test( 1085 rect(0.0, 0.0, 100.0, 100.0), 1086 None, 1087 rect(0.0, 0.0, 100.0, 100.0), 1088 &[ 1089 (rect(0.0, 0.0, 10.0, 10.0), None, ClipMode::ClipOut), 1090 (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), 1091 ], 1092 &mut [ 1093 // corners 1094 seg(0.0, 90.0, 10.0, 100.0, true, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM)), 1095 seg(90.0, 0.0, 100.0, 10.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP)), 1096 seg(90.0, 90.0, 100.0, 100.0, true, Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM)), 1097 1098 // inner 1099 seg(10.0, 10.0, 90.0, 90.0, false, None), 1100 1101 // edges 1102 seg(10.0, 0.0, 90.0, 10.0, false, Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT)), 1103 seg(10.0, 90.0, 90.0, 100.0, false, Some(EdgeAaSegmentMask::BOTTOM)), 1104 seg(0.0, 10.0, 10.0, 90.0, false, Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP)), 1105 seg(90.0, 10.0, 100.0, 90.0, false, Some(EdgeAaSegmentMask::RIGHT)), 1106 ], 1107 ); 1108 } 1109 1110 #[test] 1111 fn segment_rounded_clip_overlap_reverse() { 1112 seg_test( 1113 rect(0.0, 0.0, 100.0, 100.0), 1114 None, 1115 rect(0.0, 0.0, 100.0, 100.0), 1116 &[ 1117 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip), 1118 (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), 1119 ], 1120 &mut [ 1121 seg(10.0, 10.0, 90.0, 90.0, false, 1122 Some(EdgeAaSegmentMask::LEFT | 1123 EdgeAaSegmentMask::TOP | 1124 EdgeAaSegmentMask::RIGHT | 1125 EdgeAaSegmentMask::BOTTOM 1126 ) 1127 ), 1128 ], 1129 ); 1130 } 1131 1132 #[test] 1133 fn segment_clip_in_clip_out_overlap() { 1134 seg_test( 1135 rect(0.0, 0.0, 100.0, 100.0), 1136 None, 1137 rect(0.0, 0.0, 100.0, 100.0), 1138 &[ 1139 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip), 1140 (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::ClipOut), 1141 ], 1142 &mut [ 1143 ], 1144 ); 1145 } 1146 1147 #[test] 1148 fn segment_event_order() { 1149 seg_test( 1150 rect(0.0, 0.0, 100.0, 100.0), 1151 None, 1152 rect(0.0, 0.0, 100.0, 100.0), 1153 &[ 1154 (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut), 1155 ], 1156 &mut [ 1157 seg(0.0, 90.0, 100.0, 100.0, false, Some( 1158 EdgeAaSegmentMask::LEFT | 1159 EdgeAaSegmentMask::RIGHT | 1160 EdgeAaSegmentMask::BOTTOM | 1161 EdgeAaSegmentMask::TOP 1162 )), 1163 ], 1164 ); 1165 } 1166 1167 #[test] 1168 fn segment_region_simple() { 1169 seg_test( 1170 rect(0.0, 0.0, 100.0, 100.0), 1171 Some(rect(20.0, 40.0, 60.0, 80.0)), 1172 rect(0.0, 0.0, 100.0, 100.0), 1173 &[ 1174 ], 1175 &mut [ 1176 seg_region( 1177 0.0, 0.0, 1178 20.0, 40.0, 1179 0, 0, 1180 false, 1181 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP) 1182 ), 1183 1184 seg_region( 1185 20.0, 0.0, 1186 60.0, 40.0, 1187 1, 0, 1188 false, 1189 Some(EdgeAaSegmentMask::TOP) 1190 ), 1191 1192 seg_region( 1193 60.0, 0.0, 1194 100.0, 40.0, 1195 2, 0, 1196 false, 1197 Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT) 1198 ), 1199 1200 seg_region( 1201 0.0, 40.0, 1202 20.0, 80.0, 1203 0, 1, 1204 false, 1205 Some(EdgeAaSegmentMask::LEFT) 1206 ), 1207 1208 seg_region( 1209 20.0, 40.0, 1210 60.0, 80.0, 1211 1, 1, 1212 false, 1213 None, 1214 ), 1215 1216 seg_region( 1217 60.0, 40.0, 1218 100.0, 80.0, 1219 2, 1, 1220 false, 1221 Some(EdgeAaSegmentMask::RIGHT) 1222 ), 1223 1224 seg_region( 1225 0.0, 80.0, 1226 20.0, 100.0, 1227 0, 2, 1228 false, 1229 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM) 1230 ), 1231 1232 seg_region( 1233 20.0, 80.0, 1234 60.0, 100.0, 1235 1, 2, 1236 false, 1237 Some(EdgeAaSegmentMask::BOTTOM), 1238 ), 1239 1240 seg_region( 1241 60.0, 80.0, 1242 100.0, 100.0, 1243 2, 2, 1244 false, 1245 Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM) 1246 ), 1247 1248 ], 1249 ); 1250 } 1251 1252 #[test] 1253 fn segment_region_clip() { 1254 seg_test( 1255 rect(0.0, 0.0, 100.0, 100.0), 1256 Some(rect(20.0, 40.0, 60.0, 80.0)), 1257 rect(0.0, 0.0, 100.0, 100.0), 1258 &[ 1259 (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut), 1260 ], 1261 &mut [ 1262 seg_region( 1263 0.0, 90.0, 1264 20.0, 100.0, 1265 0, 2, 1266 false, 1267 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP) 1268 ), 1269 1270 seg_region( 1271 20.0, 90.0, 1272 60.0, 100.0, 1273 1, 2, 1274 false, 1275 Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP), 1276 ), 1277 1278 seg_region( 1279 60.0, 90.0, 1280 100.0, 100.0, 1281 2, 2, 1282 false, 1283 Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP) 1284 ), 1285 1286 ], 1287 ); 1288 } 1289 1290 #[test] 1291 fn segment_region_clip2() { 1292 seg_test( 1293 rect(0.0, 0.0, 100.0, 100.0), 1294 Some(rect(20.0, 20.0, 80.0, 80.0)), 1295 rect(0.0, 0.0, 100.0, 100.0), 1296 &[ 1297 (rect(20.0, 20.0, 100.0, 100.0), None, ClipMode::ClipOut), 1298 ], 1299 &mut [ 1300 seg_region( 1301 0.0, 0.0, 1302 20.0, 20.0, 1303 0, 0, 1304 false, 1305 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::TOP) 1306 ), 1307 1308 seg_region( 1309 20.0, 0.0, 1310 80.0, 20.0, 1311 1, 0, 1312 false, 1313 Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM), 1314 ), 1315 1316 seg_region( 1317 80.0, 0.0, 1318 100.0, 20.0, 1319 2, 0, 1320 false, 1321 Some(EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM) 1322 ), 1323 1324 seg_region( 1325 0.0, 20.0, 1326 20.0, 80.0, 1327 0, 1, 1328 false, 1329 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT) 1330 ), 1331 1332 seg_region( 1333 0.0, 80.0, 1334 20.0, 100.0, 1335 0, 2, 1336 false, 1337 Some(EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT) 1338 ), 1339 ], 1340 ); 1341 } 1342 1343 #[test] 1344 fn segment_region_clip3() { 1345 seg_test( 1346 rect(0.0, 0.0, 100.0, 100.0), 1347 Some(rect(20.0, 20.0, 80.0, 80.0)), 1348 rect(0.0, 0.0, 100.0, 100.0), 1349 &[ 1350 (rect(10.0, 10.0, 30.0, 30.0), None, ClipMode::Clip), 1351 ], 1352 &mut [ 1353 seg_region( 1354 10.0, 10.0, 1355 20.0, 20.0, 1356 0, 0, 1357 false, 1358 Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT), 1359 ), 1360 1361 seg_region( 1362 20.0, 10.0, 1363 30.0, 20.0, 1364 1, 0, 1365 false, 1366 Some(EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT), 1367 ), 1368 1369 seg_region( 1370 10.0, 20.0, 1371 20.0, 30.0, 1372 0, 1, 1373 false, 1374 Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT), 1375 ), 1376 1377 seg_region( 1378 20.0, 20.0, 1379 30.0, 30.0, 1380 1, 1, 1381 false, 1382 Some(EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT), 1383 ), 1384 ], 1385 ); 1386 } 1387 }