Provide core API permissions to extensions_unittests
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blobd68633bdf64932e02cce67fe9ed5c68e515f0c2e
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/base/region.h"
11 #include "cc/layers/layer.h"
12 #include "cc/layers/layer_impl.h"
13 #include "cc/layers/render_surface.h"
14 #include "cc/layers/render_surface_impl.h"
15 #include "ui/gfx/quad_f.h"
16 #include "ui/gfx/rect_conversions.h"
18 namespace cc {
20 template <typename LayerType>
21 OcclusionTracker<LayerType>::OcclusionTracker(
22 const gfx::Rect& screen_space_clip_rect)
23 : screen_space_clip_rect_(screen_space_clip_rect),
24 occluding_screen_space_rects_(NULL),
25 non_occluding_screen_space_rects_(NULL) {}
27 template <typename LayerType>
28 OcclusionTracker<LayerType>::~OcclusionTracker() {}
30 template <typename LayerType>
31 void OcclusionTracker<LayerType>::EnterLayer(
32 const LayerIteratorPosition<LayerType>& layer_iterator) {
33 LayerType* render_target = layer_iterator.target_render_surface_layer;
35 if (layer_iterator.represents_itself)
36 EnterRenderTarget(render_target);
37 else if (layer_iterator.represents_target_render_surface)
38 FinishedRenderTarget(render_target);
41 template <typename LayerType>
42 void OcclusionTracker<LayerType>::LeaveLayer(
43 const LayerIteratorPosition<LayerType>& layer_iterator) {
44 LayerType* render_target = layer_iterator.target_render_surface_layer;
46 if (layer_iterator.represents_itself)
47 MarkOccludedBehindLayer(layer_iterator.current_layer);
48 // TODO(danakj): This should be done when entering the contributing surface,
49 // but in a way that the surface's own occlusion won't occlude itself.
50 else if (layer_iterator.represents_contributing_render_surface)
51 LeaveToRenderTarget(render_target);
54 template <typename RenderSurfaceType>
55 static gfx::Rect ScreenSpaceClipRectInTargetSurface(
56 const RenderSurfaceType* target_surface,
57 const gfx::Rect& screen_space_clip_rect) {
58 gfx::Transform inverse_screen_space_transform(
59 gfx::Transform::kSkipInitialization);
60 if (!target_surface->screen_space_transform().GetInverse(
61 &inverse_screen_space_transform))
62 return target_surface->content_rect();
64 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
65 screen_space_clip_rect);
68 template <typename RenderSurfaceType>
69 static SimpleEnclosedRegion TransformSurfaceOpaqueRegion(
70 const SimpleEnclosedRegion& region,
71 bool have_clip_rect,
72 const gfx::Rect& clip_rect_in_new_target,
73 const gfx::Transform& transform) {
74 if (region.IsEmpty())
75 return region;
77 // Verify that rects within the |surface| will remain rects in its target
78 // surface after applying |transform|. If this is true, then apply |transform|
79 // to each rect within |region| in order to transform the entire Region.
81 // TODO(danakj): Find a rect interior to each transformed quad.
82 if (!transform.Preserves2dAxisAlignment())
83 return SimpleEnclosedRegion();
85 SimpleEnclosedRegion transformed_region;
86 for (size_t i = 0; i < region.GetRegionComplexity(); ++i) {
87 bool clipped;
88 gfx::QuadF transformed_quad =
89 MathUtil::MapQuad(transform, gfx::QuadF(region.GetRect(i)), &clipped);
90 gfx::Rect transformed_rect =
91 gfx::ToEnclosedRect(transformed_quad.BoundingBox());
92 DCHECK(!clipped); // We only map if the transform preserves axis alignment.
93 if (have_clip_rect)
94 transformed_rect.Intersect(clip_rect_in_new_target);
95 transformed_region.Union(transformed_rect);
97 return transformed_region;
100 static inline bool LayerOpacityKnown(const Layer* layer) {
101 return !layer->draw_opacity_is_animating();
103 static inline bool LayerOpacityKnown(const LayerImpl* layer) {
104 return true;
106 static inline bool LayerTransformsToTargetKnown(const Layer* layer) {
107 return !layer->draw_transform_is_animating();
109 static inline bool LayerTransformsToTargetKnown(const LayerImpl* layer) {
110 return true;
113 static inline bool SurfaceOpacityKnown(const RenderSurface* rs) {
114 return !rs->draw_opacity_is_animating();
116 static inline bool SurfaceOpacityKnown(const RenderSurfaceImpl* rs) {
117 return true;
119 static inline bool SurfaceTransformsToTargetKnown(const RenderSurface* rs) {
120 return !rs->target_surface_transforms_are_animating();
122 static inline bool SurfaceTransformsToTargetKnown(const RenderSurfaceImpl* rs) {
123 return true;
125 static inline bool SurfaceTransformsToScreenKnown(const RenderSurface* rs) {
126 return !rs->screen_space_transforms_are_animating();
128 static inline bool SurfaceTransformsToScreenKnown(const RenderSurfaceImpl* rs) {
129 return true;
132 static inline bool LayerIsInUnsorted3dRenderingContext(const Layer* layer) {
133 return layer->Is3dSorted();
135 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
136 return false;
139 template <typename LayerType>
140 static inline bool LayerIsHidden(const LayerType* layer) {
141 return layer->hide_layer_and_subtree() ||
142 (layer->parent() && LayerIsHidden(layer->parent()));
145 template <typename LayerType>
146 void OcclusionTracker<LayerType>::EnterRenderTarget(
147 const LayerType* new_target) {
148 if (!stack_.empty() && stack_.back().target == new_target)
149 return;
151 const LayerType* old_target = NULL;
152 const typename LayerType::RenderSurfaceType* old_occlusion_immune_ancestor =
153 NULL;
154 if (!stack_.empty()) {
155 old_target = stack_.back().target;
156 old_occlusion_immune_ancestor =
157 old_target->render_surface()->nearest_occlusion_immune_ancestor();
159 const typename LayerType::RenderSurfaceType* new_occlusion_immune_ancestor =
160 new_target->render_surface()->nearest_occlusion_immune_ancestor();
162 stack_.push_back(StackObject(new_target));
164 // We copy the screen occlusion into the new RenderSurface subtree, but we
165 // never copy in the occlusion from inside the target, since we are looking
166 // at a new RenderSurface target.
168 // If entering an unoccluded subtree, do not carry forward the outside
169 // occlusion calculated so far.
170 bool entering_unoccluded_subtree =
171 new_occlusion_immune_ancestor &&
172 new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
174 bool have_transform_from_screen_to_new_target = false;
175 gfx::Transform inverse_new_target_screen_space_transform(
176 // Note carefully, not used if screen space transform is uninvertible.
177 gfx::Transform::kSkipInitialization);
178 if (SurfaceTransformsToScreenKnown(new_target->render_surface())) {
179 have_transform_from_screen_to_new_target =
180 new_target->render_surface()->screen_space_transform().GetInverse(
181 &inverse_new_target_screen_space_transform);
184 bool entering_root_target = new_target->parent() == NULL;
186 bool copy_outside_occlusion_forward =
187 stack_.size() > 1 &&
188 !entering_unoccluded_subtree &&
189 have_transform_from_screen_to_new_target &&
190 !entering_root_target;
191 if (!copy_outside_occlusion_forward)
192 return;
194 int last_index = stack_.size() - 1;
195 gfx::Transform old_target_to_new_target_transform(
196 inverse_new_target_screen_space_transform,
197 old_target->render_surface()->screen_space_transform());
198 stack_[last_index].occlusion_from_outside_target =
199 TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
200 stack_[last_index - 1].occlusion_from_outside_target,
201 false,
202 gfx::Rect(),
203 old_target_to_new_target_transform);
204 stack_[last_index].occlusion_from_outside_target.Union(
205 TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
206 stack_[last_index - 1].occlusion_from_inside_target,
207 false,
208 gfx::Rect(),
209 old_target_to_new_target_transform));
212 template <typename LayerType>
213 void OcclusionTracker<LayerType>::FinishedRenderTarget(
214 const LayerType* finished_target) {
215 // Make sure we know about the target surface.
216 EnterRenderTarget(finished_target);
218 typename LayerType::RenderSurfaceType* surface =
219 finished_target->render_surface();
221 // Readbacks always happen on render targets so we only need to check
222 // for readbacks here.
223 bool target_is_only_for_copy_request =
224 finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
226 // If the occlusion within the surface can not be applied to things outside of
227 // the surface's subtree, then clear the occlusion here so it won't be used.
228 if (finished_target->mask_layer() || !SurfaceOpacityKnown(surface) ||
229 surface->draw_opacity() < 1 ||
230 !finished_target->uses_default_blend_mode() ||
231 target_is_only_for_copy_request ||
232 finished_target->filters().HasFilterThatAffectsOpacity()) {
233 stack_.back().occlusion_from_outside_target.Clear();
234 stack_.back().occlusion_from_inside_target.Clear();
235 } else if (!SurfaceTransformsToTargetKnown(surface)) {
236 stack_.back().occlusion_from_inside_target.Clear();
237 stack_.back().occlusion_from_outside_target.Clear();
241 template <typename LayerType>
242 static void ReduceOcclusionBelowSurface(
243 LayerType* contributing_layer,
244 const gfx::Rect& surface_rect,
245 const gfx::Transform& surface_transform,
246 LayerType* render_target,
247 SimpleEnclosedRegion* 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 SimpleEnclosedRegion affected_occlusion = *occlusion_from_inside_target;
269 affected_occlusion.Intersect(affected_area_in_target);
271 occlusion_from_inside_target->Subtract(affected_area_in_target);
272 for (size_t i = 0; i < affected_occlusion.GetRegionComplexity(); ++i) {
273 gfx::Rect occlusion_rect = affected_occlusion.GetRect(i);
275 // Shrink the rect by expanding the non-opaque pixels outside the rect.
277 // The left outset of the filters moves pixels on the right side of
278 // the occlusion_rect into it, shrinking its right edge.
279 int shrink_left =
280 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
281 int shrink_top =
282 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
283 int shrink_right =
284 occlusion_rect.right() == affected_area_in_target.right() ?
285 0 : outset_left;
286 int shrink_bottom =
287 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
288 0 : outset_top;
290 occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
292 occlusion_from_inside_target->Union(occlusion_rect);
296 template <typename LayerType>
297 void OcclusionTracker<LayerType>::LeaveToRenderTarget(
298 const LayerType* new_target) {
299 int last_index = stack_.size() - 1;
300 bool surface_will_be_at_top_after_pop =
301 stack_.size() > 1 && stack_[last_index - 1].target == new_target;
303 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
304 // out to its parent target RenderSurfaceImpl. The target occlusion can be
305 // merged out as well but needs to be transformed to the new target.
307 const LayerType* old_target = stack_[last_index].target;
308 const typename LayerType::RenderSurfaceType* old_surface =
309 old_target->render_surface();
311 SimpleEnclosedRegion old_occlusion_from_inside_target_in_new_target =
312 TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
313 stack_[last_index].occlusion_from_inside_target,
314 old_surface->is_clipped(),
315 old_surface->clip_rect(),
316 old_surface->draw_transform());
317 if (old_target->has_replica() && !old_target->replica_has_mask()) {
318 old_occlusion_from_inside_target_in_new_target.Union(
319 TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
320 stack_[last_index].occlusion_from_inside_target,
321 old_surface->is_clipped(),
322 old_surface->clip_rect(),
323 old_surface->replica_draw_transform()));
326 SimpleEnclosedRegion old_occlusion_from_outside_target_in_new_target =
327 TransformSurfaceOpaqueRegion<typename LayerType::RenderSurfaceType>(
328 stack_[last_index].occlusion_from_outside_target,
329 false,
330 gfx::Rect(),
331 old_surface->draw_transform());
333 gfx::Rect unoccluded_surface_rect;
334 gfx::Rect unoccluded_replica_rect;
335 if (old_target->background_filters().HasFilterThatMovesPixels()) {
336 unoccluded_surface_rect = UnoccludedContributingSurfaceContentRect(
337 old_surface->content_rect(), old_surface->draw_transform());
338 if (old_target->has_replica()) {
339 unoccluded_replica_rect = UnoccludedContributingSurfaceContentRect(
340 old_surface->content_rect(),
341 old_surface->replica_draw_transform());
345 if (surface_will_be_at_top_after_pop) {
346 // Merge the top of the stack down.
347 stack_[last_index - 1].occlusion_from_inside_target.Union(
348 old_occlusion_from_inside_target_in_new_target);
349 // TODO(danakj): Strictly this should subtract the inside target occlusion
350 // before union.
351 if (new_target->parent()) {
352 stack_[last_index - 1].occlusion_from_outside_target.Union(
353 old_occlusion_from_outside_target_in_new_target);
355 stack_.pop_back();
356 } else {
357 // Replace the top of the stack with the new pushed surface.
358 stack_.back().target = new_target;
359 stack_.back().occlusion_from_inside_target =
360 old_occlusion_from_inside_target_in_new_target;
361 if (new_target->parent()) {
362 stack_.back().occlusion_from_outside_target =
363 old_occlusion_from_outside_target_in_new_target;
364 } else {
365 stack_.back().occlusion_from_outside_target.Clear();
369 if (!old_target->background_filters().HasFilterThatMovesPixels())
370 return;
372 ReduceOcclusionBelowSurface(old_target,
373 unoccluded_surface_rect,
374 old_surface->draw_transform(),
375 new_target,
376 &stack_.back().occlusion_from_inside_target);
377 ReduceOcclusionBelowSurface(old_target,
378 unoccluded_surface_rect,
379 old_surface->draw_transform(),
380 new_target,
381 &stack_.back().occlusion_from_outside_target);
383 if (!old_target->has_replica())
384 return;
385 ReduceOcclusionBelowSurface(old_target,
386 unoccluded_replica_rect,
387 old_surface->replica_draw_transform(),
388 new_target,
389 &stack_.back().occlusion_from_inside_target);
390 ReduceOcclusionBelowSurface(old_target,
391 unoccluded_replica_rect,
392 old_surface->replica_draw_transform(),
393 new_target,
394 &stack_.back().occlusion_from_outside_target);
397 template <typename LayerType>
398 void OcclusionTracker<LayerType>::MarkOccludedBehindLayer(
399 const LayerType* layer) {
400 DCHECK(!stack_.empty());
401 DCHECK_EQ(layer->render_target(), stack_.back().target);
403 if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1)
404 return;
406 if (!layer->uses_default_blend_mode())
407 return;
409 if (LayerIsInUnsorted3dRenderingContext(layer))
410 return;
412 if (!LayerTransformsToTargetKnown(layer))
413 return;
415 SimpleEnclosedRegion opaque_contents = layer->VisibleContentOpaqueRegion();
416 if (opaque_contents.IsEmpty())
417 return;
419 DCHECK(layer->visible_content_rect().Contains(opaque_contents.bounds()));
421 // TODO(danakj): Find a rect interior to each transformed quad.
422 if (!layer->draw_transform().Preserves2dAxisAlignment())
423 return;
425 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
426 layer->render_target()->render_surface(), screen_space_clip_rect_);
427 if (layer->is_clipped()) {
428 clip_rect_in_target.Intersect(layer->clip_rect());
429 } else {
430 clip_rect_in_target.Intersect(
431 layer->render_target()->render_surface()->content_rect());
434 for (size_t i = 0; i < opaque_contents.GetRegionComplexity(); ++i) {
435 bool clipped;
436 gfx::QuadF transformed_quad =
437 MathUtil::MapQuad(layer->draw_transform(),
438 gfx::QuadF(opaque_contents.GetRect(i)),
439 &clipped);
440 gfx::Rect transformed_rect =
441 gfx::ToEnclosedRect(transformed_quad.BoundingBox());
442 DCHECK(!clipped); // We only map if the transform preserves axis alignment.
443 transformed_rect.Intersect(clip_rect_in_target);
444 if (transformed_rect.width() < minimum_tracking_size_.width() &&
445 transformed_rect.height() < minimum_tracking_size_.height())
446 continue;
447 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
449 if (!occluding_screen_space_rects_)
450 continue;
452 // Save the occluding area in screen space for debug visualization.
453 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
454 layer->render_target()->render_surface()->screen_space_transform(),
455 gfx::QuadF(transformed_rect), &clipped);
456 // TODO(danakj): Store the quad in the debug info instead of the bounding
457 // box.
458 gfx::Rect screen_space_rect =
459 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
460 occluding_screen_space_rects_->push_back(screen_space_rect);
463 if (!non_occluding_screen_space_rects_)
464 return;
466 Region non_opaque_contents(gfx::Rect(layer->content_bounds()));
467 non_opaque_contents.Subtract(opaque_contents);
469 for (Region::Iterator non_opaque_content_rects(non_opaque_contents);
470 non_opaque_content_rects.has_rect();
471 non_opaque_content_rects.next()) {
472 // We've already checked for clipping in the MapQuad call above, these calls
473 // should not clip anything further.
474 gfx::Rect transformed_rect = gfx::ToEnclosedRect(
475 MathUtil::MapClippedRect(layer->draw_transform(),
476 gfx::RectF(non_opaque_content_rects.rect())));
477 transformed_rect.Intersect(clip_rect_in_target);
478 if (transformed_rect.IsEmpty())
479 continue;
481 bool clipped;
482 gfx::QuadF screen_space_quad = MathUtil::MapQuad(
483 layer->render_target()->render_surface()->screen_space_transform(),
484 gfx::QuadF(transformed_rect),
485 &clipped);
486 // TODO(danakj): Store the quad in the debug info instead of the bounding
487 // box.
488 gfx::Rect screen_space_rect =
489 gfx::ToEnclosedRect(screen_space_quad.BoundingBox());
490 non_occluding_screen_space_rects_->push_back(screen_space_rect);
494 template <typename LayerType>
495 bool OcclusionTracker<LayerType>::Occluded(
496 const LayerType* render_target,
497 const gfx::Rect& content_rect,
498 const gfx::Transform& draw_transform) const {
499 DCHECK(!stack_.empty());
500 if (stack_.empty())
501 return false;
502 if (content_rect.IsEmpty())
503 return true;
505 // For tests with no render target.
506 if (!render_target)
507 return false;
509 DCHECK_EQ(render_target->render_target(), render_target);
510 DCHECK(render_target->render_surface());
511 DCHECK_EQ(render_target, stack_.back().target);
513 const StackObject& back = stack_.back();
514 if (back.occlusion_from_inside_target.IsEmpty() &&
515 back.occlusion_from_outside_target.IsEmpty()) {
516 return false;
519 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
520 // partial pixels in the resulting Rect.
521 gfx::Rect unoccluded_rect_in_target_surface =
522 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
523 DCHECK_LE(back.occlusion_from_inside_target.GetRegionComplexity(), 1u);
524 DCHECK_LE(back.occlusion_from_outside_target.GetRegionComplexity(), 1u);
525 // These subtract operations are more lossy than if we did both operations at
526 // once.
527 unoccluded_rect_in_target_surface.Subtract(
528 stack_.back().occlusion_from_inside_target.bounds());
529 unoccluded_rect_in_target_surface.Subtract(
530 stack_.back().occlusion_from_outside_target.bounds());
532 return unoccluded_rect_in_target_surface.IsEmpty();
535 template <typename LayerType>
536 gfx::Rect OcclusionTracker<LayerType>::UnoccludedContentRect(
537 const gfx::Rect& content_rect,
538 const gfx::Transform& draw_transform) const {
539 DCHECK(!stack_.empty());
540 if (content_rect.IsEmpty())
541 return content_rect;
543 const StackObject& back = stack_.back();
544 if (back.occlusion_from_inside_target.IsEmpty() &&
545 back.occlusion_from_outside_target.IsEmpty()) {
546 return content_rect;
549 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
550 bool ok = draw_transform.GetInverse(&inverse_draw_transform);
551 DCHECK(ok);
553 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
554 // partial pixels in the resulting Rect.
555 gfx::Rect unoccluded_rect_in_target_surface =
556 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
557 DCHECK_LE(back.occlusion_from_inside_target.GetRegionComplexity(), 1u);
558 DCHECK_LE(back.occlusion_from_outside_target.GetRegionComplexity(), 1u);
559 // These subtract operations are more lossy than if we did both operations at
560 // once.
561 unoccluded_rect_in_target_surface.Subtract(
562 back.occlusion_from_inside_target.bounds());
563 unoccluded_rect_in_target_surface.Subtract(
564 back.occlusion_from_outside_target.bounds());
566 if (unoccluded_rect_in_target_surface.IsEmpty())
567 return gfx::Rect();
569 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
570 inverse_draw_transform, unoccluded_rect_in_target_surface);
571 unoccluded_rect.Intersect(content_rect);
573 return unoccluded_rect;
576 template <typename LayerType>
577 gfx::Rect OcclusionTracker<LayerType>::UnoccludedContributingSurfaceContentRect(
578 const gfx::Rect& content_rect,
579 const gfx::Transform& draw_transform) const {
580 if (content_rect.IsEmpty())
581 return content_rect;
583 // A contributing surface doesn't get occluded by things inside its own
584 // surface, so only things outside the surface can occlude it. That occlusion
585 // is found just below the top of the stack (if it exists).
586 bool has_occlusion = stack_.size() > 1;
587 if (!has_occlusion)
588 return content_rect;
590 const StackObject& second_last = stack_[stack_.size() - 2];
591 if (second_last.occlusion_from_inside_target.IsEmpty() &&
592 second_last.occlusion_from_outside_target.IsEmpty())
593 return content_rect;
595 gfx::Transform inverse_draw_transform(gfx::Transform::kSkipInitialization);
596 bool ok = draw_transform.GetInverse(&inverse_draw_transform);
597 DCHECK(ok);
599 // Take the ToEnclosingRect at each step, as we want to contain any unoccluded
600 // partial pixels in the resulting Rect.
601 gfx::Rect unoccluded_rect_in_target_surface =
602 MathUtil::MapEnclosingClippedRect(draw_transform, content_rect);
603 DCHECK_LE(second_last.occlusion_from_inside_target.GetRegionComplexity(), 1u);
604 DCHECK_LE(second_last.occlusion_from_outside_target.GetRegionComplexity(),
605 1u);
606 // These subtract operations are more lossy than if we did both operations at
607 // once.
608 unoccluded_rect_in_target_surface.Subtract(
609 second_last.occlusion_from_inside_target.bounds());
610 unoccluded_rect_in_target_surface.Subtract(
611 second_last.occlusion_from_outside_target.bounds());
613 if (unoccluded_rect_in_target_surface.IsEmpty())
614 return gfx::Rect();
616 gfx::Rect unoccluded_rect = MathUtil::ProjectEnclosingClippedRect(
617 inverse_draw_transform, unoccluded_rect_in_target_surface);
618 unoccluded_rect.Intersect(content_rect);
620 return unoccluded_rect;
623 template <typename LayerType>
624 Region OcclusionTracker<LayerType>::ComputeVisibleRegionInScreen() const {
625 DCHECK(!stack_.back().target->parent());
626 const SimpleEnclosedRegion& occluded =
627 stack_.back().occlusion_from_inside_target;
628 Region visible_region(screen_space_clip_rect_);
629 for (size_t i = 0; i < occluded.GetRegionComplexity(); ++i)
630 visible_region.Subtract(occluded.GetRect(i));
631 return visible_region;
634 // Instantiate (and export) templates here for the linker.
635 template class OcclusionTracker<Layer>;
636 template class OcclusionTracker<LayerImpl>;
638 } // namespace cc