Source code

Revision control

Other Tools

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