1use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU};
6use api::{NormalBorder as ApiNormalBorder, RepeatMode};
7use api::units::*;
8use crate::clip::ClipNodeId;
9use crate::ellipse::Ellipse;
10use euclid::vec2;
11use crate::scene_building::SceneBuilder;
12use crate::spatial_tree::SpatialNodeIndex;
13use crate::gpu_types::{BorderInstance, BorderSegment, BrushFlags};
14use crate::prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
15use crate::prim_store::borders::{NormalBorderPrim, NormalBorderData};
16use crate::util::{lerp, RectHelpers};
17use crate::internal_types::LayoutPrimitiveInfo;
18use crate::segment::EdgeAaSegmentMask;
19
20pub const MAX_BORDER_RESOLUTION: u32 = 2048;
28pub const MAX_DASH_COUNT: u32 = 2048;
32
33#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
39#[cfg_attr(feature = "capture", derive(Serialize))]
40#[cfg_attr(feature = "replay", derive(Deserialize))]
41pub struct BorderRadiusAu {
42 pub top_left: LayoutSizeAu,
43 pub top_right: LayoutSizeAu,
44 pub bottom_left: LayoutSizeAu,
45 pub bottom_right: LayoutSizeAu,
46}
47
48impl 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
59impl 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))]
73pub struct BorderSideAu {
74 pub color: ColorU,
75 pub style: BorderStyle,
76}
77
78impl 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
87impl 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)]
99pub struct NormalBorderAu {
100 pub left: BorderSideAu,
101 pub right: BorderSideAu,
102 pub top: BorderSideAu,
103 pub bottom: BorderSideAu,
104 pub radius: BorderRadiusAu,
105 pub do_aa: bool,
110}
111
112impl NormalBorderAu {
113 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
124impl 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
137impl 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#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
153#[cfg_attr(feature = "capture", derive(Serialize))]
154#[cfg_attr(feature = "replay", derive(Deserialize))]
155pub 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
168pub 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
217impl<'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
242pub trait BorderSideHelpers {
243 fn border_color(&self, is_inner_border: bool) -> ColorF;
244}
245
246impl 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 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#[repr(C)]
276#[derive(Copy, Debug, Clone, PartialEq)]
277pub enum BorderClipKind {
278 DashCorner = 1,
279 DashEdge = 2,
280 Dot = 3,
281}
282
283fn 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
307fn 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
393fn 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 let inner_radius = (corner_radius - widths * 0.5).abs();
415 let ellipse = Ellipse::new(inner_radius);
416
417 let min_diameter = widths.width.min(widths.height);
421
422 let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
425
426 (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 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 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 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 let t = next_dot_pos / ellipse.total_arc_length;
489 let dot_diameter = lerp(widths.width, widths.height, t);
490
491 if leftover < dot_diameter {
493 leftover_arc_length = leftover;
494 break;
495 }
496
497 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 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 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)]
551struct DotInfo {
552 arc_pos: f32,
553 diameter: f32,
554}
555
556impl DotInfo {
557 fn new(arc_pos: f32, diameter: f32) -> DotInfo {
558 DotInfo { arc_pos, diameter }
559 }
560}
561
562#[derive(Debug)]
564struct EdgeInfo {
565 local_offset: f32,
567 local_size: f32,
569 stretch_size: f32,
571}
572
573impl 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
587fn compute_half_dash(side_width: f32, total_size: f32) -> (f32, u32) {
590 let half_dash = side_width * 1.5;
591 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 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
612fn get_edge_info(
616 style: BorderStyle,
617 side_width: f32,
618 avail_size: f32,
619) -> EdgeInfo {
620 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 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
652pub 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
888pub 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
904fn 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 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
1042fn 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 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 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
1179fn 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 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
1243pub 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
1306impl NinePatchDescriptor {
1307 pub fn create_segments(
1308 &self,
1309 size: LayoutSize,
1310 ) -> Vec<BrushSegment> {
1311 let rect = LayoutRect::from_size(size);
1312
1313 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 let mut brush_flags =
1352 BrushFlags::SEGMENT_RELATIVE |
1353 BrushFlags::SEGMENT_TEXEL_RECT |
1354 extra_flags;
1355
1356 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 let mut segments = Vec::new();
1387
1388 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 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 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 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 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_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 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 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 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}