Home | History | Annotate | Download | only in graphics
      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 
     28 namespace WebCore {
     29 
     30 static const int defaultTileWidth = 512;
     31 static const int defaultTileHeight = 512;
     32 
     33 TiledBackingStore::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 
     47 TiledBackingStore::~TiledBackingStore()
     48 {
     49     delete m_tileBufferUpdateTimer;
     50     delete m_tileCreationTimer;
     51 }
     52 
     53 void TiledBackingStore::setTileSize(const IntSize& size)
     54 {
     55     m_tileSize = size;
     56     m_tiles.clear();
     57     startTileCreationTimer();
     58 }
     59 
     60 void TiledBackingStore::setTileCreationDelay(double delay)
     61 {
     62     m_tileCreationDelay = delay;
     63 }
     64 
     65 void TiledBackingStore::setKeepAndCoverAreaMultipliers(const FloatSize& keepMultiplier, const FloatSize& coverMultiplier)
     66 {
     67     m_keepAreaMultiplier = keepMultiplier;
     68     m_coverAreaMultiplier = coverMultiplier;
     69     startTileCreationTimer();
     70 }
     71 
     72 void 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 
     91 void 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 
    125 void 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 
    156 void 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 
    166 void 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 
    178 void TiledBackingStore::commitScaleChange()
    179 {
    180     m_contentsScale = m_pendingScale;
    181     m_pendingScale = 0;
    182     m_tiles.clear();
    183     createTiles();
    184 }
    185 
    186 double 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 
    199 void 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 
    269 void 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 
    287 void 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 
    304 PassRefPtr<Tile> TiledBackingStore::tileAt(const Tile::Coordinate& coordinate) const
    305 {
    306     return m_tiles.get(coordinate);
    307 }
    308 
    309 void TiledBackingStore::setTile(const Tile::Coordinate& coordinate, PassRefPtr<Tile> tile)
    310 {
    311     m_tiles.set(coordinate, tile);
    312 }
    313 
    314 void TiledBackingStore::removeTile(const Tile::Coordinate& coordinate)
    315 {
    316     m_tiles.remove(coordinate);
    317 }
    318 
    319 IntRect 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 
    327 IntRect 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 
    335 IntRect TiledBackingStore::contentsRect() const
    336 {
    337     return mapFromContents(m_client->tiledBackingStoreContentsRect());
    338 }
    339 
    340 IntRect 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 
    351 Tile::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 
    359 void TiledBackingStore::startTileBufferUpdateTimer()
    360 {
    361     if (m_tileBufferUpdateTimer->isActive() || m_contentsFrozen)
    362         return;
    363     m_tileBufferUpdateTimer->startOneShot(0);
    364 }
    365 
    366 void TiledBackingStore::tileBufferUpdateTimerFired(TileTimer*)
    367 {
    368     updateTileBuffers();
    369 }
    370 
    371 void TiledBackingStore::startTileCreationTimer()
    372 {
    373     if (m_tileCreationTimer->isActive() || m_contentsFrozen)
    374         return;
    375     m_tileCreationTimer->startOneShot(0);
    376 }
    377 
    378 void TiledBackingStore::tileCreationTimerFired(TileTimer*)
    379 {
    380     createTiles();
    381 }
    382 
    383 void 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