1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/trees/occlusion_tracker.h"
9 #include "cc/base/math_util.h"
10 #include "cc/layers/layer.h"
11 #include "cc/layers/layer_impl.h"
12 #include "cc/layers/render_surface.h"
13 #include "cc/layers/render_surface_impl.h"
14 #include "ui/gfx/quad_f.h"
15 #include "ui/gfx/rect_conversions.h"
19 template <typename LayerType
>
20 OcclusionTracker
<LayerType
>::OcclusionTracker(
21 const gfx::Rect
& screen_space_clip_rect
)
22 : screen_space_clip_rect_(screen_space_clip_rect
),
23 occluding_screen_space_rects_(NULL
),
24 non_occluding_screen_space_rects_(NULL
) {}
26 template <typename LayerType
>
27 OcclusionTracker
<LayerType
>::~OcclusionTracker() {}
29 template <typename LayerType
>
30 void OcclusionTracker
<LayerType
>::EnterLayer(
31 const LayerIteratorPosition
<LayerType
>& layer_iterator
) {
32 LayerType
* render_target
= layer_iterator
.target_render_surface_layer
;
34 if (layer_iterator
.represents_itself
)
35 EnterRenderTarget(render_target
);
36 else if (layer_iterator
.represents_target_render_surface
)
37 FinishedRenderTarget(render_target
);
40 template <typename LayerType
>
41 void OcclusionTracker
<LayerType
>::LeaveLayer(
42 const LayerIteratorPosition
<LayerType
>& layer_iterator
) {
43 LayerType
* render_target
= layer_iterator
.target_render_surface_layer
;
45 if (layer_iterator
.represents_itself
)
46 MarkOccludedBehindLayer(layer_iterator
.current_layer
);
47 // TODO(danakj): This should be done when entering the contributing surface,
48 // but in a way that the surface's own occlusion won't occlude itself.
49 else if (layer_iterator
.represents_contributing_render_surface
)
50 LeaveToRenderTarget(render_target
);
53 template <typename RenderSurfaceType
>
54 static gfx::Rect
ScreenSpaceClipRectInTargetSurface(
55 const RenderSurfaceType
* target_surface
,
56 const gfx::Rect
& screen_space_clip_rect
) {
57 gfx::Transform
inverse_screen_space_transform(
58 gfx::Transform::kSkipInitialization
);
59 if (!target_surface
->screen_space_transform().GetInverse(
60 &inverse_screen_space_transform
))
61 return target_surface
->content_rect();
63 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform
,
64 screen_space_clip_rect
);
67 template <typename RenderSurfaceType
>
68 static Region
TransformSurfaceOpaqueRegion(
71 const gfx::Rect
& clip_rect_in_new_target
,
72 const gfx::Transform
& transform
) {
76 // Verify that rects within the |surface| will remain rects in its target
77 // surface after applying |transform|. If this is true, then apply |transform|
78 // to each rect within |region| in order to transform the entire Region.
80 // TODO(danakj): Find a rect interior to each transformed quad.
81 if (!transform
.Preserves2dAxisAlignment())
84 // TODO(danakj): If the Region is too complex, degrade gracefully here by
85 // skipping rects in it.
86 Region transformed_region
;
87 for (Region::Iterator
rects(region
); rects
.has_rect(); rects
.next()) {
89 gfx::QuadF transformed_quad
=
90 MathUtil::MapQuad(transform
, gfx::QuadF(rects
.rect()), &clipped
);
91 gfx::Rect transformed_rect
=
92 gfx::ToEnclosedRect(transformed_quad
.BoundingBox());
93 DCHECK(!clipped
); // We only map if the transform preserves axis alignment.
95 transformed_rect
.Intersect(clip_rect_in_new_target
);
96 transformed_region
.Union(transformed_rect
);
98 return transformed_region
;
101 static inline bool LayerOpacityKnown(const Layer
* layer
) {
102 return !layer
->draw_opacity_is_animating();
104 static inline bool LayerOpacityKnown(const LayerImpl
* layer
) {
107 static inline bool LayerTransformsToTargetKnown(const Layer
* layer
) {
108 return !layer
->draw_transform_is_animating();
110 static inline bool LayerTransformsToTargetKnown(const LayerImpl
* layer
) {
114 static inline bool SurfaceOpacityKnown(const RenderSurface
* rs
) {
115 return !rs
->draw_opacity_is_animating();
117 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl
* rs
) {
120 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface
* rs
) {
121 return !rs
->target_surface_transforms_are_animating();
123 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl
* rs
) {
126 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface
* rs
) {
127 return !rs
->screen_space_transforms_are_animating();
129 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl
* rs
) {
133 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer
* layer
) {
134 return layer
->is_3d_sorted();
136 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl
* layer
) {
140 template <typename LayerType
>
141 static inline bool LayerIsHidden(const LayerType
* layer
) {
142 return layer
->hide_layer_and_subtree() ||
143 (layer
->parent() && LayerIsHidden(layer
->parent()));
146 template <typename LayerType
>
147 void OcclusionTracker
<LayerType
>::EnterRenderTarget(
148 const LayerType
* new_target
) {
149 if (!stack_
.empty() && stack_
.back().target
== new_target
)
152 const LayerType
* old_target
= NULL
;
153 const typename
LayerType::RenderSurfaceType
* old_occlusion_immune_ancestor
=
155 if (!stack_
.empty()) {
156 old_target
= stack_
.back().target
;
157 old_occlusion_immune_ancestor
=
158 old_target
->render_surface()->nearest_occlusion_immune_ancestor();
160 const typename
LayerType::RenderSurfaceType
* new_occlusion_immune_ancestor
=
161 new_target
->render_surface()->nearest_occlusion_immune_ancestor();
163 stack_
.push_back(StackObject(new_target
));
165 // We copy the screen occlusion into the new RenderSurface subtree, but we
166 // never copy in the occlusion from inside the target, since we are looking
167 // at a new RenderSurface target.
169 // If entering an unoccluded subtree, do not carry forward the outside
170 // occlusion calculated so far.
171 bool entering_unoccluded_subtree
=
172 new_occlusion_immune_ancestor
&&
173 new_occlusion_immune_ancestor
!= old_occlusion_immune_ancestor
;
175 bool have_transform_from_screen_to_new_target
= false;
176 gfx::Transform
inverse_new_target_screen_space_transform(
177 // Note carefully, not used if screen space transform is uninvertible.
178 gfx::Transform::kSkipInitialization
);
179 if (SurfaceTransformsToScreenKnown(new_target
->render_surface())) {
180 have_transform_from_screen_to_new_target
=
181 new_target
->render_surface()->screen_space_transform().GetInverse(
182 &inverse_new_target_screen_space_transform
);
185 bool entering_root_target
= new_target
->parent() == NULL
;
187 bool copy_outside_occlusion_forward
=
189 !entering_unoccluded_subtree
&&
190 have_transform_from_screen_to_new_target
&&
191 !entering_root_target
;
192 if (!copy_outside_occlusion_forward
)
195 int last_index
= stack_
.size() - 1;
196 gfx::Transform
old_target_to_new_target_transform(
197 inverse_new_target_screen_space_transform
,
198 old_target
->render_surface()->screen_space_transform());
199 stack_
[last_index
].occlusion_from_outside_target
=
200 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
201 stack_
[last_index
- 1].occlusion_from_outside_target
,
204 old_target_to_new_target_transform
);
205 stack_
[last_index
].occlusion_from_outside_target
.Union(
206 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
207 stack_
[last_index
- 1].occlusion_from_inside_target
,
210 old_target_to_new_target_transform
));
213 template <typename LayerType
>
214 void OcclusionTracker
<LayerType
>::FinishedRenderTarget(
215 const LayerType
* finished_target
) {
216 // Make sure we know about the target surface.
217 EnterRenderTarget(finished_target
);
219 typename
LayerType::RenderSurfaceType
* surface
=
220 finished_target
->render_surface();
222 // Readbacks always happen on render targets so we only need to check
223 // for readbacks here.
224 bool target_is_only_for_copy_request
=
225 finished_target
->HasCopyRequest() && LayerIsHidden(finished_target
);
227 // If the occlusion within the surface can not be applied to things outside of
228 // the surface's subtree, then clear the occlusion here so it won't be used.
229 if (finished_target
->mask_layer() || !SurfaceOpacityKnown(surface
) ||
230 surface
->draw_opacity() < 1 ||
231 !finished_target
->uses_default_blend_mode() ||
232 target_is_only_for_copy_request
||
233 finished_target
->filters().HasFilterThatAffectsOpacity()) {
234 stack_
.back().occlusion_from_outside_target
.Clear();
235 stack_
.back().occlusion_from_inside_target
.Clear();
236 } else if (!SurfaceTransformsToTargetKnown(surface
)) {
237 stack_
.back().occlusion_from_inside_target
.Clear();
238 stack_
.back().occlusion_from_outside_target
.Clear();
242 template <typename LayerType
>
243 static void ReduceOcclusionBelowSurface(LayerType
* contributing_layer
,
244 const gfx::Rect
& surface_rect
,
245 const gfx::Transform
& surface_transform
,
246 LayerType
* render_target
,
247 Region
* occlusion_from_inside_target
) {
248 if (surface_rect
.IsEmpty())
251 gfx::Rect affected_area_in_target
=
252 MathUtil::MapEnclosingClippedRect(surface_transform
, surface_rect
);
253 if (contributing_layer
->render_surface()->is_clipped()) {
254 affected_area_in_target
.Intersect(
255 contributing_layer
->render_surface()->clip_rect());
257 if (affected_area_in_target
.IsEmpty())
260 int outset_top
, outset_right
, outset_bottom
, outset_left
;
261 contributing_layer
->background_filters().GetOutsets(
262 &outset_top
, &outset_right
, &outset_bottom
, &outset_left
);
264 // The filter can move pixels from outside of the clip, so allow affected_area
265 // to expand outside the clip.
266 affected_area_in_target
.Inset(
267 -outset_left
, -outset_top
, -outset_right
, -outset_bottom
);
268 Region affected_occlusion
= IntersectRegions(*occlusion_from_inside_target
,
269 affected_area_in_target
);
270 Region::Iterator
affected_occlusion_rects(affected_occlusion
);
272 occlusion_from_inside_target
->Subtract(affected_area_in_target
);
273 for (; affected_occlusion_rects
.has_rect(); affected_occlusion_rects
.next()) {
274 gfx::Rect occlusion_rect
= affected_occlusion_rects
.rect();
276 // Shrink the rect by expanding the non-opaque pixels outside the rect.
278 // The left outset of the filters moves pixels on the right side of
279 // the occlusion_rect into it, shrinking its right edge.
281 occlusion_rect
.x() == affected_area_in_target
.x() ? 0 : outset_right
;
283 occlusion_rect
.y() == affected_area_in_target
.y() ? 0 : outset_bottom
;
285 occlusion_rect
.right() == affected_area_in_target
.right() ?
288 occlusion_rect
.bottom() == affected_area_in_target
.bottom() ?
291 occlusion_rect
.Inset(shrink_left
, shrink_top
, shrink_right
, shrink_bottom
);
293 occlusion_from_inside_target
->Union(occlusion_rect
);
297 template <typename LayerType
>
298 void OcclusionTracker
<LayerType
>::LeaveToRenderTarget(
299 const LayerType
* new_target
) {
300 int last_index
= stack_
.size() - 1;
301 bool surface_will_be_at_top_after_pop
=
302 stack_
.size() > 1 && stack_
[last_index
- 1].target
== new_target
;
304 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
305 // out to its parent target RenderSurfaceImpl. The target occlusion can be
306 // merged out as well but needs to be transformed to the new target.
308 const LayerType
* old_target
= stack_
[last_index
].target
;
309 const typename
LayerType::RenderSurfaceType
* old_surface
=
310 old_target
->render_surface();
312 Region old_occlusion_from_inside_target_in_new_target
=
313 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
314 stack_
[last_index
].occlusion_from_inside_target
,
315 old_surface
->is_clipped(),
316 old_surface
->clip_rect(),
317 old_surface
->draw_transform());
318 if (old_target
->has_replica() && !old_target
->replica_has_mask()) {
319 old_occlusion_from_inside_target_in_new_target
.Union(
320 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
321 stack_
[last_index
].occlusion_from_inside_target
,
322 old_surface
->is_clipped(),
323 old_surface
->clip_rect(),
324 old_surface
->replica_draw_transform()));
327 Region old_occlusion_from_outside_target_in_new_target
=
328 TransformSurfaceOpaqueRegion
<typename
LayerType::RenderSurfaceType
>(
329 stack_
[last_index
].occlusion_from_outside_target
,
332 old_surface
->draw_transform());
334 gfx::Rect unoccluded_surface_rect
;
335 gfx::Rect unoccluded_replica_rect
;
336 if (old_target
->background_filters().HasFilterThatMovesPixels()) {
337 unoccluded_surface_rect
= UnoccludedContributingSurfaceContentRect(
338 old_target
, old_surface
->content_rect(), old_surface
->draw_transform());
339 if (old_target
->has_replica()) {
340 unoccluded_replica_rect
= UnoccludedContributingSurfaceContentRect(
342 old_surface
->content_rect(),
343 old_surface
->replica_draw_transform());
347 if (surface_will_be_at_top_after_pop
) {
348 // Merge the top of the stack down.
349 stack_
[last_index
- 1].occlusion_from_inside_target
.Union(
350 old_occlusion_from_inside_target_in_new_target
);
351 // TODO(danakj): Strictly this should subtract the inside target occlusion
353 if (new_target
->parent()) {
354 stack_
[last_index
- 1].occlusion_from_outside_target
.Union(
355 old_occlusion_from_outside_target_in_new_target
);
359 // Replace the top of the stack with the new pushed surface.
360 stack_
.back().target
= new_target
;
361 stack_
.back().occlusion_from_inside_target
=
362 old_occlusion_from_inside_target_in_new_target
;
363 if (new_target
->parent()) {
364 stack_
.back().occlusion_from_outside_target
=
365 old_occlusion_from_outside_target_in_new_target
;
367 stack_
.back().occlusion_from_outside_target
.Clear();
371 if (!old_target
->background_filters().HasFilterThatMovesPixels())
374 ReduceOcclusionBelowSurface(old_target
,
375 unoccluded_surface_rect
,
376 old_surface
->draw_transform(),
378 &stack_
.back().occlusion_from_inside_target
);
379 ReduceOcclusionBelowSurface(old_target
,
380 unoccluded_surface_rect
,
381 old_surface
->draw_transform(),
383 &stack_
.back().occlusion_from_outside_target
);
385 if (!old_target
->has_replica())
387 ReduceOcclusionBelowSurface(old_target
,
388 unoccluded_replica_rect
,
389 old_surface
->replica_draw_transform(),
391 &stack_
.back().occlusion_from_inside_target
);
392 ReduceOcclusionBelowSurface(old_target
,
393 unoccluded_replica_rect
,
394 old_surface
->replica_draw_transform(),
396 &stack_
.back().occlusion_from_outside_target
);
399 template <typename LayerType
>
400 void OcclusionTracker
<LayerType
>::MarkOccludedBehindLayer(
401 const LayerType
* layer
) {
402 DCHECK(!stack_
.empty());
403 DCHECK_EQ(layer
->render_target(), stack_
.back().target
);
407 if (!LayerOpacityKnown(layer
) || layer
->draw_opacity() < 1)
410 if (!layer
->uses_default_blend_mode())
413 if (LayerIsInUnsorted3dRenderingContext(layer
))
416 if (!LayerTransformsToTargetKnown(layer
))
419 Region opaque_contents
= layer
->VisibleContentOpaqueRegion();
420 if (opaque_contents
.IsEmpty())
423 DCHECK(layer
->visible_content_rect().Contains(opaque_contents
.bounds()));
425 // TODO(danakj): Find a rect interior to each transformed quad.
426 if (!layer
->draw_transform().Preserves2dAxisAlignment())
429 gfx::Rect clip_rect_in_target
= ScreenSpaceClipRectInTargetSurface(
430 layer
->render_target()->render_surface(), screen_space_clip_rect_
);
431 if (layer
->is_clipped()) {
432 clip_rect_in_target
.Intersect(layer
->clip_rect());
434 clip_rect_in_target
.Intersect(
435 layer
->render_target()->render_surface()->content_rect());
438 for (Region::Iterator
opaque_content_rects(opaque_contents
);
439 opaque_content_rects
.has_rect();
440 opaque_content_rects
.next()) {
442 gfx::QuadF transformed_quad
= MathUtil::MapQuad(
443 layer
->draw_transform(),
444 gfx::QuadF(opaque_content_rects
.rect()),
446 gfx::Rect transformed_rect
=
447 gfx::ToEnclosedRect(transformed_quad
.BoundingBox());
448 DCHECK(!clipped
); // We only map if the transform preserves axis alignment.
449 transformed_rect
.Intersect(clip_rect_in_target
);
450 if (transformed_rect
.width() < minimum_tracking_size_
.width() &&
451 transformed_rect
.height() < minimum_tracking_size_
.height())
453 stack_
.back().occlusion_from_inside_target
.Union(transformed_rect
);
455 if (!occluding_screen_space_rects_
)
458 // Save the occluding area in screen space for debug visualization.
459 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
460 layer
->render_target()->render_surface()->screen_space_transform(),
461 gfx::QuadF(transformed_rect
), &clipped
);
462 // TODO(danakj): Store the quad in the debug info instead of the bounding
464 gfx::Rect screen_space_rect
=
465 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
466 occluding_screen_space_rects_
->push_back(screen_space_rect
);
469 if (!non_occluding_screen_space_rects_
)
472 Region non_opaque_contents
=
473 SubtractRegions(gfx::Rect(layer
->content_bounds()), opaque_contents
);
474 for (Region::Iterator
non_opaque_content_rects(non_opaque_contents
);
475 non_opaque_content_rects
.has_rect();
476 non_opaque_content_rects
.next()) {
477 // We've already checked for clipping in the MapQuad call above, these calls
478 // should not clip anything further.
479 gfx::Rect transformed_rect
= gfx::ToEnclosedRect(
480 MathUtil::MapClippedRect(layer
->draw_transform(),
481 gfx::RectF(non_opaque_content_rects
.rect())));
482 transformed_rect
.Intersect(clip_rect_in_target
);
483 if (transformed_rect
.IsEmpty())
487 gfx::QuadF screen_space_quad
= MathUtil::MapQuad(
488 layer
->render_target()->render_surface()->screen_space_transform(),
489 gfx::QuadF(transformed_rect
),
491 // TODO(danakj): Store the quad in the debug info instead of the bounding
493 gfx::Rect screen_space_rect
=
494 gfx::ToEnclosedRect(screen_space_quad
.BoundingBox());
495 non_occluding_screen_space_rects_
->push_back(screen_space_rect
);
499 template <typename LayerType
>
500 bool OcclusionTracker
<LayerType
>::Occluded(
501 const LayerType
* render_target
,
502 const gfx::Rect
& content_rect
,
503 const gfx::Transform
& draw_transform
) const {
504 DCHECK(!stack_
.empty());
507 if (content_rect
.IsEmpty())
510 // For tests with no render target.
514 DCHECK_EQ(render_target
->render_target(), render_target
);
515 DCHECK(render_target
->render_surface());
516 DCHECK_EQ(render_target
, stack_
.back().target
);
518 if (stack_
.back().occlusion_from_inside_target
.IsEmpty() &&
519 stack_
.back().occlusion_from_outside_target
.IsEmpty()) {
523 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
524 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
527 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
528 // partial pixels in the resulting Rect.
529 Region unoccluded_region_in_target_surface
=
530 MathUtil::MapEnclosingClippedRect(draw_transform
, content_rect
);
531 unoccluded_region_in_target_surface
.Subtract(
532 stack_
.back().occlusion_from_inside_target
);
533 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion
=
534 unoccluded_region_in_target_surface
.bounds();
535 unoccluded_region_in_target_surface
.Subtract(
536 stack_
.back().occlusion_from_outside_target
);
538 gfx::RectF unoccluded_rect_in_target_surface
=
539 unoccluded_region_in_target_surface
.bounds();
541 return unoccluded_rect_in_target_surface
.IsEmpty();
544 template <typename LayerType
>
545 gfx::Rect OcclusionTracker
<LayerType
>::UnoccludedContentRect(
546 const LayerType
* render_target
,
547 const gfx::Rect
& content_rect
,
548 const gfx::Transform
& draw_transform
) const {
549 DCHECK(!stack_
.empty());
552 if (content_rect
.IsEmpty())
555 // For tests with no render target.
559 DCHECK_EQ(render_target
->render_target(), render_target
);
560 DCHECK(render_target
->render_surface());
561 DCHECK_EQ(render_target
, stack_
.back().target
);
563 if (stack_
.back().occlusion_from_inside_target
.IsEmpty() &&
564 stack_
.back().occlusion_from_outside_target
.IsEmpty()) {
568 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
569 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
572 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
573 // partial pixels in the resulting Rect.
574 Region unoccluded_region_in_target_surface
=
575 MathUtil::MapEnclosingClippedRect(draw_transform
, content_rect
);
576 unoccluded_region_in_target_surface
.Subtract(
577 stack_
.back().occlusion_from_inside_target
);
578 unoccluded_region_in_target_surface
.Subtract(
579 stack_
.back().occlusion_from_outside_target
);
581 gfx::Rect unoccluded_rect_in_target_surface
=
582 unoccluded_region_in_target_surface
.bounds();
583 gfx::Rect unoccluded_rect
= MathUtil::ProjectEnclosingClippedRect(
584 inverse_draw_transform
, unoccluded_rect_in_target_surface
);
585 unoccluded_rect
.Intersect(content_rect
);
587 return unoccluded_rect
;
590 template <typename LayerType
>
591 gfx::Rect OcclusionTracker
<LayerType
>::UnoccludedContributingSurfaceContentRect(
592 const LayerType
* layer
,
593 const gfx::Rect
& content_rect
,
594 const gfx::Transform
& draw_transform
) const {
595 DCHECK(!stack_
.empty());
596 // The layer is a contributing render_target so it should have a surface.
597 DCHECK(layer
->render_surface());
598 // The layer is a contributing render_target so its target should be itself.
599 DCHECK_EQ(layer
->render_target(), layer
);
600 // The layer should not be the root, else what is is contributing to?
601 DCHECK(layer
->parent());
602 // This should be called while the layer is still considered the current
603 // target in the occlusion tracker.
604 DCHECK_EQ(layer
, stack_
.back().target
);
606 if (content_rect
.IsEmpty())
609 // A contributing surface doesn't get occluded by things inside its own
610 // surface, so only things outside the surface can occlude it. That occlusion
611 // is found just below the top of the stack (if it exists).
612 bool has_occlusion
= stack_
.size() > 1;
616 const StackObject
& second_last
= stack_
[stack_
.size() - 2];
618 if (second_last
.occlusion_from_inside_target
.IsEmpty() &&
619 second_last
.occlusion_from_outside_target
.IsEmpty())
622 gfx::Transform
inverse_draw_transform(gfx::Transform::kSkipInitialization
);
623 if (!draw_transform
.GetInverse(&inverse_draw_transform
))
626 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
627 // partial pixels in the resulting Rect.
628 Region unoccluded_region_in_target_surface
=
629 MathUtil::MapEnclosingClippedRect(draw_transform
, content_rect
);
630 unoccluded_region_in_target_surface
.Subtract(
631 second_last
.occlusion_from_inside_target
);
632 unoccluded_region_in_target_surface
.Subtract(
633 second_last
.occlusion_from_outside_target
);
635 gfx::Rect unoccluded_rect_in_target_surface
=
636 unoccluded_region_in_target_surface
.bounds();
637 gfx::Rect unoccluded_rect
= MathUtil::ProjectEnclosingClippedRect(
638 inverse_draw_transform
, unoccluded_rect_in_target_surface
);
639 unoccluded_rect
.Intersect(content_rect
);
641 return unoccluded_rect
;
644 // Instantiate (and export) templates here for the linker.
645 template class OcclusionTracker
<Layer
>;
646 template class OcclusionTracker
<LayerImpl
>;