• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TiledBackingStore(TiledBackingStoreClient * client)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 
~TiledBackingStore()47 TiledBackingStore::~TiledBackingStore()
48 {
49     delete m_tileBufferUpdateTimer;
50     delete m_tileCreationTimer;
51 }
52 
setTileSize(const IntSize & size)53 void TiledBackingStore::setTileSize(const IntSize& size)
54 {
55     m_tileSize = size;
56     m_tiles.clear();
57     startTileCreationTimer();
58 }
59 
setTileCreationDelay(double delay)60 void TiledBackingStore::setTileCreationDelay(double delay)
61 {
62     m_tileCreationDelay = delay;
63 }
64 
setKeepAndCoverAreaMultipliers(const FloatSize & keepMultiplier,const FloatSize & coverMultiplier)65 void TiledBackingStore::setKeepAndCoverAreaMultipliers(const FloatSize& keepMultiplier, const FloatSize& coverMultiplier)
66 {
67     m_keepAreaMultiplier = keepMultiplier;
68     m_coverAreaMultiplier = coverMultiplier;
69     startTileCreationTimer();
70 }
71 
invalidate(const IntRect & contentsDirtyRect)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 
updateTileBuffers()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 
paint(GraphicsContext * context,const IntRect & rect)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 
adjustVisibleRect()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 
setContentsScale(float scale)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 
commitScaleChange()178 void TiledBackingStore::commitScaleChange()
179 {
180     m_contentsScale = m_pendingScale;
181     m_pendingScale = 0;
182     m_tiles.clear();
183     createTiles();
184 }
185 
tileDistance(const IntRect & viewport,const Tile::Coordinate & tileCoordinate)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 
createTiles()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 
dropOverhangingTiles()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 
dropTilesOutsideRect(const IntRect & keepRect)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 
tileAt(const Tile::Coordinate & coordinate) const304 PassRefPtr<Tile> TiledBackingStore::tileAt(const Tile::Coordinate& coordinate) const
305 {
306     return m_tiles.get(coordinate);
307 }
308 
setTile(const Tile::Coordinate & coordinate,PassRefPtr<Tile> tile)309 void TiledBackingStore::setTile(const Tile::Coordinate& coordinate, PassRefPtr<Tile> tile)
310 {
311     m_tiles.set(coordinate, tile);
312 }
313 
removeTile(const Tile::Coordinate & coordinate)314 void TiledBackingStore::removeTile(const Tile::Coordinate& coordinate)
315 {
316     m_tiles.remove(coordinate);
317 }
318 
mapToContents(const IntRect & rect) const319 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 
mapFromContents(const IntRect & rect) const327 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 
contentsRect() const335 IntRect TiledBackingStore::contentsRect() const
336 {
337     return mapFromContents(m_client->tiledBackingStoreContentsRect());
338 }
339 
tileRectForCoordinate(const Tile::Coordinate & coordinate) const340 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 
tileCoordinateForPoint(const IntPoint & point) const351 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 
startTileBufferUpdateTimer()359 void TiledBackingStore::startTileBufferUpdateTimer()
360 {
361     if (m_tileBufferUpdateTimer->isActive() || m_contentsFrozen)
362         return;
363     m_tileBufferUpdateTimer->startOneShot(0);
364 }
365 
tileBufferUpdateTimerFired(TileTimer *)366 void TiledBackingStore::tileBufferUpdateTimerFired(TileTimer*)
367 {
368     updateTileBuffers();
369 }
370 
startTileCreationTimer()371 void TiledBackingStore::startTileCreationTimer()
372 {
373     if (m_tileCreationTimer->isActive() || m_contentsFrozen)
374         return;
375     m_tileCreationTimer->startOneShot(0);
376 }
377 
tileCreationTimerFired(TileTimer *)378 void TiledBackingStore::tileCreationTimerFired(TileTimer*)
379 {
380     createTiles();
381 }
382 
setContentsFrozen(bool freeze)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