1 // Copyright 2010 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/base/tiling_data.h"
9 #include "ui/gfx/rect.h"
10 #include "ui/gfx/vector2d.h"
14 static int ComputeNumTiles(int max_texture_size
,
17 if (max_texture_size
- 2 * border_texels
<= 0)
18 return total_size
> 0 && max_texture_size
>= total_size
? 1 : 0;
20 int num_tiles
= std::max(1,
21 1 + (total_size
- 1 - 2 * border_texels
) /
22 (max_texture_size
- 2 * border_texels
));
23 return total_size
> 0 ? num_tiles
: 0;
26 TilingData::TilingData()
31 TilingData::TilingData(const gfx::Size
& max_texture_size
,
32 const gfx::Rect
& tiling_rect
,
33 bool has_border_texels
)
34 : max_texture_size_(max_texture_size
),
35 tiling_rect_(tiling_rect
),
36 border_texels_(has_border_texels
? 1 : 0) {
40 TilingData::TilingData(const gfx::Size
& max_texture_size
,
41 const gfx::Rect
& tiling_rect
,
43 : max_texture_size_(max_texture_size
),
44 tiling_rect_(tiling_rect
),
45 border_texels_(border_texels
) {
49 void TilingData::SetTilingRect(const gfx::Rect
& tiling_rect
) {
50 tiling_rect_
= tiling_rect
;
54 void TilingData::SetMaxTextureSize(const gfx::Size
& max_texture_size
) {
55 max_texture_size_
= max_texture_size
;
59 void TilingData::SetHasBorderTexels(bool has_border_texels
) {
60 border_texels_
= has_border_texels
? 1 : 0;
64 void TilingData::SetBorderTexels(int border_texels
) {
65 border_texels_
= border_texels
;
69 int TilingData::TileXIndexFromSrcCoord(int src_position
) const {
70 if (num_tiles_x_
<= 1)
73 src_position
-= tiling_rect_
.x();
75 DCHECK_GT(max_texture_size_
.width() - 2 * border_texels_
, 0);
76 int x
= (src_position
- border_texels_
) /
77 (max_texture_size_
.width() - 2 * border_texels_
);
78 return std::min(std::max(x
, 0), num_tiles_x_
- 1);
81 int TilingData::TileYIndexFromSrcCoord(int src_position
) const {
82 if (num_tiles_y_
<= 1)
85 src_position
-= tiling_rect_
.y();
87 DCHECK_GT(max_texture_size_
.height() - 2 * border_texels_
, 0);
88 int y
= (src_position
- border_texels_
) /
89 (max_texture_size_
.height() - 2 * border_texels_
);
90 return std::min(std::max(y
, 0), num_tiles_y_
- 1);
93 int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position
) const {
94 if (num_tiles_x_
<= 1)
97 src_position
-= tiling_rect_
.x();
99 DCHECK_GT(max_texture_size_
.width() - 2 * border_texels_
, 0);
100 int inner_tile_size
= max_texture_size_
.width() - 2 * border_texels_
;
101 int x
= (src_position
- 2 * border_texels_
) / inner_tile_size
;
102 return std::min(std::max(x
, 0), num_tiles_x_
- 1);
105 int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position
) const {
106 if (num_tiles_y_
<= 1)
109 src_position
-= tiling_rect_
.y();
111 DCHECK_GT(max_texture_size_
.height() - 2 * border_texels_
, 0);
112 int inner_tile_size
= max_texture_size_
.height() - 2 * border_texels_
;
113 int y
= (src_position
- 2 * border_texels_
) / inner_tile_size
;
114 return std::min(std::max(y
, 0), num_tiles_y_
- 1);
117 int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position
) const {
118 if (num_tiles_x_
<= 1)
121 src_position
-= tiling_rect_
.x();
123 DCHECK_GT(max_texture_size_
.width() - 2 * border_texels_
, 0);
124 int inner_tile_size
= max_texture_size_
.width() - 2 * border_texels_
;
125 int x
= src_position
/ inner_tile_size
;
126 return std::min(std::max(x
, 0), num_tiles_x_
- 1);
129 int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position
) const {
130 if (num_tiles_y_
<= 1)
133 src_position
-= tiling_rect_
.y();
135 DCHECK_GT(max_texture_size_
.height() - 2 * border_texels_
, 0);
136 int inner_tile_size
= max_texture_size_
.height() - 2 * border_texels_
;
137 int y
= src_position
/ inner_tile_size
;
138 return std::min(std::max(y
, 0), num_tiles_y_
- 1);
141 gfx::Rect
TilingData::ExpandRectToTileBoundsWithBorders(
142 const gfx::Rect
& rect
) const {
143 if (!rect
.Intersects(tiling_rect_
) || has_empty_bounds())
145 int index_x
= FirstBorderTileXIndexFromSrcCoord(rect
.x());
146 int index_y
= FirstBorderTileYIndexFromSrcCoord(rect
.y());
147 int index_right
= LastBorderTileXIndexFromSrcCoord(rect
.right() - 1);
148 int index_bottom
= LastBorderTileYIndexFromSrcCoord(rect
.bottom() - 1);
150 gfx::Rect
rect_top_left(TileBoundsWithBorder(index_x
, index_y
));
151 gfx::Rect
rect_bottom_right(TileBoundsWithBorder(index_right
, index_bottom
));
153 return gfx::UnionRects(rect_top_left
, rect_bottom_right
);
156 gfx::Rect
TilingData::ExpandRectToTileBounds(const gfx::Rect
& rect
) const {
157 if (!rect
.Intersects(tiling_rect_
) || has_empty_bounds())
159 int index_x
= FirstBorderTileXIndexFromSrcCoord(rect
.x());
160 int index_y
= FirstBorderTileYIndexFromSrcCoord(rect
.y());
161 int index_right
= LastBorderTileXIndexFromSrcCoord(rect
.right() - 1);
162 int index_bottom
= LastBorderTileYIndexFromSrcCoord(rect
.bottom() - 1);
164 gfx::Rect
rect_top_left(TileBounds(index_x
, index_y
));
165 gfx::Rect
rect_bottom_right(TileBounds(index_right
, index_bottom
));
167 return gfx::UnionRects(rect_top_left
, rect_bottom_right
);
170 gfx::Rect
TilingData::TileBounds(int i
, int j
) const {
172 int max_texture_size_x
= max_texture_size_
.width() - 2 * border_texels_
;
173 int max_texture_size_y
= max_texture_size_
.height() - 2 * border_texels_
;
175 int lo_x
= tiling_rect_
.x() + max_texture_size_x
* i
;
177 lo_x
+= border_texels_
;
179 int lo_y
= tiling_rect_
.y() + max_texture_size_y
* j
;
181 lo_y
+= border_texels_
;
183 int hi_x
= tiling_rect_
.x() + max_texture_size_x
* (i
+ 1) + border_texels_
;
184 if (i
+ 1 == num_tiles_x_
)
185 hi_x
+= border_texels_
;
187 int hi_y
= tiling_rect_
.y() + max_texture_size_y
* (j
+ 1) + border_texels_
;
188 if (j
+ 1 == num_tiles_y_
)
189 hi_y
+= border_texels_
;
191 hi_x
= std::min(hi_x
, tiling_rect_
.right());
192 hi_y
= std::min(hi_y
, tiling_rect_
.bottom());
196 int width
= hi_x
- lo_x
;
197 int height
= hi_y
- lo_y
;
198 DCHECK_GE(x
, tiling_rect_
.x());
199 DCHECK_GE(y
, tiling_rect_
.y());
201 DCHECK_GE(height
, 0);
202 DCHECK_LE(x
, tiling_rect_
.right());
203 DCHECK_LE(y
, tiling_rect_
.bottom());
204 return gfx::Rect(x
, y
, width
, height
);
207 gfx::Rect
TilingData::TileBoundsWithBorder(int i
, int j
) const {
209 int max_texture_size_x
= max_texture_size_
.width() - 2 * border_texels_
;
210 int max_texture_size_y
= max_texture_size_
.height() - 2 * border_texels_
;
212 int lo_x
= tiling_rect_
.x() + max_texture_size_x
* i
;
213 int lo_y
= tiling_rect_
.y() + max_texture_size_y
* j
;
215 int hi_x
= lo_x
+ max_texture_size_x
+ 2 * border_texels_
;
216 int hi_y
= lo_y
+ max_texture_size_y
+ 2 * border_texels_
;
218 hi_x
= std::min(hi_x
, tiling_rect_
.right());
219 hi_y
= std::min(hi_y
, tiling_rect_
.bottom());
223 int width
= hi_x
- lo_x
;
224 int height
= hi_y
- lo_y
;
225 DCHECK_GE(x
, tiling_rect_
.x());
226 DCHECK_GE(y
, tiling_rect_
.y());
228 DCHECK_GE(height
, 0);
229 DCHECK_LE(x
, tiling_rect_
.right());
230 DCHECK_LE(y
, tiling_rect_
.bottom());
231 return gfx::Rect(x
, y
, width
, height
);
234 int TilingData::TilePositionX(int x_index
) const {
235 DCHECK_GE(x_index
, 0);
236 DCHECK_LT(x_index
, num_tiles_x_
);
238 int pos
= (max_texture_size_
.width() - 2 * border_texels_
) * x_index
;
240 pos
+= border_texels_
;
242 pos
+= tiling_rect_
.x();
247 int TilingData::TilePositionY(int y_index
) const {
248 DCHECK_GE(y_index
, 0);
249 DCHECK_LT(y_index
, num_tiles_y_
);
251 int pos
= (max_texture_size_
.height() - 2 * border_texels_
) * y_index
;
253 pos
+= border_texels_
;
255 pos
+= tiling_rect_
.y();
260 int TilingData::TileSizeX(int x_index
) const {
261 DCHECK_GE(x_index
, 0);
262 DCHECK_LT(x_index
, num_tiles_x_
);
264 if (!x_index
&& num_tiles_x_
== 1)
265 return tiling_rect_
.width();
266 if (!x_index
&& num_tiles_x_
> 1)
267 return max_texture_size_
.width() - border_texels_
;
268 if (x_index
< num_tiles_x_
- 1)
269 return max_texture_size_
.width() - 2 * border_texels_
;
270 if (x_index
== num_tiles_x_
- 1)
271 return tiling_rect_
.right() - TilePositionX(x_index
);
277 int TilingData::TileSizeY(int y_index
) const {
278 DCHECK_GE(y_index
, 0);
279 DCHECK_LT(y_index
, num_tiles_y_
);
281 if (!y_index
&& num_tiles_y_
== 1)
282 return tiling_rect_
.height();
283 if (!y_index
&& num_tiles_y_
> 1)
284 return max_texture_size_
.height() - border_texels_
;
285 if (y_index
< num_tiles_y_
- 1)
286 return max_texture_size_
.height() - 2 * border_texels_
;
287 if (y_index
== num_tiles_y_
- 1)
288 return tiling_rect_
.bottom() - TilePositionY(y_index
);
294 gfx::Vector2d
TilingData::TextureOffset(int x_index
, int y_index
) const {
295 int left
= (!x_index
|| num_tiles_x_
== 1) ? 0 : border_texels_
;
296 int top
= (!y_index
|| num_tiles_y_
== 1) ? 0 : border_texels_
;
298 return gfx::Vector2d(left
, top
);
301 void TilingData::RecomputeNumTiles() {
302 num_tiles_x_
= ComputeNumTiles(
303 max_texture_size_
.width(), tiling_rect_
.width(), border_texels_
);
304 num_tiles_y_
= ComputeNumTiles(
305 max_texture_size_
.height(), tiling_rect_
.height(), border_texels_
);
308 TilingData::BaseIterator::BaseIterator(const TilingData
* tiling_data
)
309 : tiling_data_(tiling_data
),
314 TilingData::Iterator::Iterator() : BaseIterator(NULL
) { done(); }
316 TilingData::Iterator::Iterator(const TilingData
* tiling_data
,
317 const gfx::Rect
& tiling_rect
,
318 bool include_borders
)
319 : BaseIterator(tiling_data
), left_(-1), right_(-1), bottom_(-1) {
320 if (tiling_data_
->num_tiles_x() <= 0 || tiling_data_
->num_tiles_y() <= 0) {
325 gfx::Rect
rect(tiling_rect
);
326 rect
.Intersect(tiling_data_
->tiling_rect());
328 gfx::Rect top_left_tile
;
329 if (include_borders
) {
330 index_x_
= tiling_data_
->FirstBorderTileXIndexFromSrcCoord(rect
.x());
331 index_y_
= tiling_data_
->FirstBorderTileYIndexFromSrcCoord(rect
.y());
332 right_
= tiling_data_
->LastBorderTileXIndexFromSrcCoord(rect
.right() - 1);
333 bottom_
= tiling_data_
->LastBorderTileYIndexFromSrcCoord(rect
.bottom() - 1);
334 top_left_tile
= tiling_data_
->TileBoundsWithBorder(index_x_
, index_y_
);
336 index_x_
= tiling_data_
->TileXIndexFromSrcCoord(rect
.x());
337 index_y_
= tiling_data_
->TileYIndexFromSrcCoord(rect
.y());
338 right_
= tiling_data_
->TileXIndexFromSrcCoord(rect
.right() - 1);
339 bottom_
= tiling_data_
->TileYIndexFromSrcCoord(rect
.bottom() - 1);
340 top_left_tile
= tiling_data_
->TileBounds(index_x_
, index_y_
);
344 // Index functions always return valid indices, so explicitly check
345 // for non-intersecting rects.
346 if (!top_left_tile
.Intersects(rect
))
350 TilingData::Iterator
& TilingData::Iterator::operator++() {
355 if (index_x_
> right_
) {
358 if (index_y_
> bottom_
)
365 TilingData::DifferenceIterator::DifferenceIterator(
366 const TilingData
* tiling_data
,
367 const gfx::Rect
& consider_rect
,
368 const gfx::Rect
& ignore_rect
)
369 : BaseIterator(tiling_data
),
373 consider_bottom_(-1),
378 if (tiling_data_
->num_tiles_x() <= 0 || tiling_data_
->num_tiles_y() <= 0) {
383 gfx::Rect
consider(consider_rect
);
384 gfx::Rect
ignore(ignore_rect
);
385 consider
.Intersect(tiling_data_
->tiling_rect());
386 ignore
.Intersect(tiling_data_
->tiling_rect());
387 if (consider
.IsEmpty()) {
393 tiling_data_
->FirstBorderTileXIndexFromSrcCoord(consider
.x());
395 tiling_data_
->FirstBorderTileYIndexFromSrcCoord(consider
.y());
397 tiling_data_
->LastBorderTileXIndexFromSrcCoord(consider
.right() - 1);
399 tiling_data_
->LastBorderTileYIndexFromSrcCoord(consider
.bottom() - 1);
401 if (!ignore
.IsEmpty()) {
403 tiling_data_
->FirstBorderTileXIndexFromSrcCoord(ignore
.x());
405 tiling_data_
->FirstBorderTileYIndexFromSrcCoord(ignore
.y());
407 tiling_data_
->LastBorderTileXIndexFromSrcCoord(ignore
.right() - 1);
409 tiling_data_
->LastBorderTileYIndexFromSrcCoord(ignore
.bottom() - 1);
411 // Clamp ignore indices to consider indices.
412 ignore_left_
= std::max(ignore_left_
, consider_left_
);
413 ignore_top_
= std::max(ignore_top_
, consider_top_
);
414 ignore_right_
= std::min(ignore_right_
, consider_right_
);
415 ignore_bottom_
= std::min(ignore_bottom_
, consider_bottom_
);
418 if (ignore_left_
== consider_left_
&& ignore_right_
== consider_right_
&&
419 ignore_top_
== consider_top_
&& ignore_bottom_
== consider_bottom_
) {
424 index_x_
= consider_left_
;
425 index_y_
= consider_top_
;
427 if (in_ignore_rect())
431 TilingData::DifferenceIterator
& TilingData::DifferenceIterator::operator++() {
436 if (in_ignore_rect())
437 index_x_
= ignore_right_
+ 1;
439 if (index_x_
> consider_right_
) {
440 index_x_
= consider_left_
;
443 if (in_ignore_rect()) {
444 index_x_
= ignore_right_
+ 1;
445 // If the ignore rect spans the whole consider rect horizontally, then
446 // ignore_right + 1 will be out of bounds.
447 if (in_ignore_rect() || index_x_
> consider_right_
) {
448 index_y_
= ignore_bottom_
+ 1;
449 index_x_
= consider_left_
;
453 if (index_y_
> consider_bottom_
)
460 TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator()
461 : BaseIterator(NULL
) {
465 TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator(
466 const TilingData
* tiling_data
,
467 const gfx::Rect
& consider_rect
,
468 const gfx::Rect
& ignore_rect
,
469 const gfx::Rect
& center_rect
)
470 : BaseIterator(tiling_data
),
474 consider_bottom_(-1),
483 horizontal_step_count_(0),
484 vertical_step_count_(0) {
485 if (tiling_data_
->num_tiles_x() <= 0 || tiling_data_
->num_tiles_y() <= 0) {
490 gfx::Rect
consider(consider_rect
);
491 gfx::Rect
ignore(ignore_rect
);
492 gfx::Rect
center(center_rect
);
493 consider
.Intersect(tiling_data_
->tiling_rect());
494 ignore
.Intersect(tiling_data_
->tiling_rect());
495 if (consider
.IsEmpty()) {
501 tiling_data_
->FirstBorderTileXIndexFromSrcCoord(consider
.x());
502 consider_top_
= tiling_data_
->FirstBorderTileYIndexFromSrcCoord(consider
.y());
504 tiling_data_
->LastBorderTileXIndexFromSrcCoord(consider
.right() - 1);
506 tiling_data_
->LastBorderTileYIndexFromSrcCoord(consider
.bottom() - 1);
508 if (!ignore
.IsEmpty()) {
509 ignore_left_
= tiling_data_
->FirstBorderTileXIndexFromSrcCoord(ignore
.x());
510 ignore_top_
= tiling_data_
->FirstBorderTileYIndexFromSrcCoord(ignore
.y());
512 tiling_data_
->LastBorderTileXIndexFromSrcCoord(ignore
.right() - 1);
514 tiling_data_
->LastBorderTileYIndexFromSrcCoord(ignore
.bottom() - 1);
516 // Clamp ignore indices to consider indices.
517 ignore_left_
= std::max(ignore_left_
, consider_left_
);
518 ignore_top_
= std::max(ignore_top_
, consider_top_
);
519 ignore_right_
= std::min(ignore_right_
, consider_right_
);
520 ignore_bottom_
= std::min(ignore_bottom_
, consider_bottom_
);
523 if (ignore_left_
== consider_left_
&& ignore_right_
== consider_right_
&&
524 ignore_top_
== consider_top_
&& ignore_bottom_
== consider_bottom_
) {
529 // Determine around left, such that it is between -1 and num_tiles_x.
531 if (center
.x() < tiling_data
->tiling_rect().x() || center
.IsEmpty())
533 else if (center
.x() > tiling_data
->tiling_rect().right())
534 around_left
= tiling_data
->num_tiles_x();
536 around_left
= tiling_data
->FirstBorderTileXIndexFromSrcCoord(center
.x());
538 // Determine around top, such that it is between -1 and num_tiles_y.
540 if (center
.y() < tiling_data
->tiling_rect().y() || center
.IsEmpty())
542 else if (center
.y() > tiling_data
->tiling_rect().bottom())
543 around_top
= tiling_data
->num_tiles_y();
545 around_top
= tiling_data
->FirstBorderTileYIndexFromSrcCoord(center
.y());
547 // Determine around right, such that it is between -1 and num_tiles_x.
548 int right_src_coord
= center
.right() - 1;
549 int around_right
= 0;
550 if (right_src_coord
< tiling_data
->tiling_rect().x() || center
.IsEmpty()) {
552 } else if (right_src_coord
> tiling_data
->tiling_rect().right()) {
553 around_right
= tiling_data
->num_tiles_x();
556 tiling_data
->LastBorderTileXIndexFromSrcCoord(right_src_coord
);
559 // Determine around bottom, such that it is between -1 and num_tiles_y.
560 int bottom_src_coord
= center
.bottom() - 1;
561 int around_bottom
= 0;
562 if (bottom_src_coord
< tiling_data
->tiling_rect().y() || center
.IsEmpty()) {
564 } else if (bottom_src_coord
> tiling_data
->tiling_rect().bottom()) {
565 around_bottom
= tiling_data
->num_tiles_y();
568 tiling_data
->LastBorderTileYIndexFromSrcCoord(bottom_src_coord
);
571 vertical_step_count_
= around_bottom
- around_top
+ 1;
572 horizontal_step_count_
= around_right
- around_left
+ 1;
573 current_step_
= horizontal_step_count_
- 1;
575 index_x_
= around_right
;
576 index_y_
= around_bottom
;
578 // The current index is the bottom right of the around rect, which is also
579 // ignored. So we have to advance.
583 TilingData::SpiralDifferenceIterator
& TilingData::SpiralDifferenceIterator::
585 int cannot_hit_consider_count
= 0;
586 while (cannot_hit_consider_count
< 4) {
587 if (needs_direction_switch())
590 index_x_
+= delta_x_
;
591 index_y_
+= delta_y_
;
594 if (in_consider_rect()) {
595 cannot_hit_consider_count
= 0;
597 if (!in_ignore_rect())
600 // Steps needed to reach the very edge of the ignore rect, while remaining
601 // inside (so that the continue would take us outside).
602 int steps_to_edge
= 0;
603 switch (direction_
) {
605 steps_to_edge
= index_y_
- ignore_top_
;
608 steps_to_edge
= index_x_
- ignore_left_
;
611 steps_to_edge
= ignore_bottom_
- index_y_
;
614 steps_to_edge
= ignore_right_
- index_x_
;
618 // We need to switch directions in |max_steps|.
619 int max_steps
= current_step_count() - current_step_
;
621 int steps_to_take
= std::min(steps_to_edge
, max_steps
);
622 DCHECK_GE(steps_to_take
, 0);
624 index_x_
+= steps_to_take
* delta_x_
;
625 index_y_
+= steps_to_take
* delta_y_
;
626 current_step_
+= steps_to_take
;
628 int max_steps
= current_step_count() - current_step_
;
629 int steps_to_take
= max_steps
;
630 bool can_hit_consider_rect
= false;
631 switch (direction_
) {
633 if (valid_column() && consider_bottom_
< index_y_
)
634 steps_to_take
= index_y_
- consider_bottom_
- 1;
635 can_hit_consider_rect
|= consider_right_
>= index_x_
;
638 if (valid_row() && consider_right_
< index_x_
)
639 steps_to_take
= index_x_
- consider_right_
- 1;
640 can_hit_consider_rect
|= consider_top_
<= index_y_
;
643 if (valid_column() && consider_top_
> index_y_
)
644 steps_to_take
= consider_top_
- index_y_
- 1;
645 can_hit_consider_rect
|= consider_left_
<= index_x_
;
648 if (valid_row() && consider_left_
> index_x_
)
649 steps_to_take
= consider_left_
- index_x_
- 1;
650 can_hit_consider_rect
|= consider_bottom_
>= index_y_
;
653 steps_to_take
= std::min(steps_to_take
, max_steps
);
654 DCHECK_GE(steps_to_take
, 0);
656 index_x_
+= steps_to_take
* delta_x_
;
657 index_y_
+= steps_to_take
* delta_y_
;
658 current_step_
+= steps_to_take
;
660 if (can_hit_consider_rect
)
661 cannot_hit_consider_count
= 0;
663 ++cannot_hit_consider_count
;
667 if (cannot_hit_consider_count
>= 4)
672 bool TilingData::SpiralDifferenceIterator::needs_direction_switch() const {
673 return current_step_
>= current_step_count();
676 void TilingData::SpiralDifferenceIterator::switch_direction() {
677 int new_delta_x_
= delta_y_
;
678 delta_y_
= -delta_x_
;
679 delta_x_
= new_delta_x_
;
682 direction_
= static_cast<Direction
>((direction_
+ 1) % 4);
684 if (direction_
== RIGHT
|| direction_
== LEFT
) {
685 ++vertical_step_count_
;
686 ++horizontal_step_count_
;