Lots of random cleanups, mostly for native_theme_win.cc:
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blobf4a414f6de29c35815f8abf9592974f0c56a6e1a
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"
7 #include <algorithm>
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"
17 namespace cc {
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(
69 const Region& region,
70 bool have_clip_rect,
71 const gfx::Rect& clip_rect_in_new_target,
72 const gfx::Transform& transform) {
73 if (region.IsEmpty())
74 return Region();
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())
82 return Region();
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()) {
88 bool clipped;
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.
94 if (have_clip_rect)
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) {
105 return true;
107 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
108 return !layer->draw_transform_is_animating();
110 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
111 return true;
114 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
115 return !rs->draw_opacity_is_animating();
117 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
118 return true;
120 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
121 return !rs->target_surface_transforms_are_animating();
123 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
124 return true;
126 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
127 return !rs->screen_space_transforms_are_animating();
129 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
130 return true;
133 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
134 return layer->Is3dSorted();
136 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
137 return false;
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)
150 return;
152 const LayerType* old_target = NULL;
153 const typename LayerType::RenderSurfaceType* old_occlusion_immune_ancestor =
154 NULL;
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 =
188 stack_.size() > 1 &&
189 !entering_unoccluded_subtree &&
190 have_transform_from_screen_to_new_target &&
191 !entering_root_target;
192 if (!copy_outside_occlusion_forward)
193 return;
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,
202 false,
203 gfx::Rect(),
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,
208 false,
209 gfx::Rect(),
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())
249 return;
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())
258 return;
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.
280 int shrink_left =
281 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
282 int shrink_top =
283 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
284 int shrink_right =
285 occlusion_rect.right() == affected_area_in_target.right() ?
286 0 : outset_left;
287 int shrink_bottom =
288 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
289 0 : outset_top;
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,
330 false,
331 gfx::Rect(),
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_surface->content_rect(), old_surface->draw_transform());
339 if (old_target->has_replica()) {
340 unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
341 old_surface->content_rect(),
342 old_surface->replica_draw_transform());
346 if (surface_will_be_at_top_after_pop) {
347 // Merge the top of the stack down.
348 stack_[last_index - 1].occlusion_from_inside_target.Union(
349 old_occlusion_from_inside_target_in_new_target);
350 // TODO(danakj): Strictly this should subtract the inside target occlusion
351 // before union.
352 if (new_target->parent()) {
353 stack_[last_index - 1].occlusion_from_outside_target.Union(
354 old_occlusion_from_outside_target_in_new_target);
356 stack_.pop_back();
357 } else {
358 // Replace the top of the stack with the new pushed surface.
359 stack_.back().target = new_target;
360 stack_.back().occlusion_from_inside_target =
361 old_occlusion_from_inside_target_in_new_target;
362 if (new_target->parent()) {
363 stack_.back().occlusion_from_outside_target =
364 old_occlusion_from_outside_target_in_new_target;
365 } else {
366 stack_.back().occlusion_from_outside_target.Clear();
370 if (!old_target->background_filters().HasFilterThatMovesPixels())
371 return;
373 ReduceOcclusionBelowSurface(old_target,
374 unoccluded_surface_rect,
375 old_surface->draw_transform(),
376 new_target,
377 &stack_.back().occlusion_from_inside_target);
378 ReduceOcclusionBelowSurface(old_target,
379 unoccluded_surface_rect,
380 old_surface->draw_transform(),
381 new_target,
382 &stack_.back().occlusion_from_outside_target);
384 if (!old_target->has_replica())
385 return;
386 ReduceOcclusionBelowSurface(old_target,
387 unoccluded_replica_rect,
388 old_surface->replica_draw_transform(),
389 new_target,
390 &stack_.back().occlusion_from_inside_target);
391 ReduceOcclusionBelowSurface(old_target,
392 unoccluded_replica_rect,
393 old_surface->replica_draw_transform(),
394 new_target,
395 &stack_.back().occlusion_from_outside_target);
398 template <typename LayerType>
399 void OcclusionTracker<LayerType>::MarkOccludedBehindLayer(
400 const LayerType* layer) {
401 DCHECK(!stack_.empty());
402 DCHECK_EQ(layer->render_target(), stack_.back().target);
403 if (stack_.empty())
404 return;
406 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
407 return;
409 if (!layer->uses_default_blend_mode())
410 return;
412 if (LayerIsInUnsorted3dRenderingContext(layer))
413 return;
415 if (!LayerTransformsToTargetKnown(layer))
416 return;
418 Region opaque_contents = layer->VisibleContentOpaqueRegion();
419 if (opaque_contents.IsEmpty())
420 return;
422 DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
424 // TODO(danakj): Find a rect interior to each transformed quad.
425 if (!layer->draw_transform().Preserves2dAxisAlignment())
426 return;
428 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
429 layer->render_target()->render_surface(), screen_space_clip_rect_);
430 if (layer->is_clipped()) {
431 clip_rect_in_target.Intersect(layer->clip_rect());
432 } else {
433 clip_rect_in_target.Intersect(
434 layer->render_target()->render_surface()->content_rect());
437 for (Region::Iterator opaque_content_rects(opaque_contents);
438 opaque_content_rects.has_rect();
439 opaque_content_rects.next()) {
440 bool clipped;
441 gfx::QuadF transformed_quad = MathUtil::MapQuad(
442 layer->draw_transform(),
443 gfx::QuadF(opaque_content_rects.rect()),
444 &clipped);
445 gfx::Rect transformed_rect =
446 gfx::ToEnclosedRect(transformed_quad.BoundingBox());
447 DCHECK(!clipped); // We only map if the transform preserves axis alignment.
448 transformed_rect.Intersect(clip_rect_in_target);
449 if (transformed_rect.width() < minimum_tracking_size_.width() &&
450 transformed_rect.height() < minimum_tracking_size_.height())
451 continue;
452 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
454 if (!occluding_screen_space_rects_)
455 continue;
457 // Save the occluding area in screen space for debug visualization.
458 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
459 layer->render_target()->render_surface()->screen_space_transform(),
460 gfx::QuadF(transformed_rect), &clipped);
461 // TODO(danakj): Store the quad in the debug info instead of the bounding
462 // box.
463 gfx::Rect screen_space_rect =
464 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
465 occluding_screen_space_rects_->push_back(screen_space_rect);
468 if (!non_occluding_screen_space_rects_)
469 return;
471 Region non_opaque_contents =
472 SubtractRegions(gfx::Rect(layer->content_bounds()), opaque_contents);
473 for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
474 non_opaque_content_rects.has_rect();
475 non_opaque_content_rects.next()) {
476 // We've already checked for clipping in the MapQuad call above, these calls
477 // should not clip anything further.
478 gfx::Rect transformed_rect = gfx::ToEnclosedRect(
479 MathUtil::MapClippedRect(layer->draw_transform(),
480 gfx::RectF(non_opaque_content_rects.rect())));
481 transformed_rect.Intersect(clip_rect_in_target);
482 if (transformed_rect.IsEmpty())
483 continue;
485 bool clipped;
486 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
487 layer->render_target()->render_surface()->screen_space_transform(),
488 gfx::QuadF(transformed_rect),
489 &clipped);
490 // TODO(danakj): Store the quad in the debug info instead of the bounding
491 // box.
492 gfx::Rect screen_space_rect =
493 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
494 non_occluding_screen_space_rects_->push_back(screen_space_rect);
498 template <typename LayerType>
499 bool OcclusionTracker<LayerType>::Occluded(
500 const LayerType* render_target,
501 const gfx::Rect& content_rect,
502 const gfx::Transform& draw_transform) const {
503 DCHECK(!stack_.empty());
504 if (stack_.empty())
505 return false;
506 if (content_rect.IsEmpty())
507 return true;
509 // For tests with no render target.
510 if (!render_target)
511 return false;
513 DCHECK_EQ(render_target->render_target(), render_target);
514 DCHECK(render_target->render_surface());
515 DCHECK_EQ(render_target, stack_.back().target);
517 if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
518 stack_.back().occlusion_from_outside_target.IsEmpty()) {
519 return false;
522 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
523 if (!draw_transform.GetInverse(&inverse_draw_transform))
524 return false;
526 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
527 // partial pixels in the resulting Rect.
528 Region unoccluded_region_in_target_surface =
529 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
530 unoccluded_region_in_target_surface.Subtract(
531 stack_.back().occlusion_from_inside_target);
532 gfx::RectF unoccluded_rect_in_target_surface_without_outside_occlusion =
533 unoccluded_region_in_target_surface.bounds();
534 unoccluded_region_in_target_surface.Subtract(
535 stack_.back().occlusion_from_outside_target);
537 gfx::RectF unoccluded_rect_in_target_surface =
538 unoccluded_region_in_target_surface.bounds();
540 return unoccluded_rect_in_target_surface.IsEmpty();
543 template <typename LayerType>
544 gfx::Rect OcclusionTracker<LayerType>::UnoccludedContentRect(
545 const gfx::Rect& content_rect,
546 const gfx::Transform& draw_transform) const {
547 if (stack_.empty())
548 return content_rect;
549 if (content_rect.IsEmpty())
550 return content_rect;
552 if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
553 stack_.back().occlusion_from_outside_target.IsEmpty()) {
554 return content_rect;
557 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
558 if (!draw_transform.GetInverse(&inverse_draw_transform))
559 return content_rect;
561 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
562 // partial pixels in the resulting Rect.
563 Region unoccluded_region_in_target_surface =
564 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
565 unoccluded_region_in_target_surface.Subtract(
566 stack_.back().occlusion_from_inside_target);
567 unoccluded_region_in_target_surface.Subtract(
568 stack_.back().occlusion_from_outside_target);
570 gfx::Rect unoccluded_rect_in_target_surface =
571 unoccluded_region_in_target_surface.bounds();
572 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
573 inverse_draw_transform, unoccluded_rect_in_target_surface);
574 unoccluded_rect.Intersect(content_rect);
576 return unoccluded_rect;
579 template <typename LayerType>
580 gfx::Rect OcclusionTracker<LayerType>::UnoccludedContributingSurfaceContentRect(
581 const gfx::Rect& content_rect,
582 const gfx::Transform& draw_transform) const {
583 if (content_rect.IsEmpty())
584 return content_rect;
586 // A contributing surface doesn't get occluded by things inside its own
587 // surface, so only things outside the surface can occlude it. That occlusion
588 // is found just below the top of the stack (if it exists).
589 bool has_occlusion = stack_.size() > 1;
590 if (!has_occlusion)
591 return content_rect;
593 const StackObject& second_last = stack_[stack_.size() - 2];
595 if (second_last.occlusion_from_inside_target.IsEmpty() &&
596 second_last.occlusion_from_outside_target.IsEmpty())
597 return content_rect;
599 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
600 if (!draw_transform.GetInverse(&inverse_draw_transform))
601 return content_rect;
603 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
604 // partial pixels in the resulting Rect.
605 Region unoccluded_region_in_target_surface =
606 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
607 unoccluded_region_in_target_surface.Subtract(
608 second_last.occlusion_from_inside_target);
609 unoccluded_region_in_target_surface.Subtract(
610 second_last.occlusion_from_outside_target);
612 gfx::Rect unoccluded_rect_in_target_surface =
613 unoccluded_region_in_target_surface.bounds();
614 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
615 inverse_draw_transform, unoccluded_rect_in_target_surface);
616 unoccluded_rect.Intersect(content_rect);
618 return unoccluded_rect;
621 // Instantiate (and export) templates here for the linker.
622 template class OcclusionTracker<Layer>;
623 template class OcclusionTracker<LayerImpl>;
625 } // namespace cc