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