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(const TilingData* tiling_data, gfx::Rect rect) 276 : BaseIterator(tiling_data), 277 left_(-1), 278 right_(-1), 279 bottom_(-1) { 280 if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) { 281 done(); 282 return; 283 } 284 285 rect.Intersect(gfx::Rect(tiling_data_->total_size())); 286 index_x_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(rect.x()); 287 index_y_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(rect.y()); 288 left_ = index_x_; 289 right_ = tiling_data_->LastBorderTileXIndexFromSrcCoord(rect.right() - 1); 290 bottom_ = tiling_data_->LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1); 291 292 // Index functions always return valid indices, so explicitly check 293 // for non-intersecting rects. 294 gfx::Rect new_rect = tiling_data_->TileBoundsWithBorder(index_x_, index_y_); 295 if (!new_rect.Intersects(rect)) 296 done(); 297 } 298 299 TilingData::Iterator& TilingData::Iterator::operator++() { 300 if (!*this) 301 return *this; 302 303 index_x_++; 304 if (index_x_ > right_) { 305 index_x_ = left_; 306 index_y_++; 307 if (index_y_ > bottom_) 308 done(); 309 } 310 311 return *this; 312 } 313 314 TilingData::DifferenceIterator::DifferenceIterator( 315 const TilingData* tiling_data, 316 gfx::Rect consider, 317 gfx::Rect ignore) 318 : BaseIterator(tiling_data), 319 consider_left_(-1), 320 consider_top_(-1), 321 consider_right_(-1), 322 consider_bottom_(-1), 323 ignore_left_(-1), 324 ignore_top_(-1), 325 ignore_right_(-1), 326 ignore_bottom_(-1) { 327 if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) { 328 done(); 329 return; 330 } 331 332 gfx::Rect bounds(tiling_data_->total_size()); 333 consider.Intersect(bounds); 334 ignore.Intersect(bounds); 335 if (consider.IsEmpty()) { 336 done(); 337 return; 338 } 339 340 consider_left_ = 341 tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x()); 342 consider_top_ = 343 tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y()); 344 consider_right_ = 345 tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1); 346 consider_bottom_ = 347 tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1); 348 349 if (!ignore.IsEmpty()) { 350 ignore_left_ = 351 tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x()); 352 ignore_top_ = 353 tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y()); 354 ignore_right_ = 355 tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1); 356 ignore_bottom_ = 357 tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1); 358 359 // Clamp ignore indices to consider indices. 360 ignore_left_ = std::max(ignore_left_, consider_left_); 361 ignore_top_ = std::max(ignore_top_, consider_top_); 362 ignore_right_ = std::min(ignore_right_, consider_right_); 363 ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_); 364 } 365 366 if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ && 367 ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) { 368 done(); 369 return; 370 } 371 372 index_x_ = consider_left_; 373 index_y_ = consider_top_; 374 375 if (in_ignore_rect()) 376 ++(*this); 377 } 378 379 TilingData::DifferenceIterator& TilingData::DifferenceIterator::operator++() { 380 if (!*this) 381 return *this; 382 383 index_x_++; 384 if (in_ignore_rect()) 385 index_x_ = ignore_right_ + 1; 386 387 if (index_x_ > consider_right_) { 388 index_x_ = consider_left_; 389 index_y_++; 390 391 if (in_ignore_rect()) { 392 index_x_ = ignore_right_ + 1; 393 // If the ignore rect spans the whole consider rect horizontally, then 394 // ignore_right + 1 will be out of bounds. 395 if (in_ignore_rect() || index_x_ > consider_right_) { 396 index_y_ = ignore_bottom_ + 1; 397 index_x_ = consider_left_; 398 } 399 } 400 401 if (index_y_ > consider_bottom_) 402 done(); 403 } 404 405 return *this; 406 } 407 408 } // namespace cc 409