Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / cc / trees / occlusion_tracker.cc
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.
4
5 #include "cc/trees/occlusion_tracker.h"
6
7 #include <algorithm>
8
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"
16
17 namespace cc {
18
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) {}
25
26 template <typename LayerType>
27 OcclusionTracker<LayerType>::~OcclusionTracker() {}
28
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;
33
34   if (layer_iterator.represents_itself)
35     EnterRenderTarget(render_target);
36   else if (layer_iterator.represents_target_render_surface)
37     FinishedRenderTarget(render_target);
38 }
39
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;
44
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);
51 }
52
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();
62
63   return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
64                                                screen_space_clip_rect);
65 }
66
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();
75
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.
79
80   // TODO(danakj): Find a rect interior to each transformed quad.
81   if (!transform.Preserves2dAxisAlignment())
82     return Region();
83
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);
97   }
98   return transformed_region;
99 }
100
101 static inline bool LayerOpacityKnown(const Layer* layer) {
102   return !layer->draw_opacity_is_animating();
103 }
104 static inline bool LayerOpacityKnown(const LayerImpl* layer) {
105   return true;
106 }
107 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
108   return !layer->draw_transform_is_animating();
109 }
110 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
111   return true;
112 }
113
114 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
115   return !rs->draw_opacity_is_animating();
116 }
117 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
118   return true;
119 }
120 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
121   return !rs->target_surface_transforms_are_animating();
122 }
123 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
124   return true;
125 }
126 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
127   return !rs->screen_space_transforms_are_animating();
128 }
129 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
130   return true;
131 }
132
133 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
134   return layer->Is3dSorted();
135 }
136 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
137   return false;
138 }
139
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()));
144 }
145
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;
151
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();
159   }
160   const typename LayerType::RenderSurfaceType* new_occlusion_immune_ancestor =
161       new_target->render_surface()->nearest_occlusion_immune_ancestor();
162
163   stack_.push_back(StackObject(new_target));
164
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.
168
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;
174
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);
183   }
184
185   bool entering_root_target = new_target->parent() == NULL;
186
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;
194
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));
211 }
212
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);
218
219   typename LayerType::RenderSurfaceType* surface =
220       finished_target->render_surface();
221
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);
226
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();
239   }
240 }
241
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;
250
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());
256   }
257   if (affected_area_in_target.IsEmpty())
258     return;
259
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);
263
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);
271
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();
275
276     // Shrink the rect by expanding the non-opaque pixels outside the rect.
277
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;
290
291     occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
292
293     occlusion_from_inside_target->Union(occlusion_rect);
294   }
295 }
296
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;
303
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.
307
308   const LayerType* old_target = stack_[last_index].target;
309   const typename LayerType::RenderSurfaceType* old_surface =
310       old_target->render_surface();
311
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()));
325   }
326
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());
333
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());
343     }
344   }
345
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);
355     }
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();
367     }
368   }
369
370   if (!old_target->background_filters().HasFilterThatMovesPixels())
371     return;
372
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);
383
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);
396 }
397
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;
405
406   if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
407     return;
408
409   if (!layer->uses_default_blend_mode())
410     return;
411
412   if (LayerIsInUnsorted3dRenderingContext(layer))
413     return;
414
415   if (!LayerTransformsToTargetKnown(layer))
416     return;
417
418   Region opaque_contents = layer->VisibleContentOpaqueRegion();
419   if (opaque_contents.IsEmpty())
420     return;
421
422   DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
423
424   // TODO(danakj): Find a rect interior to each transformed quad.
425   if (!layer->draw_transform().Preserves2dAxisAlignment())
426     return;
427
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());
435   }
436
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);
453
454     if (!occluding_screen_space_rects_)
455       continue;
456
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);
466   }
467
468   if (!non_occluding_screen_space_rects_)
469     return;
470
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;
484
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);
495   }
496 }
497
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;
508
509   // For tests with no render target.
510   if (!render_target)
511     return false;
512
513   DCHECK_EQ(render_target->render_target(), render_target);
514   DCHECK(render_target->render_surface());
515   DCHECK_EQ(render_target, stack_.back().target);
516
517   if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
518       stack_.back().occlusion_from_outside_target.IsEmpty()) {
519     return false;
520   }
521
522   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
523   if (!draw_transform.GetInverse(&inverse_draw_transform))
524     return false;
525
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);
536
537   gfx::RectF unoccluded_rect_in_target_surface =
538       unoccluded_region_in_target_surface.bounds();
539
540   return unoccluded_rect_in_target_surface.IsEmpty();
541 }
542
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;
551
552   if (stack_.back().occlusion_from_inside_target.IsEmpty() &&
553       stack_.back().occlusion_from_outside_target.IsEmpty()) {
554     return content_rect;
555   }
556
557   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
558   if (!draw_transform.GetInverse(&inverse_draw_transform))
559     return content_rect;
560
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);
569
570   if (unoccluded_region_in_target_surface.IsEmpty())
571     return gfx::Rect();
572
573   gfx::Rect unoccluded_rect_in_target_surface =
574       unoccluded_region_in_target_surface.bounds();
575   gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
576       inverse_draw_transform, unoccluded_rect_in_target_surface);
577   unoccluded_rect.Intersect(content_rect);
578
579   return unoccluded_rect;
580 }
581
582 template <typename LayerType>
583 gfx::Rect OcclusionTracker<LayerType>::UnoccludedContributingSurfaceContentRect(
584     const gfx::Rect& content_rect,
585     const gfx::Transform& draw_transform) const {
586   if (content_rect.IsEmpty())
587     return content_rect;
588
589   // A contributing surface doesn't get occluded by things inside its own
590   // surface, so only things outside the surface can occlude it. That occlusion
591   // is found just below the top of the stack (if it exists).
592   bool has_occlusion = stack_.size() > 1;
593   if (!has_occlusion)
594     return content_rect;
595
596   const StackObject& second_last = stack_[stack_.size() - 2];
597
598   if (second_last.occlusion_from_inside_target.IsEmpty() &&
599       second_last.occlusion_from_outside_target.IsEmpty())
600     return content_rect;
601
602   gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
603   if (!draw_transform.GetInverse(&inverse_draw_transform))
604     return content_rect;
605
606   // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
607   // partial pixels in the resulting Rect.
608   Region unoccluded_region_in_target_surface =
609       MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
610   unoccluded_region_in_target_surface.Subtract(
611       second_last.occlusion_from_inside_target);
612   unoccluded_region_in_target_surface.Subtract(
613       second_last.occlusion_from_outside_target);
614
615   if (unoccluded_region_in_target_surface.IsEmpty())
616     return gfx::Rect();
617
618   gfx::Rect unoccluded_rect_in_target_surface =
619       unoccluded_region_in_target_surface.bounds();
620   gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
621       inverse_draw_transform, unoccluded_rect_in_target_surface);
622   unoccluded_rect.Intersect(content_rect);
623
624   return unoccluded_rect;
625 }
626
627 // Instantiate (and export) templates here for the linker.
628 template class OcclusionTracker<Layer>;
629 template class OcclusionTracker<LayerImpl>;
630
631 }  // namespace cc