1/* 2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "TiledBackingStore.h" 22 23#if ENABLE(TILED_BACKING_STORE) 24 25#include "GraphicsContext.h" 26#include "TiledBackingStoreClient.h" 27 28namespace WebCore { 29 30static const int defaultTileWidth = 512; 31static const int defaultTileHeight = 512; 32 33TiledBackingStore::TiledBackingStore(TiledBackingStoreClient* client) 34 : m_client(client) 35 , m_tileBufferUpdateTimer(new TileTimer(this, &TiledBackingStore::tileBufferUpdateTimerFired)) 36 , m_tileCreationTimer(new TileTimer(this, &TiledBackingStore::tileCreationTimerFired)) 37 , m_tileSize(defaultTileWidth, defaultTileHeight) 38 , m_tileCreationDelay(0.01) 39 , m_keepAreaMultiplier(2.f, 3.5f) 40 , m_coverAreaMultiplier(1.5f, 2.5f) 41 , m_contentsScale(1.f) 42 , m_pendingScale(0) 43 , m_contentsFrozen(false) 44{ 45} 46 47TiledBackingStore::~TiledBackingStore() 48{ 49 delete m_tileBufferUpdateTimer; 50 delete m_tileCreationTimer; 51} 52 53void TiledBackingStore::setTileSize(const IntSize& size) 54{ 55 m_tileSize = size; 56 m_tiles.clear(); 57 startTileCreationTimer(); 58} 59 60void TiledBackingStore::setTileCreationDelay(double delay) 61{ 62 m_tileCreationDelay = delay; 63} 64 65void TiledBackingStore::setKeepAndCoverAreaMultipliers(const FloatSize& keepMultiplier, const FloatSize& coverMultiplier) 66{ 67 m_keepAreaMultiplier = keepMultiplier; 68 m_coverAreaMultiplier = coverMultiplier; 69 startTileCreationTimer(); 70} 71 72void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect) 73{ 74 IntRect dirtyRect(mapFromContents(contentsDirtyRect)); 75 76 Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location()); 77 Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(dirtyRect.maxX(), dirtyRect.maxY())); 78 79 for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { 80 for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { 81 RefPtr<Tile> currentTile = tileAt(Tile::Coordinate(xCoordinate, yCoordinate)); 82 if (!currentTile) 83 continue; 84 currentTile->invalidate(dirtyRect); 85 } 86 } 87 88 startTileBufferUpdateTimer(); 89} 90 91void TiledBackingStore::updateTileBuffers() 92{ 93 if (m_contentsFrozen) 94 return; 95 96 m_client->tiledBackingStorePaintBegin(); 97 98 Vector<IntRect> paintedArea; 99 Vector<RefPtr<Tile> > dirtyTiles; 100 TileMap::iterator end = m_tiles.end(); 101 for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) { 102 if (!it->second->isDirty()) 103 continue; 104 dirtyTiles.append(it->second); 105 } 106 107 if (dirtyTiles.isEmpty()) { 108 m_client->tiledBackingStorePaintEnd(paintedArea); 109 return; 110 } 111 112 // FIXME: In single threaded case, tile back buffers could be updated asynchronously 113 // one by one and then swapped to front in one go. This would minimize the time spent 114 // blocking on tile updates. 115 unsigned size = dirtyTiles.size(); 116 for (unsigned n = 0; n < size; ++n) { 117 Vector<IntRect> paintedRects = dirtyTiles[n]->updateBackBuffer(); 118 paintedArea.append(paintedRects); 119 dirtyTiles[n]->swapBackBufferToFront(); 120 } 121 122 m_client->tiledBackingStorePaintEnd(paintedArea); 123} 124 125void TiledBackingStore::paint(GraphicsContext* context, const IntRect& rect) 126{ 127 context->save(); 128 129 // Assumes the backing store is painted with the scale transform applied. 130 // Since tile content is already scaled, first revert the scaling from the painter. 131 context->scale(FloatSize(1.f / m_contentsScale, 1.f / m_contentsScale)); 132 133 IntRect dirtyRect = mapFromContents(rect); 134 135 Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location()); 136 Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(dirtyRect.maxX(), dirtyRect.maxY())); 137 138 for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { 139 for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { 140 Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate); 141 RefPtr<Tile> currentTile = tileAt(currentCoordinate); 142 if (currentTile && currentTile->isReadyToPaint()) 143 currentTile->paint(context, dirtyRect); 144 else { 145 IntRect tileRect = tileRectForCoordinate(currentCoordinate); 146 IntRect target = intersection(tileRect, dirtyRect); 147 if (target.isEmpty()) 148 continue; 149 Tile::paintCheckerPattern(context, FloatRect(target)); 150 } 151 } 152 } 153 context->restore(); 154} 155 156void TiledBackingStore::adjustVisibleRect() 157{ 158 IntRect visibleRect = mapFromContents(m_client->tiledBackingStoreVisibleRect()); 159 if (m_previousVisibleRect == visibleRect) 160 return; 161 m_previousVisibleRect = visibleRect; 162 163 startTileCreationTimer(); 164} 165 166void TiledBackingStore::setContentsScale(float scale) 167{ 168 if (m_pendingScale == m_contentsScale) { 169 m_pendingScale = 0; 170 return; 171 } 172 m_pendingScale = scale; 173 if (m_contentsFrozen) 174 return; 175 commitScaleChange(); 176} 177 178void TiledBackingStore::commitScaleChange() 179{ 180 m_contentsScale = m_pendingScale; 181 m_pendingScale = 0; 182 m_tiles.clear(); 183 createTiles(); 184} 185 186double TiledBackingStore::tileDistance(const IntRect& viewport, const Tile::Coordinate& tileCoordinate) 187{ 188 if (viewport.intersects(tileRectForCoordinate(tileCoordinate))) 189 return 0; 190 191 IntPoint viewCenter = viewport.location() + IntSize(viewport.width() / 2, viewport.height() / 2); 192 Tile::Coordinate centerCoordinate = tileCoordinateForPoint(viewCenter); 193 194 // Manhattan distance, biased so that vertical distances are shorter. 195 const double horizontalBias = 1.3; 196 return abs(centerCoordinate.y() - tileCoordinate.y()) + horizontalBias * abs(centerCoordinate.x() - tileCoordinate.x()); 197} 198 199void TiledBackingStore::createTiles() 200{ 201 if (m_contentsFrozen) 202 return; 203 204 IntRect visibleRect = mapFromContents(m_client->tiledBackingStoreVisibleRect()); 205 m_previousVisibleRect = visibleRect; 206 207 if (visibleRect.isEmpty()) 208 return; 209 210 // Remove tiles that extend outside the current contents rect. 211 dropOverhangingTiles(); 212 213 IntRect keepRect = visibleRect; 214 // Inflates to both sides, so divide inflate delta by 2 215 keepRect.inflateX(visibleRect.width() * (m_keepAreaMultiplier.width() - 1.f) / 2); 216 keepRect.inflateY(visibleRect.height() * (m_keepAreaMultiplier.height() - 1.f) / 2); 217 keepRect.intersect(contentsRect()); 218 219 dropTilesOutsideRect(keepRect); 220 221 IntRect coverRect = visibleRect; 222 // Inflates to both sides, so divide inflate delta by 2 223 coverRect.inflateX(visibleRect.width() * (m_coverAreaMultiplier.width() - 1.f) / 2); 224 coverRect.inflateY(visibleRect.height() * (m_coverAreaMultiplier.height() - 1.f) / 2); 225 coverRect.intersect(contentsRect()); 226 227 // Search for the tile position closest to the viewport center that does not yet contain a tile. 228 // Which position is considered the closest depends on the tileDistance function. 229 double shortestDistance = std::numeric_limits<double>::infinity(); 230 Vector<Tile::Coordinate> tilesToCreate; 231 unsigned requiredTileCount = 0; 232 Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.location()); 233 Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(coverRect.maxX(), coverRect.maxY())); 234 for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { 235 for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { 236 Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate); 237 if (tileAt(currentCoordinate)) 238 continue; 239 ++requiredTileCount; 240 // Distance is 0 for all currently visible tiles. 241 double distance = tileDistance(visibleRect, currentCoordinate); 242 if (distance > shortestDistance) 243 continue; 244 if (distance < shortestDistance) { 245 tilesToCreate.clear(); 246 shortestDistance = distance; 247 } 248 tilesToCreate.append(currentCoordinate); 249 } 250 } 251 252 // Now construct the tile(s) 253 unsigned tilesToCreateCount = tilesToCreate.size(); 254 for (unsigned n = 0; n < tilesToCreateCount; ++n) { 255 Tile::Coordinate coordinate = tilesToCreate[n]; 256 setTile(coordinate, Tile::create(this, coordinate)); 257 } 258 requiredTileCount -= tilesToCreateCount; 259 260 // Paint the content of the newly created tiles 261 if (tilesToCreateCount) 262 updateTileBuffers(); 263 264 // Keep creating tiles until the whole coverRect is covered. 265 if (requiredTileCount) 266 m_tileCreationTimer->startOneShot(m_tileCreationDelay); 267} 268 269void TiledBackingStore::dropOverhangingTiles() 270{ 271 IntRect contentsRect = this->contentsRect(); 272 273 Vector<Tile::Coordinate> tilesToRemove; 274 TileMap::iterator end = m_tiles.end(); 275 for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) { 276 Tile::Coordinate tileCoordinate = it->second->coordinate(); 277 IntRect tileRect = it->second->rect(); 278 IntRect expectedTileRect = tileRectForCoordinate(tileCoordinate); 279 if (expectedTileRect != tileRect || !contentsRect.contains(tileRect)) 280 tilesToRemove.append(tileCoordinate); 281 } 282 unsigned removeCount = tilesToRemove.size(); 283 for (unsigned n = 0; n < removeCount; ++n) 284 removeTile(tilesToRemove[n]); 285} 286 287void TiledBackingStore::dropTilesOutsideRect(const IntRect& keepRect) 288{ 289 FloatRect keepRectF = keepRect; 290 291 Vector<Tile::Coordinate> toRemove; 292 TileMap::iterator end = m_tiles.end(); 293 for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) { 294 Tile::Coordinate coordinate = it->second->coordinate(); 295 FloatRect tileRect = it->second->rect(); 296 if (!tileRect.intersects(keepRectF)) 297 toRemove.append(coordinate); 298 } 299 unsigned removeCount = toRemove.size(); 300 for (unsigned n = 0; n < removeCount; ++n) 301 removeTile(toRemove[n]); 302} 303 304PassRefPtr<Tile> TiledBackingStore::tileAt(const Tile::Coordinate& coordinate) const 305{ 306 return m_tiles.get(coordinate); 307} 308 309void TiledBackingStore::setTile(const Tile::Coordinate& coordinate, PassRefPtr<Tile> tile) 310{ 311 m_tiles.set(coordinate, tile); 312} 313 314void TiledBackingStore::removeTile(const Tile::Coordinate& coordinate) 315{ 316 m_tiles.remove(coordinate); 317} 318 319IntRect TiledBackingStore::mapToContents(const IntRect& rect) const 320{ 321 return enclosingIntRect(FloatRect(rect.x() / m_contentsScale, 322 rect.y() / m_contentsScale, 323 rect.width() / m_contentsScale, 324 rect.height() / m_contentsScale)); 325} 326 327IntRect TiledBackingStore::mapFromContents(const IntRect& rect) const 328{ 329 return enclosingIntRect(FloatRect(rect.x() * m_contentsScale, 330 rect.y() * m_contentsScale, 331 rect.width() * m_contentsScale, 332 rect.height() * m_contentsScale)); 333} 334 335IntRect TiledBackingStore::contentsRect() const 336{ 337 return mapFromContents(m_client->tiledBackingStoreContentsRect()); 338} 339 340IntRect TiledBackingStore::tileRectForCoordinate(const Tile::Coordinate& coordinate) const 341{ 342 IntRect rect(coordinate.x() * m_tileSize.width(), 343 coordinate.y() * m_tileSize.height(), 344 m_tileSize.width(), 345 m_tileSize.height()); 346 347 rect.intersect(contentsRect()); 348 return rect; 349} 350 351Tile::Coordinate TiledBackingStore::tileCoordinateForPoint(const IntPoint& point) const 352{ 353 int x = point.x() / m_tileSize.width(); 354 int y = point.y() / m_tileSize.height(); 355 return Tile::Coordinate(std::max(x, 0), std::max(y, 0)); 356} 357 358 359void TiledBackingStore::startTileBufferUpdateTimer() 360{ 361 if (m_tileBufferUpdateTimer->isActive() || m_contentsFrozen) 362 return; 363 m_tileBufferUpdateTimer->startOneShot(0); 364} 365 366void TiledBackingStore::tileBufferUpdateTimerFired(TileTimer*) 367{ 368 updateTileBuffers(); 369} 370 371void TiledBackingStore::startTileCreationTimer() 372{ 373 if (m_tileCreationTimer->isActive() || m_contentsFrozen) 374 return; 375 m_tileCreationTimer->startOneShot(0); 376} 377 378void TiledBackingStore::tileCreationTimerFired(TileTimer*) 379{ 380 createTiles(); 381} 382 383void TiledBackingStore::setContentsFrozen(bool freeze) 384{ 385 if (m_contentsFrozen == freeze) 386 return; 387 388 m_contentsFrozen = freeze; 389 390 // Restart the timers. There might be pending invalidations that 391 // were not painted or created because tiles are not created or 392 // painted when in frozen state. 393 if (m_contentsFrozen) 394 return; 395 if (m_pendingScale) 396 commitScaleChange(); 397 else { 398 startTileCreationTimer(); 399 startTileBufferUpdateTimer(); 400 } 401} 402 403} 404 405#endif 406