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
17namespace cc {
18
19template <typename LayerType>
20OcclusionTracker<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
26template <typename LayerType>
27OcclusionTracker<LayerType>::~OcclusionTracker() {}
28
29template <typename LayerType>
30void 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
40template <typename LayerType>
41void 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
53template <typename RenderSurfaceType>
54static 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
67template <typename RenderSurfaceType>
68static 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
101static inline bool LayerOpacityKnown(const Layer* layer) {
102  return !layer->draw_opacity_is_animating();
103}
104static inline bool LayerOpacityKnown(const LayerImpl* layer) {
105  return true;
106}
107static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
108  return !layer->draw_transform_is_animating();
109}
110static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
111  return true;
112}
113
114static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
115  return !rs->draw_opacity_is_animating();
116}
117static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
118  return true;
119}
120static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
121  return !rs->target_surface_transforms_are_animating();
122}
123static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
124  return true;
125}
126static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
127  return !rs->screen_space_transforms_are_animating();
128}
129static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
130  return true;
131}
132
133static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
134  return layer->Is3dSorted();
135}
136static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
137  return false;
138}
139
140template <typename LayerType>
141static inline bool LayerIsHidden(const LayerType* layer) {
142  return layer->hide_layer_and_subtree() ||
143         (layer->parent() && LayerIsHidden(layer->parent()));
144}
145
146template <typename LayerType>
147void 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
213template <typename LayerType>
214void 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
242template <typename LayerType>
243static 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
297template <typename LayerType>
298void 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
398template <typename LayerType>
399void 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
498template <typename LayerType>
499bool 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
543template <typename LayerType>
544gfx::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  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);
575
576  return unoccluded_rect;
577}
578
579template <typename LayerType>
580gfx::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;
585
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;
592
593  const StackObject& second_last = stack_[stack_.size() - 2];
594
595  if (second_last.occlusion_from_inside_target.IsEmpty() &&
596      second_last.occlusion_from_outside_target.IsEmpty())
597    return content_rect;
598
599  gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
600  if (!draw_transform.GetInverse(&inverse_draw_transform))
601    return content_rect;
602
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);
611
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);
617
618  return unoccluded_rect;
619}
620
621// Instantiate (and export) templates here for the linker.
622template class OcclusionTracker<Layer>;
623template class OcclusionTracker<LayerImpl>;
624
625}  // namespace cc
626