OLD | NEW |
| (Empty) |
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/picture_layer_tiling.h" | |
6 | |
7 #include <cmath> | |
8 | |
9 #include "base/debug/trace_event.h" | |
10 #include "cc/base/math_util.h" | |
11 #include "ui/gfx/point_conversions.h" | |
12 #include "ui/gfx/rect_conversions.h" | |
13 #include "ui/gfx/safe_integer_conversions.h" | |
14 #include "ui/gfx/size_conversions.h" | |
15 | |
16 namespace cc { | |
17 | |
18 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create( | |
19 float contents_scale) { | |
20 return make_scoped_ptr(new PictureLayerTiling(contents_scale)); | |
21 } | |
22 | |
23 scoped_ptr<PictureLayerTiling> PictureLayerTiling::Clone() const { | |
24 return make_scoped_ptr(new PictureLayerTiling(*this)); | |
25 } | |
26 | |
27 PictureLayerTiling::PictureLayerTiling(float contents_scale) | |
28 : client_(NULL), | |
29 contents_scale_(contents_scale), | |
30 tiling_data_(gfx::Size(), gfx::Size(), true), | |
31 resolution_(NON_IDEAL_RESOLUTION), | |
32 last_source_frame_number_(0), | |
33 last_impl_frame_time_(0) { | |
34 } | |
35 | |
36 PictureLayerTiling::~PictureLayerTiling() { | |
37 } | |
38 | |
39 void PictureLayerTiling::SetClient(PictureLayerTilingClient* client) { | |
40 client_ = client; | |
41 } | |
42 | |
43 gfx::Rect PictureLayerTiling::ContentRect() const { | |
44 return gfx::Rect(tiling_data_.total_size()); | |
45 } | |
46 | |
47 gfx::SizeF PictureLayerTiling::ContentSizeF() const { | |
48 return gfx::ScaleSize(layer_bounds_, contents_scale_); | |
49 } | |
50 | |
51 Tile* PictureLayerTiling::TileAt(int i, int j) const { | |
52 TileMap::const_iterator iter = tiles_.find(TileMapKey(i, j)); | |
53 if (iter == tiles_.end()) | |
54 return NULL; | |
55 return iter->second.get(); | |
56 } | |
57 | |
58 void PictureLayerTiling::CreateTile(int i, int j) { | |
59 gfx::Rect tile_rect = tiling_data_.TileBoundsWithBorder(i, j); | |
60 tile_rect.set_size(tiling_data_.max_texture_size()); | |
61 TileMapKey key(i, j); | |
62 DCHECK(tiles_.find(key) == tiles_.end()); | |
63 scoped_refptr<Tile> tile = client_->CreateTile(this, tile_rect); | |
64 if (tile) | |
65 tiles_[key] = tile; | |
66 } | |
67 | |
68 Region PictureLayerTiling::OpaqueRegionInContentRect( | |
69 const gfx::Rect& content_rect) const { | |
70 Region opaque_region; | |
71 // TODO(enne): implement me | |
72 return opaque_region; | |
73 } | |
74 | |
75 void PictureLayerTiling::SetLayerBounds(gfx::Size layer_bounds) { | |
76 if (layer_bounds_ == layer_bounds) | |
77 return; | |
78 | |
79 gfx::Size old_layer_bounds = layer_bounds_; | |
80 layer_bounds_ = layer_bounds; | |
81 gfx::Size old_content_bounds = tiling_data_.total_size(); | |
82 gfx::Size content_bounds = | |
83 gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds_, contents_scale_)); | |
84 | |
85 tiling_data_.SetTotalSize(content_bounds); | |
86 if (layer_bounds_.IsEmpty()) { | |
87 tiles_.clear(); | |
88 return; | |
89 } | |
90 | |
91 gfx::Size tile_size = client_->CalculateTileSize( | |
92 tiling_data_.max_texture_size(), | |
93 content_bounds); | |
94 if (tile_size != tiling_data_.max_texture_size()) { | |
95 tiling_data_.SetMaxTextureSize(tile_size); | |
96 tiles_.clear(); | |
97 CreateTilesFromLayerRect(gfx::Rect(layer_bounds_)); | |
98 return; | |
99 } | |
100 | |
101 // Any tiles outside our new bounds are invalid and should be dropped. | |
102 if (old_content_bounds.width() > content_bounds.width() || | |
103 old_content_bounds.height() > content_bounds.height()) { | |
104 int right = | |
105 tiling_data_.TileXIndexFromSrcCoord(content_bounds.width() - 1); | |
106 int bottom = | |
107 tiling_data_.TileYIndexFromSrcCoord(content_bounds.height() - 1); | |
108 | |
109 std::vector<TileMapKey> invalid_tile_keys; | |
110 for (TileMap::const_iterator it = tiles_.begin(); | |
111 it != tiles_.end(); ++it) { | |
112 if (it->first.first > right || it->first.second > bottom) | |
113 invalid_tile_keys.push_back(it->first); | |
114 } | |
115 for (size_t i = 0; i < invalid_tile_keys.size(); ++i) | |
116 tiles_.erase(invalid_tile_keys[i]); | |
117 } | |
118 | |
119 // Create tiles for newly exposed areas. | |
120 Region layer_region((gfx::Rect(layer_bounds_))); | |
121 layer_region.Subtract(gfx::Rect(old_layer_bounds)); | |
122 for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) { | |
123 Invalidate(iter.rect()); | |
124 CreateTilesFromLayerRect(iter.rect()); | |
125 } | |
126 } | |
127 | |
128 void PictureLayerTiling::Invalidate(const Region& layer_invalidation) { | |
129 std::vector<TileMapKey> new_tiles; | |
130 | |
131 for (Region::Iterator region_iter(layer_invalidation); | |
132 region_iter.has_rect(); | |
133 region_iter.next()) { | |
134 | |
135 gfx::Rect layer_invalidation = region_iter.rect(); | |
136 layer_invalidation.Intersect(gfx::Rect(layer_bounds_)); | |
137 gfx::Rect rect = | |
138 gfx::ToEnclosingRect(ScaleRect(layer_invalidation, contents_scale_)); | |
139 | |
140 for (PictureLayerTiling::Iterator tile_iter(this, contents_scale_, rect, | |
141 PictureLayerTiling::LayerDeviceA
lignmentUnknown); | |
142 tile_iter; | |
143 ++tile_iter) { | |
144 TileMapKey key(tile_iter.tile_i_, tile_iter.tile_j_); | |
145 TileMap::iterator found = tiles_.find(key); | |
146 if (found == tiles_.end()) | |
147 continue; | |
148 | |
149 tiles_.erase(found); | |
150 new_tiles.push_back(key); | |
151 } | |
152 } | |
153 | |
154 for (size_t i = 0; i < new_tiles.size(); ++i) | |
155 CreateTile(new_tiles[i].first, new_tiles[i].second); | |
156 } | |
157 | |
158 void PictureLayerTiling::CreateTilesFromLayerRect(gfx::Rect layer_rect) { | |
159 gfx::Rect content_rect = | |
160 gfx::ToEnclosingRect(ScaleRect(layer_rect, contents_scale_)); | |
161 CreateTilesFromContentRect(content_rect); | |
162 } | |
163 | |
164 void PictureLayerTiling::CreateTilesFromContentRect(gfx::Rect content_rect) { | |
165 for (TilingData::Iterator iter(&tiling_data_, content_rect); iter; ++iter) { | |
166 TileMap::iterator found = | |
167 tiles_.find(TileMapKey(iter.index_x(), iter.index_y())); | |
168 // Ignore any tiles that already exist. | |
169 if (found != tiles_.end()) | |
170 continue; | |
171 CreateTile(iter.index_x(), iter.index_y()); | |
172 } | |
173 } | |
174 | |
175 PictureLayerTiling::Iterator::Iterator() | |
176 : tiling_(NULL), | |
177 current_tile_(NULL), | |
178 tile_i_(0), | |
179 tile_j_(0), | |
180 left_(0), | |
181 top_(0), | |
182 right_(-1), | |
183 bottom_(-1) { | |
184 } | |
185 | |
186 PictureLayerTiling::Iterator::Iterator(const PictureLayerTiling* tiling, | |
187 float dest_scale, | |
188 gfx::Rect dest_rect, | |
189 LayerDeviceAlignment layerDeviceAlignment
) | |
190 : tiling_(tiling), | |
191 dest_rect_(dest_rect), | |
192 dest_to_content_scale_(0), | |
193 current_tile_(NULL), | |
194 tile_i_(0), | |
195 tile_j_(0), | |
196 left_(0), | |
197 top_(0), | |
198 right_(-1), | |
199 bottom_(-1) { | |
200 DCHECK(tiling_); | |
201 if (dest_rect_.IsEmpty()) | |
202 return; | |
203 | |
204 dest_to_content_scale_ = tiling_->contents_scale_ / dest_scale; | |
205 // This is the maximum size that the dest rect can be, given the content size. | |
206 gfx::Size dest_content_size = gfx::ToCeiledSize(gfx::ScaleSize( | |
207 tiling_->ContentRect().size(), | |
208 1 / dest_to_content_scale_, | |
209 1 / dest_to_content_scale_)); | |
210 | |
211 gfx::Rect content_rect = | |
212 gfx::ToEnclosingRect(gfx::ScaleRect(dest_rect_, | |
213 dest_to_content_scale_, | |
214 dest_to_content_scale_)); | |
215 // IndexFromSrcCoord clamps to valid tile ranges, so it's necessary to | |
216 // check for non-intersection first. | |
217 content_rect.Intersect(gfx::Rect(tiling_->tiling_data_.total_size())); | |
218 if (content_rect.IsEmpty()) | |
219 return; | |
220 | |
221 left_ = tiling_->tiling_data_.TileXIndexFromSrcCoord(content_rect.x()); | |
222 top_ = tiling_->tiling_data_.TileYIndexFromSrcCoord(content_rect.y()); | |
223 right_ = tiling_->tiling_data_.TileXIndexFromSrcCoord( | |
224 content_rect.right() - 1); | |
225 bottom_ = tiling_->tiling_data_.TileYIndexFromSrcCoord( | |
226 content_rect.bottom() - 1); | |
227 | |
228 tile_i_ = left_ - 1; | |
229 tile_j_ = top_; | |
230 ++(*this); | |
231 } | |
232 | |
233 PictureLayerTiling::Iterator::~Iterator() { | |
234 } | |
235 | |
236 PictureLayerTiling::Iterator& PictureLayerTiling::Iterator::operator++() { | |
237 if (tile_j_ > bottom_) | |
238 return *this; | |
239 | |
240 bool first_time = tile_i_ < left_; | |
241 bool new_row = false; | |
242 tile_i_++; | |
243 if (tile_i_ > right_) { | |
244 tile_i_ = left_; | |
245 tile_j_++; | |
246 new_row = true; | |
247 if (tile_j_ > bottom_) { | |
248 current_tile_ = NULL; | |
249 return *this; | |
250 } | |
251 } | |
252 | |
253 current_tile_ = tiling_->TileAt(tile_i_, tile_j_); | |
254 | |
255 // Calculate the current geometry rect. Due to floating point rounding | |
256 // and ToEnclosingRect, tiles might overlap in destination space on the | |
257 // edges. | |
258 gfx::Rect last_geometry_rect = current_geometry_rect_; | |
259 | |
260 gfx::Rect content_rect = tiling_->tiling_data_.TileBounds(tile_i_, tile_j_); | |
261 | |
262 current_geometry_rect_ = gfx::ToEnclosingRect( | |
263 gfx::ScaleRect(content_rect, 1 / dest_to_content_scale_, | |
264 1 / dest_to_content_scale_)); | |
265 | |
266 current_geometry_rect_.Intersect(dest_rect_); | |
267 | |
268 if (first_time) | |
269 return *this; | |
270 | |
271 // Iteration happens left->right, top->bottom. Running off the bottom-right | |
272 // edge is handled by the intersection above with dest_rect_. Here we make | |
273 // sure that the new current geometry rect doesn't overlap with the last. | |
274 int min_left; | |
275 int min_top; | |
276 if (new_row) { | |
277 min_left = dest_rect_.x(); | |
278 min_top = last_geometry_rect.bottom(); | |
279 } else { | |
280 min_left = last_geometry_rect.right(); | |
281 min_top = last_geometry_rect.y(); | |
282 } | |
283 | |
284 int inset_left = std::max(0, min_left - current_geometry_rect_.x()); | |
285 int inset_top = std::max(0, min_top - current_geometry_rect_.y()); | |
286 current_geometry_rect_.Inset(inset_left, inset_top, 0, 0); | |
287 | |
288 if (!new_row) { | |
289 DCHECK_EQ(last_geometry_rect.right(), current_geometry_rect_.x()); | |
290 DCHECK_EQ(last_geometry_rect.bottom(), current_geometry_rect_.bottom()); | |
291 DCHECK_EQ(last_geometry_rect.y(), current_geometry_rect_.y()); | |
292 } | |
293 | |
294 return *this; | |
295 } | |
296 | |
297 gfx::Rect PictureLayerTiling::Iterator::geometry_rect() const { | |
298 return current_geometry_rect_; | |
299 } | |
300 | |
301 gfx::Rect PictureLayerTiling::Iterator::full_tile_geometry_rect() const { | |
302 gfx::Rect rect = tiling_->tiling_data_.TileBoundsWithBorder(tile_i_, tile_j_); | |
303 rect.set_size(tiling_->tiling_data_.max_texture_size()); | |
304 return rect; | |
305 } | |
306 | |
307 gfx::RectF PictureLayerTiling::Iterator::texture_rect() const { | |
308 gfx::PointF tex_origin = | |
309 tiling_->tiling_data_.TileBoundsWithBorder(tile_i_, tile_j_).origin(); | |
310 | |
311 // Convert from dest space => content space => texture space. | |
312 gfx::RectF texture_rect(current_geometry_rect_); | |
313 texture_rect.Scale(dest_to_content_scale_, | |
314 dest_to_content_scale_); | |
315 texture_rect.Offset(-tex_origin.OffsetFromOrigin()); | |
316 texture_rect.Intersect(tiling_->ContentRect()); | |
317 | |
318 return texture_rect; | |
319 } | |
320 | |
321 gfx::Size PictureLayerTiling::Iterator::texture_size() const { | |
322 return tiling_->tiling_data_.max_texture_size(); | |
323 } | |
324 | |
325 void PictureLayerTiling::UpdateTilePriorities( | |
326 WhichTree tree, | |
327 gfx::Size device_viewport, | |
328 const gfx::RectF& viewport_in_layer_space, | |
329 gfx::Size last_layer_bounds, | |
330 gfx::Size current_layer_bounds, | |
331 float last_layer_contents_scale, | |
332 float current_layer_contents_scale, | |
333 const gfx::Transform& last_screen_transform, | |
334 const gfx::Transform& current_screen_transform, | |
335 int current_source_frame_number, | |
336 double current_frame_time, | |
337 bool store_screen_space_quads_on_tiles) { | |
338 if (ContentRect().IsEmpty()) | |
339 return; | |
340 | |
341 bool first_update_in_new_source_frame = | |
342 current_source_frame_number != last_source_frame_number_; | |
343 | |
344 bool first_update_in_new_impl_frame = | |
345 current_frame_time != last_impl_frame_time_; | |
346 | |
347 // In pending tree, this is always called. We update priorities: | |
348 // - Immediately after a commit (first_update_in_new_source_frame). | |
349 // - On animation ticks after the first frame in the tree | |
350 // (first_update_in_new_impl_frame). | |
351 // In active tree, this is only called during draw. We update priorities: | |
352 // - On draw if properties were not already computed by the pending tree | |
353 // and activated for the frame (first_update_in_new_impl_frame). | |
354 if (!first_update_in_new_impl_frame && !first_update_in_new_source_frame) | |
355 return; | |
356 | |
357 double time_delta = 0; | |
358 if (last_impl_frame_time_ != 0 && last_layer_bounds == current_layer_bounds) | |
359 time_delta = current_frame_time - last_impl_frame_time_; | |
360 | |
361 gfx::Rect viewport_in_content_space = | |
362 gfx::ToEnclosingRect(gfx::ScaleRect(viewport_in_layer_space, | |
363 contents_scale_)); | |
364 | |
365 gfx::Size tile_size = tiling_data_.max_texture_size(); | |
366 int64 prioritized_rect_area = | |
367 TilePriority::kNumTilesToCoverWithInflatedViewportRectForPrioritization * | |
368 tile_size.width() * tile_size.height(); | |
369 | |
370 gfx::Rect prioritized_rect = ExpandRectEquallyToAreaBoundedBy( | |
371 viewport_in_content_space, | |
372 prioritized_rect_area, | |
373 ContentRect()); | |
374 DCHECK(ContentRect().Contains(prioritized_rect)); | |
375 | |
376 // Iterate through all of the tiles that were live last frame but will | |
377 // not be live this frame, and mark them as being dead. | |
378 for (TilingData::DifferenceIterator iter(&tiling_data_, | |
379 last_prioritized_rect_, | |
380 prioritized_rect); | |
381 iter; | |
382 ++iter) { | |
383 TileMap::iterator find = tiles_.find(iter.index()); | |
384 if (find == tiles_.end()) | |
385 continue; | |
386 | |
387 TilePriority priority; | |
388 DCHECK(!priority.is_live); | |
389 Tile* tile = find->second.get(); | |
390 tile->SetPriority(tree, priority); | |
391 } | |
392 last_prioritized_rect_ = prioritized_rect; | |
393 | |
394 gfx::Rect view_rect(device_viewport); | |
395 float current_scale = current_layer_contents_scale / contents_scale_; | |
396 float last_scale = last_layer_contents_scale / contents_scale_; | |
397 | |
398 // Fast path tile priority calculation when both transforms are translations. | |
399 if (last_screen_transform.IsIdentityOrTranslation() && | |
400 current_screen_transform.IsIdentityOrTranslation()) | |
401 { | |
402 gfx::Vector2dF current_offset( | |
403 current_screen_transform.matrix().get(0, 3), | |
404 current_screen_transform.matrix().get(1, 3)); | |
405 gfx::Vector2dF last_offset( | |
406 last_screen_transform.matrix().get(0, 3), | |
407 last_screen_transform.matrix().get(1, 3)); | |
408 | |
409 for (TilingData::Iterator iter(&tiling_data_, prioritized_rect); | |
410 iter; ++iter) { | |
411 TileMap::iterator find = tiles_.find(iter.index()); | |
412 if (find == tiles_.end()) | |
413 continue; | |
414 Tile* tile = find->second.get(); | |
415 | |
416 gfx::Rect tile_bounds = | |
417 tiling_data_.TileBounds(iter.index_x(), iter.index_y()); | |
418 gfx::RectF current_screen_rect = gfx::ScaleRect( | |
419 tile_bounds, | |
420 current_scale, | |
421 current_scale) + current_offset; | |
422 gfx::RectF last_screen_rect = gfx::ScaleRect( | |
423 tile_bounds, | |
424 last_scale, | |
425 last_scale) + last_offset; | |
426 | |
427 float distance_to_visible_in_pixels = | |
428 TilePriority::manhattanDistance(current_screen_rect, view_rect); | |
429 | |
430 float time_to_visible_in_seconds = | |
431 TilePriority::TimeForBoundsToIntersect( | |
432 last_screen_rect, current_screen_rect, time_delta, view_rect); | |
433 TilePriority priority( | |
434 resolution_, | |
435 time_to_visible_in_seconds, | |
436 distance_to_visible_in_pixels); | |
437 if (store_screen_space_quads_on_tiles) | |
438 priority.set_current_screen_quad(gfx::QuadF(current_screen_rect)); | |
439 tile->SetPriority(tree, priority); | |
440 } | |
441 } else { | |
442 for (TilingData::Iterator iter(&tiling_data_, prioritized_rect); | |
443 iter; ++iter) { | |
444 TileMap::iterator find = tiles_.find(iter.index()); | |
445 if (find == tiles_.end()) | |
446 continue; | |
447 Tile* tile = find->second.get(); | |
448 | |
449 gfx::Rect tile_bounds = | |
450 tiling_data_.TileBounds(iter.index_x(), iter.index_y()); | |
451 gfx::RectF current_layer_content_rect = gfx::ScaleRect( | |
452 tile_bounds, | |
453 current_scale, | |
454 current_scale); | |
455 gfx::RectF current_screen_rect = MathUtil::MapClippedRect( | |
456 current_screen_transform, current_layer_content_rect); | |
457 gfx::RectF last_layer_content_rect = gfx::ScaleRect( | |
458 tile_bounds, | |
459 last_scale, | |
460 last_scale); | |
461 gfx::RectF last_screen_rect = MathUtil::MapClippedRect( | |
462 last_screen_transform, last_layer_content_rect); | |
463 | |
464 float distance_to_visible_in_pixels = | |
465 TilePriority::manhattanDistance(current_screen_rect, view_rect); | |
466 | |
467 float time_to_visible_in_seconds = | |
468 TilePriority::TimeForBoundsToIntersect( | |
469 last_screen_rect, current_screen_rect, time_delta, view_rect); | |
470 | |
471 TilePriority priority( | |
472 resolution_, | |
473 time_to_visible_in_seconds, | |
474 distance_to_visible_in_pixels); | |
475 if (store_screen_space_quads_on_tiles) { | |
476 bool clipped; | |
477 priority.set_current_screen_quad( | |
478 MathUtil::MapQuad(current_screen_transform, | |
479 gfx::QuadF(current_layer_content_rect), | |
480 &clipped)); | |
481 } | |
482 tile->SetPriority(tree, priority); | |
483 } | |
484 } | |
485 | |
486 last_source_frame_number_ = current_source_frame_number; | |
487 last_impl_frame_time_ = current_frame_time; | |
488 } | |
489 | |
490 void PictureLayerTiling::DidBecomeActive() { | |
491 for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) { | |
492 it->second->SetPriority(ACTIVE_TREE, it->second->priority(PENDING_TREE)); | |
493 it->second->SetPriority(PENDING_TREE, TilePriority()); | |
494 | |
495 // Tile holds a ref onto a picture pile. If the tile never gets invalidated | |
496 // and recreated, then that picture pile ref could exist indefinitely. To | |
497 // prevent this, ask the client to update the pile to its own ref. This | |
498 // will cause PicturePileImpls and their clones to get deleted once the | |
499 // corresponding PictureLayerImpl and any in flight raster jobs go out of | |
500 // scope. | |
501 client_->UpdatePile(it->second); | |
502 } | |
503 } | |
504 | |
505 scoped_ptr<base::Value> PictureLayerTiling::AsValue() const { | |
506 scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue()); | |
507 state->SetInteger("num_tiles", tiles_.size()); | |
508 state->SetDouble("content_scale", contents_scale_); | |
509 state->Set("content_bounds", | |
510 MathUtil::AsValue(ContentRect().size()).release()); | |
511 return state.PassAs<base::Value>(); | |
512 } | |
513 | |
514 namespace { | |
515 | |
516 int ComputeOffsetToExpand4EdgesEqually(int old_width, | |
517 int old_height, | |
518 int64 target_area) { | |
519 // We need to expand the rect in 4 directions, we can compute the | |
520 // amount to expand along each axis with a quadratic equation: | |
521 // (old_w + add) * (old_h + add) = target_area | |
522 // old_w * old_h + old_w * add + add * old_h + add * add = target_area | |
523 // add^2 + add * (old_w + old_h) - target_area + old_w * old_h = 0 | |
524 // Therefore, we solve the quadratic equation with: | |
525 // a = 1 | |
526 // b = old_w + old_h | |
527 // c = -target_area + old_w * old_h | |
528 int a = 1; | |
529 int64 b = old_width + old_height; | |
530 int64 c = -target_area + old_width * old_height; | |
531 int sqrt_part = std::sqrt(b * b - 4.0 * a * c); | |
532 int add_each_axis = (-b + sqrt_part) / 2 / a; | |
533 return add_each_axis / 2; | |
534 } | |
535 | |
536 int ComputeOffsetToExpand3EdgesEqually(int old_width, | |
537 int old_height, | |
538 int64 target_area, | |
539 bool left_complete, | |
540 bool top_complete, | |
541 bool right_complete, | |
542 bool bottom_complete) { | |
543 // We need to expand the rect in three directions, so we will have to | |
544 // expand along one axis twice as much as the other. Otherwise, this | |
545 // is very similar to the case where we expand in all 4 directions. | |
546 | |
547 if (left_complete || right_complete) { | |
548 // Expanding twice as much vertically as horizontally. | |
549 // (old_w + add) * (old_h + add*2) = target_area | |
550 // old_w * old_h + old_w * add*2 + add * old_h + add * add*2 = target_area | |
551 // (add^2)*2 + add * (old_w*2 + old_h) - target_area + old_w * old_h = 0 | |
552 // Therefore, we solve the quadratic equation with: | |
553 // a = 2 | |
554 // b = old_w*2 + old_h | |
555 // c = -target_area + old_w * old_h | |
556 int a = 2; | |
557 int64 b = old_width * 2 + old_height; | |
558 int64 c = -target_area + old_width * old_height; | |
559 int sqrt_part = std::sqrt(b * b - 4.0 * a * c); | |
560 int add_each_direction = (-b + sqrt_part) / 2 / a; | |
561 return add_each_direction; | |
562 } else { | |
563 // Expanding twice as much horizontally as vertically. | |
564 // (old_w + add*2) * (old_h + add) = target_area | |
565 // old_w * old_h + old_w * add + add*2 * old_h + add*2 * add = target_area | |
566 // (add^2)*2 + add * (old_w + old_h*2) - target_area + old_w * old_h = 0 | |
567 // Therefore, we solve the quadratic equation with: | |
568 // a = 2 | |
569 // b = old_w + old_h*2 | |
570 // c = -target_area + old_w * old_h | |
571 int a = 2; | |
572 int64 b = old_width + old_height * 2; | |
573 int64 c = -target_area + old_width * old_height; | |
574 int sqrt_part = std::sqrt(b * b - 4.0 * a * c); | |
575 int add_each_direction = (-b + sqrt_part) / 2 / a; | |
576 return add_each_direction; | |
577 } | |
578 } | |
579 | |
580 int ComputeOffsetToExpand2EdgesEqually(int old_width, | |
581 int old_height, | |
582 int64 target_area, | |
583 bool left_complete, | |
584 bool top_complete, | |
585 bool right_complete, | |
586 bool bottom_complete) { | |
587 // We need to expand the rect along two directions. If the two directions | |
588 // are opposite from each other then we only need to compute a distance | |
589 // along a single axis. | |
590 if (left_complete && right_complete) { | |
591 // Expanding along the vertical axis only: | |
592 // old_w * (old_h + add) = target_area | |
593 // old_w * old_h + old_w * add = target_area | |
594 // add_vertically = (target_area - old_w * old_h) / old_w | |
595 int add_vertically = target_area / old_width - old_height; | |
596 return add_vertically / 2; | |
597 } else if (top_complete && bottom_complete) { | |
598 // Expanding along the horizontal axis only: | |
599 // (old_w + add) * old_h = target_area | |
600 // old_w * old_h + add * old_h = target_area | |
601 // add_horizontally = (target_area - old_w * old_h) / old_h | |
602 int add_horizontally = target_area / old_height - old_width; | |
603 return add_horizontally / 2; | |
604 } else { | |
605 // If we need to expand along both horizontal and vertical axes, we can use | |
606 // the same result as if we were expanding all four edges. But we apply the | |
607 // offset computed for opposing edges to a single edge. | |
608 int add_each_direction = ComputeOffsetToExpand4EdgesEqually( | |
609 old_width, old_height, target_area); | |
610 return add_each_direction * 2; | |
611 } | |
612 } | |
613 | |
614 int ComputeOffsetToExpand1Edge(int old_width, | |
615 int old_height, | |
616 int64 target_area, | |
617 bool left_complete, | |
618 bool top_complete, | |
619 bool right_complete, | |
620 bool bottom_complete) { | |
621 // We need to expand the rect in a single direction, so we are either | |
622 // moving just a verical edge, or just a horizontal edge. | |
623 if (!top_complete || !bottom_complete) { | |
624 // Moving a vertical edge: | |
625 // old_w * (old_h + add) = target_area | |
626 // old_w * old_h + old_w * add = target_area | |
627 // add_vertically = (target_area - old_w * old_h) / old_w | |
628 int add_vertically = target_area / old_width - old_height; | |
629 return add_vertically; | |
630 } else { | |
631 // Moving a horizontal edge: | |
632 // (old_w + add) * old_h = target_area | |
633 // old_w * old_h + add * old_h = target_area | |
634 // add_horizontally = (target_area - old_w * old_h) / old_h | |
635 int add_horizontally = target_area / old_height - old_width; | |
636 return add_horizontally; | |
637 } | |
638 } | |
639 | |
640 } // namespace | |
641 | |
642 // static | |
643 gfx::Rect PictureLayerTiling::ExpandRectEquallyToAreaBoundedBy( | |
644 gfx::Rect starting_rect, | |
645 int64 target_area, | |
646 gfx::Rect bounding_rect) { | |
647 | |
648 bool left_complete = false; | |
649 bool top_complete = false; | |
650 bool right_complete = false; | |
651 bool bottom_complete = false; | |
652 int num_edges_complete = 0; | |
653 | |
654 gfx::Rect working_rect = starting_rect; | |
655 for (int i = 0; i < 4; ++i) { | |
656 if (num_edges_complete != i) | |
657 continue; | |
658 int offset_for_each_edge = 0; | |
659 switch (num_edges_complete) { | |
660 case 0: | |
661 offset_for_each_edge = ComputeOffsetToExpand4EdgesEqually( | |
662 working_rect.width(), | |
663 working_rect.height(), | |
664 target_area); | |
665 break; | |
666 case 1: | |
667 offset_for_each_edge = ComputeOffsetToExpand3EdgesEqually( | |
668 working_rect.width(), | |
669 working_rect.height(), | |
670 target_area, | |
671 left_complete, | |
672 top_complete, | |
673 right_complete, | |
674 bottom_complete); | |
675 break; | |
676 case 2: | |
677 offset_for_each_edge = ComputeOffsetToExpand2EdgesEqually( | |
678 working_rect.width(), | |
679 working_rect.height(), | |
680 target_area, | |
681 left_complete, | |
682 top_complete, | |
683 right_complete, | |
684 bottom_complete); | |
685 break; | |
686 case 3: | |
687 offset_for_each_edge = ComputeOffsetToExpand1Edge( | |
688 working_rect.width(), | |
689 working_rect.height(), | |
690 target_area, | |
691 left_complete, | |
692 top_complete, | |
693 right_complete, | |
694 bottom_complete); | |
695 } | |
696 | |
697 working_rect.Inset((left_complete ? 0 : -offset_for_each_edge), | |
698 (top_complete ? 0 : -offset_for_each_edge), | |
699 (right_complete ? 0 : -offset_for_each_edge), | |
700 (bottom_complete ? 0 : -offset_for_each_edge)); | |
701 | |
702 if (bounding_rect.Contains(working_rect)) | |
703 return working_rect; | |
704 working_rect.Intersect(bounding_rect); | |
705 | |
706 if (working_rect.x() == bounding_rect.x()) left_complete = true; | |
707 if (working_rect.y() == bounding_rect.y()) top_complete = true; | |
708 if (working_rect.right() == bounding_rect.right()) right_complete = true; | |
709 if (working_rect.bottom() == bounding_rect.bottom()) bottom_complete = true; | |
710 | |
711 num_edges_complete = (left_complete ? 1 : 0) + | |
712 (top_complete ? 1 : 0) + | |
713 (right_complete ? 1 : 0) + | |
714 (bottom_complete ? 1 : 0); | |
715 if (num_edges_complete == 4) | |
716 return working_rect; | |
717 } | |
718 | |
719 NOTREACHED(); | |
720 return starting_rect; | |
721 } | |
722 | |
723 } // namespace cc | |
OLD | NEW |