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. 4 5 #include "cc/base/tiling_data.h" 6 7 #include <algorithm> 8 9 #include "ui/gfx/rect.h" 10 #include "ui/gfx/vector2d.h" 11 12 namespace cc { 13 14 static int ComputeNumTiles(int max_texture_size, 15 int total_size, 16 int border_texels) { 17 if (max_texture_size - 2 * border_texels <= 0) 18 return total_size > 0 && max_texture_size >= total_size ? 1 : 0; 19 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; 24 } 25 26 TilingData::TilingData() 27 : border_texels_(0) { 28 RecomputeNumTiles(); 29 } 30 31 TilingData::TilingData( 32 gfx::Size max_texture_size, 33 gfx::Size total_size, 34 bool has_border_texels) 35 : max_texture_size_(max_texture_size), 36 total_size_(total_size), 37 border_texels_(has_border_texels ? 1 : 0) { 38 RecomputeNumTiles(); 39 } 40 41 TilingData::TilingData( 42 gfx::Size max_texture_size, 43 gfx::Size total_size, 44 int border_texels) 45 : max_texture_size_(max_texture_size), 46 total_size_(total_size), 47 border_texels_(border_texels) { 48 RecomputeNumTiles(); 49 } 50 51 void TilingData::SetTotalSize(gfx::Size total_size) { 52 total_size_ = total_size; 53 RecomputeNumTiles(); 54 } 55 56 void TilingData::SetMaxTextureSize(gfx::Size max_texture_size) { 57 max_texture_size_ = max_texture_size; 58 RecomputeNumTiles(); 59 } 60 61 void TilingData::SetHasBorderTexels(bool has_border_texels) { 62 border_texels_ = has_border_texels ? 1 : 0; 63 RecomputeNumTiles(); 64 } 65 66 void TilingData::SetBorderTexels(int border_texels) { 67 border_texels_ = border_texels; 68 RecomputeNumTiles(); 69 } 70 71 int TilingData::TileXIndexFromSrcCoord(int src_position) const { 72 if (num_tiles_x_ <= 1) 73 return 0; 74 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); 79 } 80 81 int TilingData::TileYIndexFromSrcCoord(int src_position) const { 82 if (num_tiles_y_ <= 1) 83 return 0; 84 85 DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); 86 int y = (src_position - border_texels_) / 87 (max_texture_size_.height() - 2 * border_texels_); 88 return std::min(std::max(y, 0), num_tiles_y_ - 1); 89 } 90 91 int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position) const { 92 if (num_tiles_x_ <= 1) 93 return 0; 94 95 DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0); 96 int inner_tile_size = max_texture_size_.width() - 2 * border_texels_; 97 int x = (src_position - 2 * border_texels_) / inner_tile_size; 98 return std::min(std::max(x, 0), num_tiles_x_ - 1); 99 } 100 101 int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position) const { 102 if (num_tiles_y_ <= 1) 103 return 0; 104 105 DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); 106 int inner_tile_size = max_texture_size_.height() - 2 * border_texels_; 107 int y = (src_position - 2 * border_texels_) / inner_tile_size; 108 return std::min(std::max(y, 0), num_tiles_y_ - 1); 109 } 110 111 int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position) const { 112 if (num_tiles_x_ <= 1) 113 return 0; 114 115 DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0); 116 int inner_tile_size = max_texture_size_.width() - 2 * border_texels_; 117 int x = src_position / inner_tile_size; 118 return std::min(std::max(x, 0), num_tiles_x_ - 1); 119 } 120 121 int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const { 122 if (num_tiles_y_ <= 1) 123 return 0; 124 125 DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0); 126 int inner_tile_size = max_texture_size_.height() - 2 * border_texels_; 127 int y = src_position / inner_tile_size; 128 return std::min(std::max(y, 0), num_tiles_y_ - 1); 129 } 130 131 gfx::Rect TilingData::TileBounds(int i, int j) const { 132 AssertTile(i, j); 133 int max_texture_size_x = max_texture_size_.width() - 2 * border_texels_; 134 int max_texture_size_y = max_texture_size_.height() - 2 * border_texels_; 135 int total_size_x = total_size_.width(); 136 int total_size_y = total_size_.height(); 137 138 int lo_x = max_texture_size_x * i; 139 if (i != 0) 140 lo_x += border_texels_; 141 142 int lo_y = max_texture_size_y * j; 143 if (j != 0) 144 lo_y += border_texels_; 145 146 int hi_x = max_texture_size_x * (i + 1) + border_texels_; 147 if (i + 1 == num_tiles_x_) 148 hi_x += border_texels_; 149 150 int hi_y = max_texture_size_y * (j + 1) + border_texels_; 151 if (j + 1 == num_tiles_y_) 152 hi_y += border_texels_; 153 154 hi_x = std::min(hi_x, total_size_x); 155 hi_y = std::min(hi_y, total_size_y); 156 157 int x = lo_x; 158 int y = lo_y; 159 int width = hi_x - lo_x; 160 int height = hi_y - lo_y; 161 DCHECK_GE(x, 0); 162 DCHECK_GE(y, 0); 163 DCHECK_GE(width, 0); 164 DCHECK_GE(height, 0); 165 DCHECK_LE(x, total_size_.width()); 166 DCHECK_LE(y, total_size_.height()); 167 return gfx::Rect(x, y, width, height); 168 } 169 170 gfx::Rect TilingData::TileBoundsWithBorder(int i, int j) const { 171 AssertTile(i, j); 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_; 174 int total_size_x = total_size_.width(); 175 int total_size_y = total_size_.height(); 176 177 int lo_x = max_texture_size_x * i; 178 int lo_y = max_texture_size_y * j; 179 180 int hi_x = lo_x + max_texture_size_x + 2 * border_texels_; 181 int hi_y = lo_y + max_texture_size_y + 2 * border_texels_; 182 183 hi_x = std::min(hi_x, total_size_x); 184 hi_y = std::min(hi_y, total_size_y); 185 186 int x = lo_x; 187 int y = lo_y; 188 int width = hi_x - lo_x; 189 int height = hi_y - lo_y; 190 DCHECK_GE(x, 0); 191 DCHECK_GE(y, 0); 192 DCHECK_GE(width, 0); 193 DCHECK_GE(height, 0); 194 DCHECK_LE(x, total_size_.width()); 195 DCHECK_LE(y, total_size_.height()); 196 return gfx::Rect(x, y, width, height); 197 } 198 199 int TilingData::TilePositionX(int x_index) const { 200 DCHECK_GE(x_index, 0); 201 DCHECK_LT(x_index, num_tiles_x_); 202 203 int pos = (max_texture_size_.width() - 2 * border_texels_) * x_index; 204 if (x_index != 0) 205 pos += border_texels_; 206 207 return pos; 208 } 209 210 int TilingData::TilePositionY(int y_index) const { 211 DCHECK_GE(y_index, 0); 212 DCHECK_LT(y_index, num_tiles_y_); 213 214 int pos = (max_texture_size_.height() - 2 * border_texels_) * y_index; 215 if (y_index != 0) 216 pos += border_texels_; 217 218 return pos; 219 } 220 221 int TilingData::TileSizeX(int x_index) const { 222 DCHECK_GE(x_index, 0); 223 DCHECK_LT(x_index, num_tiles_x_); 224 225 if (!x_index && num_tiles_x_ == 1) 226 return total_size_.width(); 227 if (!x_index && num_tiles_x_ > 1) 228 return max_texture_size_.width() - border_texels_; 229 if (x_index < num_tiles_x_ - 1) 230 return max_texture_size_.width() - 2 * border_texels_; 231 if (x_index == num_tiles_x_ - 1) 232 return total_size_.width() - TilePositionX(x_index); 233 234 NOTREACHED(); 235 return 0; 236 } 237 238 int TilingData::TileSizeY(int y_index) const { 239 DCHECK_GE(y_index, 0); 240 DCHECK_LT(y_index, num_tiles_y_); 241 242 if (!y_index && num_tiles_y_ == 1) 243 return total_size_.height(); 244 if (!y_index && num_tiles_y_ > 1) 245 return max_texture_size_.height() - border_texels_; 246 if (y_index < num_tiles_y_ - 1) 247 return max_texture_size_.height() - 2 * border_texels_; 248 if (y_index == num_tiles_y_ - 1) 249 return total_size_.height() - TilePositionY(y_index); 250 251 NOTREACHED(); 252 return 0; 253 } 254 255 gfx::Vector2d TilingData::TextureOffset(int x_index, int y_index) const { 256 int left = (!x_index || num_tiles_x_ == 1) ? 0 : border_texels_; 257 int top = (!y_index || num_tiles_y_ == 1) ? 0 : border_texels_; 258 259 return gfx::Vector2d(left, top); 260 } 261 262 void TilingData::RecomputeNumTiles() { 263 num_tiles_x_ = ComputeNumTiles( 264 max_texture_size_.width(), total_size_.width(), border_texels_); 265 num_tiles_y_ = ComputeNumTiles( 266 max_texture_size_.height(), total_size_.height(), border_texels_); 267 } 268 269 TilingData::BaseIterator::BaseIterator(const TilingData* tiling_data) 270 : tiling_data_(tiling_data), 271 index_x_(-1), 272 index_y_(-1) { 273 } 274 275 TilingData::Iterator::Iterator() : BaseIterator(NULL) { done(); } 276 277 TilingData::Iterator::Iterator(const TilingData* tiling_data, 278 const gfx::Rect& tiling_rect, 279 bool include_borders) 280 : BaseIterator(tiling_data), left_(-1), right_(-1), bottom_(-1) { 281 if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) { 282 done(); 283 return; 284 } 285 286 gfx::Rect rect(tiling_rect); 287 rect.Intersect(gfx::Rect(tiling_data_->total_size())); 288 289 gfx::Rect top_left_tile; 290 if (include_borders) { 291 index_x_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(rect.x()); 292 index_y_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(rect.y()); 293 right_ = tiling_data_->LastBorderTileXIndexFromSrcCoord(rect.right() - 1); 294 bottom_ = tiling_data_->LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1); 295 top_left_tile = tiling_data_->TileBoundsWithBorder(index_x_, index_y_); 296 } else { 297 index_x_ = tiling_data_->TileXIndexFromSrcCoord(rect.x()); 298 index_y_ = tiling_data_->TileYIndexFromSrcCoord(rect.y()); 299 right_ = tiling_data_->TileXIndexFromSrcCoord(rect.right() - 1); 300 bottom_ = tiling_data_->TileYIndexFromSrcCoord(rect.bottom() - 1); 301 top_left_tile = tiling_data_->TileBounds(index_x_, index_y_); 302 } 303 left_ = index_x_; 304 305 // Index functions always return valid indices, so explicitly check 306 // for non-intersecting rects. 307 if (!top_left_tile.Intersects(rect)) 308 done(); 309 } 310 311 TilingData::Iterator& TilingData::Iterator::operator++() { 312 if (!*this) 313 return *this; 314 315 index_x_++; 316 if (index_x_ > right_) { 317 index_x_ = left_; 318 index_y_++; 319 if (index_y_ > bottom_) 320 done(); 321 } 322 323 return *this; 324 } 325 326 TilingData::DifferenceIterator::DifferenceIterator( 327 const TilingData* tiling_data, 328 gfx::Rect consider, 329 gfx::Rect ignore) 330 : BaseIterator(tiling_data), 331 consider_left_(-1), 332 consider_top_(-1), 333 consider_right_(-1), 334 consider_bottom_(-1), 335 ignore_left_(-1), 336 ignore_top_(-1), 337 ignore_right_(-1), 338 ignore_bottom_(-1) { 339 if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) { 340 done(); 341 return; 342 } 343 344 gfx::Rect bounds(tiling_data_->total_size()); 345 consider.Intersect(bounds); 346 ignore.Intersect(bounds); 347 if (consider.IsEmpty()) { 348 done(); 349 return; 350 } 351 352 consider_left_ = 353 tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x()); 354 consider_top_ = 355 tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y()); 356 consider_right_ = 357 tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1); 358 consider_bottom_ = 359 tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1); 360 361 if (!ignore.IsEmpty()) { 362 ignore_left_ = 363 tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x()); 364 ignore_top_ = 365 tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y()); 366 ignore_right_ = 367 tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1); 368 ignore_bottom_ = 369 tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1); 370 371 // Clamp ignore indices to consider indices. 372 ignore_left_ = std::max(ignore_left_, consider_left_); 373 ignore_top_ = std::max(ignore_top_, consider_top_); 374 ignore_right_ = std::min(ignore_right_, consider_right_); 375 ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_); 376 } 377 378 if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ && 379 ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) { 380 done(); 381 return; 382 } 383 384 index_x_ = consider_left_; 385 index_y_ = consider_top_; 386 387 if (in_ignore_rect()) 388 ++(*this); 389 } 390 391 TilingData::DifferenceIterator& TilingData::DifferenceIterator::operator++() { 392 if (!*this) 393 return *this; 394 395 index_x_++; 396 if (in_ignore_rect()) 397 index_x_ = ignore_right_ + 1; 398 399 if (index_x_ > consider_right_) { 400 index_x_ = consider_left_; 401 index_y_++; 402 403 if (in_ignore_rect()) { 404 index_x_ = ignore_right_ + 1; 405 // If the ignore rect spans the whole consider rect horizontally, then 406 // ignore_right + 1 will be out of bounds. 407 if (in_ignore_rect() || index_x_ > consider_right_) { 408 index_y_ = ignore_bottom_ + 1; 409 index_x_ = consider_left_; 410 } 411 } 412 413 if (index_y_ > consider_bottom_) 414 done(); 415 } 416 417 return *this; 418 } 419 420 } // namespace cc 421