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, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
6 use api::{ImageRendering, LayerRect, LayerSize, LayoutPoint, LayoutVector2D, LocalClip};
7 use api::{BoxShadowClipMode, LayerPoint, LayerToWorldScale};
8 use border::{BorderCornerClipSource, ensure_no_corner_overlap};
9 use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
10 use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
11 use ellipse::Ellipse;
12 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
13 use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
14 use gpu_types::ClipScrollNodeIndex;
15 use prim_store::{ClipData, ImageMaskData};
16 use render_task::to_cache_size;
17 use resource_cache::{CacheItem, ImageRequest, ResourceCache};
18 use util::{LayerToWorldFastTransform, MaxRect, calculate_screen_bounding_rect};
19 use util::extract_inner_rect_safe;
20 use std::sync::Arc;
21 
22 pub type ClipStore = FreeList<ClipSources>;
23 pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
24 pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;
25 
26 #[derive(Clone, Debug)]
27 pub struct ClipRegion {
28     pub main: LayerRect,
29     pub image_mask: Option<ImageMask>,
30     pub complex_clips: Vec<ComplexClipRegion>,
31 }
32 
33 impl ClipRegion {
create_for_clip_node( rect: LayerRect, mut complex_clips: Vec<ComplexClipRegion>, mut image_mask: Option<ImageMask>, reference_frame_relative_offset: &LayoutVector2D, ) -> ClipRegion34     pub fn create_for_clip_node(
35         rect: LayerRect,
36         mut complex_clips: Vec<ComplexClipRegion>,
37         mut image_mask: Option<ImageMask>,
38         reference_frame_relative_offset: &LayoutVector2D,
39     ) -> ClipRegion {
40         let rect = rect.translate(reference_frame_relative_offset);
41 
42         if let Some(ref mut image_mask) = image_mask {
43             image_mask.rect = image_mask.rect.translate(reference_frame_relative_offset);
44         }
45 
46         for complex_clip in complex_clips.iter_mut() {
47             complex_clip.rect = complex_clip.rect.translate(reference_frame_relative_offset);
48         }
49 
50         ClipRegion {
51             main: rect,
52             image_mask,
53             complex_clips,
54         }
55     }
56 
create_for_clip_node_with_local_clip( local_clip: &LocalClip, reference_frame_relative_offset: &LayoutVector2D ) -> ClipRegion57     pub fn create_for_clip_node_with_local_clip(
58         local_clip: &LocalClip,
59         reference_frame_relative_offset: &LayoutVector2D
60     ) -> ClipRegion {
61         let complex_clips = match local_clip {
62             &LocalClip::Rect(_) => Vec::new(),
63             &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
64         };
65         ClipRegion::create_for_clip_node(
66             *local_clip.clip_rect(),
67             complex_clips,
68             None,
69             reference_frame_relative_offset
70         )
71     }
72 }
73 
74 #[derive(Debug)]
75 pub enum ClipSource {
76     Rectangle(LayerRect),
77     RoundedRectangle(LayerRect, BorderRadius, ClipMode),
78     Image(ImageMask),
79     /// TODO(gw): This currently only handles dashed style
80     /// clips, where the border style is dashed for both
81     /// adjacent border edges. Expand to handle dotted style
82     /// and different styles per edge.
83     BorderCorner(BorderCornerClipSource),
84     BoxShadow(BoxShadowClipSource),
85 }
86 
87 impl From<ClipRegion> for ClipSources {
from(region: ClipRegion) -> ClipSources88     fn from(region: ClipRegion) -> ClipSources {
89         let mut clips = Vec::new();
90 
91         if let Some(info) = region.image_mask {
92             clips.push(ClipSource::Image(info));
93         }
94 
95         clips.push(ClipSource::Rectangle(region.main));
96 
97         for complex in region.complex_clips {
98             clips.push(ClipSource::new_rounded_rect(
99                 complex.rect,
100                 complex.radii,
101                 complex.mode,
102             ));
103         }
104 
105         ClipSources::new(clips)
106     }
107 }
108 
109 impl ClipSource {
new_rounded_rect( rect: LayerRect, mut radii: BorderRadius, clip_mode: ClipMode ) -> ClipSource110     pub fn new_rounded_rect(
111         rect: LayerRect,
112         mut radii: BorderRadius,
113         clip_mode: ClipMode
114     ) -> ClipSource {
115         ensure_no_corner_overlap(&mut radii, &rect);
116         ClipSource::RoundedRectangle(
117             rect,
118             radii,
119             clip_mode,
120         )
121     }
122 
new_box_shadow( shadow_rect: LayerRect, shadow_radius: BorderRadius, prim_shadow_rect: LayerRect, blur_radius: f32, clip_mode: BoxShadowClipMode, ) -> ClipSource123     pub fn new_box_shadow(
124         shadow_rect: LayerRect,
125         shadow_radius: BorderRadius,
126         prim_shadow_rect: LayerRect,
127         blur_radius: f32,
128         clip_mode: BoxShadowClipMode,
129     ) -> ClipSource {
130         // Get the fractional offsets required to match the
131         // source rect with a minimal rect.
132         let fract_offset = LayerPoint::new(
133             shadow_rect.origin.x.fract().abs(),
134             shadow_rect.origin.y.fract().abs(),
135         );
136         let fract_size = LayerSize::new(
137             shadow_rect.size.width.fract().abs(),
138             shadow_rect.size.height.fract().abs(),
139         );
140 
141         // Create a minimal size primitive mask to blur. In this
142         // case, we ensure the size of each corner is the same,
143         // to simplify the shader logic that stretches the blurred
144         // result across the primitive.
145         let max_corner_width = shadow_radius.top_left.width
146                                     .max(shadow_radius.bottom_left.width)
147                                     .max(shadow_radius.top_right.width)
148                                     .max(shadow_radius.bottom_right.width);
149         let max_corner_height = shadow_radius.top_left.height
150                                     .max(shadow_radius.bottom_left.height)
151                                     .max(shadow_radius.top_right.height)
152                                     .max(shadow_radius.bottom_right.height);
153 
154         // Get maximum distance that can be affected by given blur radius.
155         let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
156 
157         // If the largest corner is smaller than the blur radius, we need to ensure
158         // that it's big enough that the corners don't affect the middle segments.
159         let used_corner_width = max_corner_width.max(blur_region);
160         let used_corner_height = max_corner_height.max(blur_region);
161 
162         // Minimal nine-patch size, corner + internal + corner.
163         let min_shadow_rect_size = LayerSize::new(
164             2.0 * used_corner_width + blur_region,
165             2.0 * used_corner_height + blur_region,
166         );
167 
168         // The minimal rect to blur.
169         let mut minimal_shadow_rect = LayerRect::new(
170             LayerPoint::new(
171                 blur_region + fract_offset.x,
172                 blur_region + fract_offset.y,
173             ),
174             LayerSize::new(
175                 min_shadow_rect_size.width + fract_size.width,
176                 min_shadow_rect_size.height + fract_size.height,
177             ),
178         );
179 
180         // If the width or height ends up being bigger than the original
181         // primitive shadow rect, just blur the entire rect and draw that
182         // as a simple blit. This is necessary for correctness, since the
183         // blur of one corner may affect the blur in another corner.
184         minimal_shadow_rect.size.width = minimal_shadow_rect.size.width.min(shadow_rect.size.width);
185         minimal_shadow_rect.size.height = minimal_shadow_rect.size.height.min(shadow_rect.size.height);
186 
187         // Expand the shadow rect by enough room for the blur to take effect.
188         let shadow_rect_alloc_size = LayerSize::new(
189             2.0 * blur_region + minimal_shadow_rect.size.width.ceil(),
190             2.0 * blur_region + minimal_shadow_rect.size.height.ceil(),
191         );
192 
193         ClipSource::BoxShadow(BoxShadowClipSource {
194             shadow_rect_alloc_size,
195             shadow_radius,
196             prim_shadow_rect,
197             blur_radius,
198             clip_mode,
199             cache_item: CacheItem::invalid(),
200             cache_key: None,
201             clip_data_handle: GpuCacheHandle::new(),
202             minimal_shadow_rect,
203         })
204     }
205 }
206 
207 #[derive(Debug)]
208 pub struct ClipSources {
209     pub clips: Vec<(ClipSource, GpuCacheHandle)>,
210     pub local_inner_rect: LayerRect,
211     pub local_outer_rect: Option<LayerRect>
212 }
213 
214 impl ClipSources {
new(clips: Vec<ClipSource>) -> ClipSources215     pub fn new(clips: Vec<ClipSource>) -> ClipSources {
216         let (local_inner_rect, local_outer_rect) = Self::calculate_inner_and_outer_rects(&clips);
217 
218         let clips = clips
219             .into_iter()
220             .map(|clip| (clip, GpuCacheHandle::new()))
221             .collect();
222 
223         ClipSources {
224             clips,
225             local_inner_rect,
226             local_outer_rect,
227         }
228     }
229 
clips(&self) -> &[(ClipSource, GpuCacheHandle)]230     pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] {
231         &self.clips
232     }
233 
calculate_inner_and_outer_rects(clips: &Vec<ClipSource>) -> (LayerRect, Option<LayerRect>)234     fn calculate_inner_and_outer_rects(clips: &Vec<ClipSource>) -> (LayerRect, Option<LayerRect>) {
235         if clips.is_empty() {
236             return (LayerRect::zero(), None);
237         }
238 
239         // Depending on the complexity of the clip, we may either know the outer and/or inner
240         // rect, or neither or these.  In the case of a clip-out, we currently set the mask bounds
241         // to be unknown. This is conservative, but ensures correctness. In the future we can make
242         // this a lot more clever with some proper region handling.
243         let mut local_outer = Some(LayerRect::max_rect());
244         let mut local_inner = local_outer;
245         let mut can_calculate_inner_rect = true;
246         let mut can_calculate_outer_rect = false;
247         for source in clips {
248             match *source {
249                 ClipSource::Image(ref mask) => {
250                     if !mask.repeat {
251                         can_calculate_outer_rect = true;
252                         local_outer = local_outer.and_then(|r| r.intersection(&mask.rect));
253                     }
254                     local_inner = None;
255                 }
256                 ClipSource::Rectangle(rect) => {
257                     can_calculate_outer_rect = true;
258                     local_outer = local_outer.and_then(|r| r.intersection(&rect));
259                     local_inner = local_inner.and_then(|r| r.intersection(&rect));
260                 }
261                 ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
262                     // Once we encounter a clip-out, we just assume the worst
263                     // case clip mask size, for now.
264                     if mode == ClipMode::ClipOut {
265                         can_calculate_inner_rect = false;
266                         break;
267                     }
268 
269                     can_calculate_outer_rect = true;
270                     local_outer = local_outer.and_then(|r| r.intersection(rect));
271 
272                     let inner_rect = extract_inner_rect_safe(rect, radius);
273                     local_inner = local_inner
274                         .and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
275                 }
276                 ClipSource::BoxShadow(..) |
277                 ClipSource::BorderCorner { .. } => {
278                     can_calculate_inner_rect = false;
279                     break;
280                 }
281             }
282         }
283 
284         let outer = match can_calculate_outer_rect {
285             true => Some(local_outer.unwrap_or_else(LayerRect::zero)),
286             false => None,
287         };
288 
289         let inner = match can_calculate_inner_rect {
290             true => local_inner.unwrap_or_else(LayerRect::zero),
291             false => LayerRect::zero(),
292         };
293 
294         (inner, outer)
295     }
296 
update( &mut self, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, device_pixel_scale: DevicePixelScale, )297     pub fn update(
298         &mut self,
299         gpu_cache: &mut GpuCache,
300         resource_cache: &mut ResourceCache,
301         device_pixel_scale: DevicePixelScale,
302     ) {
303         for &mut (ref mut source, ref mut handle) in &mut self.clips {
304             if let Some(mut request) = gpu_cache.request(handle) {
305                 match *source {
306                     ClipSource::Image(ref mask) => {
307                         let data = ImageMaskData { local_rect: mask.rect };
308                         data.write_gpu_blocks(request);
309                     }
310                     ClipSource::BoxShadow(ref info) => {
311                         request.push([
312                             info.shadow_rect_alloc_size.width,
313                             info.shadow_rect_alloc_size.height,
314                             info.clip_mode as i32 as f32,
315                             0.0,
316                         ]);
317                         request.push(info.prim_shadow_rect);
318                     }
319                     ClipSource::Rectangle(rect) => {
320                         let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
321                         data.write(&mut request);
322                     }
323                     ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
324                         let data = ClipData::rounded_rect(rect, radius, mode);
325                         data.write(&mut request);
326                     }
327                     ClipSource::BorderCorner(ref mut source) => {
328                         source.write(request);
329                     }
330                 }
331             }
332 
333             match *source {
334                 ClipSource::Image(ref mask) => {
335                     resource_cache.request_image(
336                         ImageRequest {
337                             key: mask.image,
338                             rendering: ImageRendering::Auto,
339                             tile: None,
340                         },
341                         gpu_cache,
342                     );
343                 }
344                 ClipSource::BoxShadow(ref mut info) => {
345                     // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
346                     // "the image that would be generated by applying to the shadow a
347                     // Gaussian blur with a standard deviation equal to half the blur radius."
348                     let blur_radius_dp = (info.blur_radius * 0.5 * device_pixel_scale.0).round();
349 
350                     // Create the cache key for this box-shadow render task.
351                     let content_scale = LayerToWorldScale::new(1.0) * device_pixel_scale;
352                     let cache_size = to_cache_size(info.shadow_rect_alloc_size * content_scale);
353                     let bs_cache_key = BoxShadowCacheKey {
354                         blur_radius_dp: blur_radius_dp as i32,
355                         clip_mode: info.clip_mode,
356                         rect_size: (info.shadow_rect_alloc_size * content_scale).round().to_i32(),
357                         br_top_left: (info.shadow_radius.top_left * content_scale).round().to_i32(),
358                         br_top_right: (info.shadow_radius.top_right * content_scale).round().to_i32(),
359                         br_bottom_right: (info.shadow_radius.bottom_right * content_scale).round().to_i32(),
360                         br_bottom_left: (info.shadow_radius.bottom_left * content_scale).round().to_i32(),
361                     };
362 
363                     info.cache_key = Some((cache_size, bs_cache_key));
364 
365                     if let Some(mut request) = gpu_cache.request(&mut info.clip_data_handle) {
366                         let data = ClipData::rounded_rect(
367                             &info.minimal_shadow_rect,
368                             &info.shadow_radius,
369                             ClipMode::Clip,
370                         );
371 
372                         data.write(&mut request);
373                     }
374                 }
375                 _ => {}
376             }
377         }
378     }
379 
get_screen_bounds( &self, transform: &LayerToWorldFastTransform, device_pixel_scale: DevicePixelScale, ) -> (DeviceIntRect, Option<DeviceIntRect>)380     pub fn get_screen_bounds(
381         &self,
382         transform: &LayerToWorldFastTransform,
383         device_pixel_scale: DevicePixelScale,
384     ) -> (DeviceIntRect, Option<DeviceIntRect>) {
385         // If this translation isn't axis aligned or has a perspective component, don't try to
386         // calculate the inner rectangle. The rectangle that we produce would include potentially
387         // clipped screen area.
388         // TODO(mrobinson): We should eventually try to calculate an inner region or some inner
389         // rectangle so that we can do screen inner rectangle optimizations for these kind of
390         // cilps.
391         let can_calculate_inner_rect =
392             transform.preserves_2d_axis_alignment() && !transform.has_perspective_component();
393         let screen_inner_rect = if can_calculate_inner_rect {
394             calculate_screen_bounding_rect(transform, &self.local_inner_rect, device_pixel_scale)
395         } else {
396             DeviceIntRect::zero()
397         };
398 
399         let screen_outer_rect = self.local_outer_rect.map(|outer_rect|
400             calculate_screen_bounding_rect(transform, &outer_rect, device_pixel_scale)
401         );
402 
403         (screen_inner_rect, screen_outer_rect)
404     }
405 }
406 
407 /// Represents a local rect and a device space
408 /// rectangles that are either outside or inside bounds.
409 #[derive(Clone, Debug, PartialEq)]
410 pub struct Geometry {
411     pub local_rect: LayerRect,
412     pub device_rect: DeviceIntRect,
413 }
414 
415 impl From<LayerRect> for Geometry {
from(local_rect: LayerRect) -> Self416     fn from(local_rect: LayerRect) -> Self {
417         Geometry {
418             local_rect,
419             device_rect: DeviceIntRect::zero(),
420         }
421     }
422 }
423 
424 pub trait Contains {
contains(&self, point: &LayoutPoint) -> bool425     fn contains(&self, point: &LayoutPoint) -> bool;
426 }
427 
428 impl Contains for LocalClip {
contains(&self, point: &LayoutPoint) -> bool429     fn contains(&self, point: &LayoutPoint) -> bool {
430         if !self.clip_rect().contains(point) {
431             return false;
432         }
433         match self {
434             &LocalClip::Rect(..) => true,
435             &LocalClip::RoundedRect(_, complex_clip) => complex_clip.contains(point),
436         }
437     }
438 }
439 
440 impl Contains for ComplexClipRegion {
contains(&self, point: &LayoutPoint) -> bool441     fn contains(&self, point: &LayoutPoint) -> bool {
442         rounded_rectangle_contains_point(point, &self.rect, &self.radii)
443     }
444 }
445 
rounded_rectangle_contains_point(point: &LayoutPoint, rect: &LayerRect, radii: &BorderRadius) -> bool446 pub fn rounded_rectangle_contains_point(point: &LayoutPoint,
447                                         rect: &LayerRect,
448                                         radii: &BorderRadius)
449                                         -> bool {
450     if !rect.contains(point) {
451         return false;
452     }
453 
454     let top_left_center = rect.origin + radii.top_left.to_vector();
455     if top_left_center.x > point.x && top_left_center.y > point.y &&
456        !Ellipse::new(radii.top_left).contains(*point - top_left_center.to_vector()) {
457         return false;
458     }
459 
460     let bottom_right_center = rect.bottom_right() - radii.bottom_right.to_vector();
461     if bottom_right_center.x < point.x && bottom_right_center.y < point.y &&
462        !Ellipse::new(radii.bottom_right).contains(*point - bottom_right_center.to_vector()) {
463         return false;
464     }
465 
466     let top_right_center = rect.top_right() +
467                            LayoutVector2D::new(-radii.top_right.width, radii.top_right.height);
468     if top_right_center.x < point.x && top_right_center.y > point.y &&
469        !Ellipse::new(radii.top_right).contains(*point - top_right_center.to_vector()) {
470         return false;
471     }
472 
473     let bottom_left_center = rect.bottom_left() +
474                              LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
475     if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
476        !Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
477         return false;
478     }
479 
480     true
481 }
482 
483 pub type ClipChainNodeRef = Option<Arc<ClipChainNode>>;
484 
485 #[derive(Debug, Clone)]
486 pub struct ClipChainNode {
487     pub work_item: ClipWorkItem,
488     pub local_clip_rect: LayerRect,
489     pub screen_outer_rect: DeviceIntRect,
490     pub screen_inner_rect: DeviceIntRect,
491     pub prev: ClipChainNodeRef,
492 }
493 
494 #[derive(Debug, Clone)]
495 pub struct ClipChain {
496     pub parent_index: Option<ClipChainIndex>,
497     pub combined_outer_screen_rect: DeviceIntRect,
498     pub combined_inner_screen_rect: DeviceIntRect,
499     pub nodes: ClipChainNodeRef,
500 }
501 
502 impl ClipChain {
empty(screen_rect: &DeviceIntRect) -> ClipChain503     pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain {
504         ClipChain {
505             parent_index: None,
506             combined_inner_screen_rect: *screen_rect,
507             combined_outer_screen_rect: *screen_rect,
508             nodes: None,
509         }
510     }
511 
new_with_added_node(&self, new_node: &ClipChainNode) -> ClipChain512     pub fn new_with_added_node(&self, new_node: &ClipChainNode) -> ClipChain {
513         // If the new node's inner rectangle completely surrounds our outer rectangle,
514         // we can discard the new node entirely since it isn't going to affect anything.
515         if new_node.screen_inner_rect.contains_rect(&self.combined_outer_screen_rect) {
516             return self.clone();
517         }
518 
519         let mut new_chain = self.clone();
520         new_chain.add_node(new_node.clone());
521         new_chain
522     }
523 
add_node(&mut self, mut new_node: ClipChainNode)524     pub fn add_node(&mut self, mut new_node: ClipChainNode) {
525         new_node.prev = self.nodes.clone();
526 
527         // If this clip's outer rectangle is completely enclosed by the clip
528         // chain's inner rectangle, then the only clip that matters from this point
529         // on is this clip. We can disconnect this clip from the parent clip chain.
530         if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) {
531             new_node.prev = None;
532         }
533 
534         self.combined_outer_screen_rect =
535             self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect)
536             .unwrap_or_else(DeviceIntRect::zero);
537         self.combined_inner_screen_rect =
538             self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect)
539             .unwrap_or_else(DeviceIntRect::zero);
540 
541         self.nodes = Some(Arc::new(new_node));
542     }
543 }
544 
545 pub struct ClipChainNodeIter {
546     pub current: ClipChainNodeRef,
547 }
548 
549 impl Iterator for ClipChainNodeIter {
550     type Item = Arc<ClipChainNode>;
551 
next(&mut self) -> ClipChainNodeRef552     fn next(&mut self) -> ClipChainNodeRef {
553         let previous = self.current.clone();
554         self.current = match self.current {
555             Some(ref item) => item.prev.clone(),
556             None => return None,
557         };
558         previous
559     }
560 }
561 
562 #[derive(Debug, Clone)]
563 #[cfg_attr(feature = "capture", derive(Serialize))]
564 #[cfg_attr(feature = "replay", derive(Deserialize))]
565 pub struct ClipWorkItem {
566     pub scroll_node_data_index: ClipScrollNodeIndex,
567     pub clip_sources: ClipSourcesWeakHandle,
568     pub coordinate_system_id: CoordinateSystemId,
569 }
570 
571