Add ICU message format support
[chromium-blink-merge.git] / cc / trees / occlusion_tracker.cc
blob996adc895af5a256792af485a4037e404bb751bd
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/geometry/quad_f.h"
16 #include "ui/gfx/geometry/rect_conversions.h"
18 namespace cc {
20 OcclusionTracker::OcclusionTracker(const gfx::Rect& screen_space_clip_rect)
21 : screen_space_clip_rect_(screen_space_clip_rect) {
24 OcclusionTracker::~OcclusionTracker() {
27 Occlusion OcclusionTracker::GetCurrentOcclusionForLayer(
28 const gfx::Transform& draw_transform) const {
29 DCHECK(!stack_.empty());
30 const StackObject& back = stack_.back();
31 return Occlusion(draw_transform,
32 back.occlusion_from_outside_target,
33 back.occlusion_from_inside_target);
36 Occlusion OcclusionTracker::GetCurrentOcclusionForContributingSurface(
37 const gfx::Transform& draw_transform) const {
38 DCHECK(!stack_.empty());
39 if (stack_.size() < 2)
40 return Occlusion();
41 // A contributing surface doesn't get occluded by things inside its own
42 // surface, so only things outside the surface can occlude it. That occlusion
43 // is found just below the top of the stack (if it exists).
44 const StackObject& second_last = stack_[stack_.size() - 2];
45 return Occlusion(draw_transform, second_last.occlusion_from_outside_target,
46 second_last.occlusion_from_inside_target);
49 void OcclusionTracker::EnterLayer(const LayerIteratorPosition& layer_iterator) {
50 LayerImpl* render_target = layer_iterator.target_render_surface_layer;
52 if (layer_iterator.represents_itself)
53 EnterRenderTarget(render_target);
54 else if (layer_iterator.represents_target_render_surface)
55 FinishedRenderTarget(render_target);
58 void OcclusionTracker::LeaveLayer(const LayerIteratorPosition& layer_iterator) {
59 LayerImpl* render_target = layer_iterator.target_render_surface_layer;
61 if (layer_iterator.represents_itself)
62 MarkOccludedBehindLayer(layer_iterator.current_layer);
63 // TODO(danakj): This should be done when entering the contributing surface,
64 // but in a way that the surface's own occlusion won't occlude itself.
65 else if (layer_iterator.represents_contributing_render_surface)
66 LeaveToRenderTarget(render_target);
69 static gfx::Rect ScreenSpaceClipRectInTargetSurface(
70 const RenderSurfaceImpl* target_surface,
71 const gfx::Rect& screen_space_clip_rect) {
72 gfx::Transform inverse_screen_space_transform(
73 gfx::Transform::kSkipInitialization);
74 if (!target_surface->screen_space_transform().GetInverse(
75 &inverse_screen_space_transform))
76 return target_surface->content_rect();
78 return MathUtil::ProjectEnclosingClippedRect(inverse_screen_space_transform,
79 screen_space_clip_rect);
82 static SimpleEnclosedRegion TransformSurfaceOpaqueRegion(
83 const SimpleEnclosedRegion& region,
84 bool have_clip_rect,
85 const gfx::Rect& clip_rect_in_new_target,
86 const gfx::Transform& transform) {
87 if (region.IsEmpty())
88 return region;
90 // Verify that rects within the |surface| will remain rects in its target
91 // surface after applying |transform|. If this is true, then apply |transform|
92 // to each rect within |region| in order to transform the entire Region.
94 // TODO(danakj): Find a rect interior to each transformed quad.
95 if (!transform.Preserves2dAxisAlignment())
96 return SimpleEnclosedRegion();
98 SimpleEnclosedRegion transformed_region;
99 for (size_t i = 0; i < region.GetRegionComplexity(); ++i) {
100 gfx::Rect transformed_rect =
101 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform,
102 region.GetRect(i));
103 if (have_clip_rect)
104 transformed_rect.Intersect(clip_rect_in_new_target);
105 transformed_region.Union(transformed_rect);
107 return transformed_region;
110 void OcclusionTracker::EnterRenderTarget(const LayerImpl* new_target) {
111 if (!stack_.empty() && stack_.back().target == new_target)
112 return;
114 const LayerImpl* old_target = NULL;
115 const RenderSurfaceImpl* old_occlusion_immune_ancestor = NULL;
116 if (!stack_.empty()) {
117 old_target = stack_.back().target;
118 old_occlusion_immune_ancestor =
119 old_target->render_surface()->nearest_occlusion_immune_ancestor();
121 const RenderSurfaceImpl* new_occlusion_immune_ancestor =
122 new_target->render_surface()->nearest_occlusion_immune_ancestor();
124 stack_.push_back(StackObject(new_target));
126 // We copy the screen occlusion into the new RenderSurfaceImpl subtree, but we
127 // never copy in the occlusion from inside the target, since we are looking
128 // at a new RenderSurfaceImpl target.
130 // If entering an unoccluded subtree, do not carry forward the outside
131 // occlusion calculated so far.
132 bool entering_unoccluded_subtree =
133 new_occlusion_immune_ancestor &&
134 new_occlusion_immune_ancestor != old_occlusion_immune_ancestor;
136 gfx::Transform inverse_new_target_screen_space_transform(
137 // Note carefully, not used if screen space transform is uninvertible.
138 gfx::Transform::kSkipInitialization);
139 bool have_transform_from_screen_to_new_target =
140 new_target->render_surface()->screen_space_transform().GetInverse(
141 &inverse_new_target_screen_space_transform);
143 bool entering_root_target = new_target->parent() == NULL;
145 bool copy_outside_occlusion_forward =
146 stack_.size() > 1 &&
147 !entering_unoccluded_subtree &&
148 have_transform_from_screen_to_new_target &&
149 !entering_root_target;
150 if (!copy_outside_occlusion_forward)
151 return;
153 size_t last_index = stack_.size() - 1;
154 gfx::Transform old_target_to_new_target_transform(
155 inverse_new_target_screen_space_transform,
156 old_target->render_surface()->screen_space_transform());
157 stack_[last_index].occlusion_from_outside_target =
158 TransformSurfaceOpaqueRegion(
159 stack_[last_index - 1].occlusion_from_outside_target, false,
160 gfx::Rect(), old_target_to_new_target_transform);
161 stack_[last_index].occlusion_from_outside_target.Union(
162 TransformSurfaceOpaqueRegion(
163 stack_[last_index - 1].occlusion_from_inside_target, false,
164 gfx::Rect(), old_target_to_new_target_transform));
167 static bool LayerIsHidden(const LayerImpl* layer) {
168 return layer->hide_layer_and_subtree() ||
169 (layer->parent() && LayerIsHidden(layer->parent()));
172 void OcclusionTracker::FinishedRenderTarget(const LayerImpl* finished_target) {
173 // Make sure we know about the target surface.
174 EnterRenderTarget(finished_target);
176 RenderSurfaceImpl* surface = finished_target->render_surface();
178 // Readbacks always happen on render targets so we only need to check
179 // for readbacks here.
180 bool target_is_only_for_copy_request =
181 finished_target->HasCopyRequest() && LayerIsHidden(finished_target);
183 // If the occlusion within the surface can not be applied to things outside of
184 // the surface's subtree, then clear the occlusion here so it won't be used.
185 if (finished_target->mask_layer() || surface->draw_opacity() < 1 ||
186 !finished_target->uses_default_blend_mode() ||
187 target_is_only_for_copy_request ||
188 finished_target->filters().HasFilterThatAffectsOpacity()) {
189 stack_.back().occlusion_from_outside_target.Clear();
190 stack_.back().occlusion_from_inside_target.Clear();
194 static void ReduceOcclusionBelowSurface(
195 const LayerImpl* contributing_layer,
196 const gfx::Rect& surface_rect,
197 const gfx::Transform& surface_transform,
198 const LayerImpl* render_target,
199 SimpleEnclosedRegion* occlusion_from_inside_target) {
200 if (surface_rect.IsEmpty())
201 return;
203 gfx::Rect affected_area_in_target =
204 MathUtil::MapEnclosingClippedRect(surface_transform, surface_rect);
205 if (contributing_layer->render_surface()->is_clipped()) {
206 affected_area_in_target.Intersect(
207 contributing_layer->render_surface()->clip_rect());
209 if (affected_area_in_target.IsEmpty())
210 return;
212 int outset_top, outset_right, outset_bottom, outset_left;
213 contributing_layer->background_filters().GetOutsets(
214 &outset_top, &outset_right, &outset_bottom, &outset_left);
216 // The filter can move pixels from outside of the clip, so allow affected_area
217 // to expand outside the clip.
218 affected_area_in_target.Inset(
219 -outset_left, -outset_top, -outset_right, -outset_bottom);
220 SimpleEnclosedRegion affected_occlusion = *occlusion_from_inside_target;
221 affected_occlusion.Intersect(affected_area_in_target);
223 occlusion_from_inside_target->Subtract(affected_area_in_target);
224 for (size_t i = 0; i < affected_occlusion.GetRegionComplexity(); ++i) {
225 gfx::Rect occlusion_rect = affected_occlusion.GetRect(i);
227 // Shrink the rect by expanding the non-opaque pixels outside the rect.
229 // The left outset of the filters moves pixels on the right side of
230 // the occlusion_rect into it, shrinking its right edge.
231 int shrink_left =
232 occlusion_rect.x() == affected_area_in_target.x() ? 0 : outset_right;
233 int shrink_top =
234 occlusion_rect.y() == affected_area_in_target.y() ? 0 : outset_bottom;
235 int shrink_right =
236 occlusion_rect.right() == affected_area_in_target.right() ?
237 0 : outset_left;
238 int shrink_bottom =
239 occlusion_rect.bottom() == affected_area_in_target.bottom() ?
240 0 : outset_top;
242 occlusion_rect.Inset(shrink_left, shrink_top, shrink_right, shrink_bottom);
244 occlusion_from_inside_target->Union(occlusion_rect);
248 void OcclusionTracker::LeaveToRenderTarget(const LayerImpl* new_target) {
249 DCHECK(!stack_.empty());
250 size_t last_index = stack_.size() - 1;
251 bool surface_will_be_at_top_after_pop =
252 stack_.size() > 1 && stack_[last_index - 1].target == new_target;
254 // We merge the screen occlusion from the current RenderSurfaceImpl subtree
255 // out to its parent target RenderSurfaceImpl. The target occlusion can be
256 // merged out as well but needs to be transformed to the new target.
258 const LayerImpl* old_target = stack_[last_index].target;
259 const RenderSurfaceImpl* old_surface = old_target->render_surface();
261 SimpleEnclosedRegion old_occlusion_from_inside_target_in_new_target =
262 TransformSurfaceOpaqueRegion(
263 stack_[last_index].occlusion_from_inside_target,
264 old_surface->is_clipped(), old_surface->clip_rect(),
265 old_surface->draw_transform());
266 if (old_target->has_replica() && !old_target->replica_has_mask()) {
267 old_occlusion_from_inside_target_in_new_target.Union(
268 TransformSurfaceOpaqueRegion(
269 stack_[last_index].occlusion_from_inside_target,
270 old_surface->is_clipped(), old_surface->clip_rect(),
271 old_surface->replica_draw_transform()));
274 SimpleEnclosedRegion old_occlusion_from_outside_target_in_new_target =
275 TransformSurfaceOpaqueRegion(
276 stack_[last_index].occlusion_from_outside_target, false, gfx::Rect(),
277 old_surface->draw_transform());
279 gfx::Rect unoccluded_surface_rect;
280 gfx::Rect unoccluded_replica_rect;
281 if (old_target->background_filters().HasFilterThatMovesPixels()) {
282 Occlusion surface_occlusion = GetCurrentOcclusionForContributingSurface(
283 old_surface->draw_transform());
284 unoccluded_surface_rect =
285 surface_occlusion.GetUnoccludedContentRect(old_surface->content_rect());
286 if (old_target->has_replica()) {
287 Occlusion replica_occlusion = GetCurrentOcclusionForContributingSurface(
288 old_surface->replica_draw_transform());
289 unoccluded_replica_rect = replica_occlusion.GetUnoccludedContentRect(
290 old_surface->content_rect());
294 if (surface_will_be_at_top_after_pop) {
295 // Merge the top of the stack down.
296 stack_[last_index - 1].occlusion_from_inside_target.Union(
297 old_occlusion_from_inside_target_in_new_target);
298 // TODO(danakj): Strictly this should subtract the inside target occlusion
299 // before union.
300 if (new_target->parent()) {
301 stack_[last_index - 1].occlusion_from_outside_target.Union(
302 old_occlusion_from_outside_target_in_new_target);
304 stack_.pop_back();
305 } else {
306 // Replace the top of the stack with the new pushed surface.
307 stack_.back().target = new_target;
308 stack_.back().occlusion_from_inside_target =
309 old_occlusion_from_inside_target_in_new_target;
310 if (new_target->parent()) {
311 stack_.back().occlusion_from_outside_target =
312 old_occlusion_from_outside_target_in_new_target;
313 } else {
314 stack_.back().occlusion_from_outside_target.Clear();
318 if (!old_target->background_filters().HasFilterThatMovesPixels())
319 return;
321 ReduceOcclusionBelowSurface(old_target,
322 unoccluded_surface_rect,
323 old_surface->draw_transform(),
324 new_target,
325 &stack_.back().occlusion_from_inside_target);
326 ReduceOcclusionBelowSurface(old_target,
327 unoccluded_surface_rect,
328 old_surface->draw_transform(),
329 new_target,
330 &stack_.back().occlusion_from_outside_target);
332 if (!old_target->has_replica())
333 return;
334 ReduceOcclusionBelowSurface(old_target,
335 unoccluded_replica_rect,
336 old_surface->replica_draw_transform(),
337 new_target,
338 &stack_.back().occlusion_from_inside_target);
339 ReduceOcclusionBelowSurface(old_target,
340 unoccluded_replica_rect,
341 old_surface->replica_draw_transform(),
342 new_target,
343 &stack_.back().occlusion_from_outside_target);
346 void OcclusionTracker::MarkOccludedBehindLayer(const LayerImpl* layer) {
347 DCHECK(!stack_.empty());
348 DCHECK_EQ(layer->render_target(), stack_.back().target);
350 if (layer->draw_opacity() < 1)
351 return;
353 if (!layer->uses_default_blend_mode())
354 return;
356 if (layer->Is3dSorted())
357 return;
359 SimpleEnclosedRegion opaque_layer_region = layer->VisibleOpaqueRegion();
360 if (opaque_layer_region.IsEmpty())
361 return;
363 DCHECK(layer->visible_layer_rect().Contains(opaque_layer_region.bounds()));
365 // TODO(danakj): Find a rect interior to each transformed quad.
366 if (!layer->draw_transform().Preserves2dAxisAlignment())
367 return;
369 gfx::Rect clip_rect_in_target = ScreenSpaceClipRectInTargetSurface(
370 layer->render_target()->render_surface(), screen_space_clip_rect_);
371 if (layer->is_clipped()) {
372 clip_rect_in_target.Intersect(layer->clip_rect());
373 } else {
374 clip_rect_in_target.Intersect(
375 layer->render_target()->render_surface()->content_rect());
378 for (size_t i = 0; i < opaque_layer_region.GetRegionComplexity(); ++i) {
379 gfx::Rect transformed_rect =
380 MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
381 layer->draw_transform(), opaque_layer_region.GetRect(i));
382 transformed_rect.Intersect(clip_rect_in_target);
383 if (transformed_rect.width() < minimum_tracking_size_.width() &&
384 transformed_rect.height() < minimum_tracking_size_.height())
385 continue;
386 stack_.back().occlusion_from_inside_target.Union(transformed_rect);
390 Region OcclusionTracker::ComputeVisibleRegionInScreen() const {
391 DCHECK(!stack_.back().target->parent());
392 const SimpleEnclosedRegion& occluded =
393 stack_.back().occlusion_from_inside_target;
394 Region visible_region(screen_space_clip_rect_);
395 for (size_t i = 0; i < occluded.GetRegionComplexity(); ++i)
396 visible_region.Subtract(occluded.GetRect(i));
397 return visible_region;
400 } // namespace cc