root/trunk/BAL/Graphics/WebCore/WK/BCTiledBackingStoreWK.cpp

Revision 1404, 11.9 kB (checked in by gbertal, 6 months ago)

merge with webkit revision 55986

Line 
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_contentsScale(1.f)
39     , m_contentsFrozen(false)
40 {
41 }
42
43 TiledBackingStore::~TiledBackingStore()
44 {
45     delete m_tileBufferUpdateTimer;
46     delete m_tileCreationTimer;
47 }
48
49 void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect)
50 {
51     IntRect dirtyRect(mapFromContents(contentsDirtyRect));
52    
53     Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.topLeft());
54     Tile::Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.bottomRight());
55    
56     for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
57         for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
58             RefPtr<Tile> currentTile = tileAt(Tile::Coordinate(xCoordinate, yCoordinate));
59             if (!currentTile)
60                 continue;
61             currentTile->invalidate(dirtyRect);
62         }
63     }
64
65     startTileBufferUpdateTimer();
66 }
67
68 void TiledBackingStore::updateTileBuffers()
69 {
70     if (m_contentsFrozen)
71         return;
72    
73     Vector<IntRect> paintedArea;
74     Vector<RefPtr<Tile> > dirtyTiles;
75     TileMap::iterator end = m_tiles.end();
76     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
77         if (!it->second->isDirty())
78             continue;
79         dirtyTiles.append(it->second);
80         // FIXME: should not request system repaint for the full tile.
81         paintedArea.append(mapToContents(it->second->rect()));
82     }
83    
84     if (dirtyTiles.isEmpty())
85         return;
86    
87     m_client->tiledBackingStorePaintBegin();
88
89     // FIXME: In single threaded case, tile back buffers could be updated asynchronously
90     // one by one and then swapped to front in one go. This would minimize the time spent
91     // blocking on tile updates.
92     unsigned size = dirtyTiles.size();
93     for (unsigned n = 0; n < size; ++n)
94         dirtyTiles[n]->updateBackBuffer();
95
96     for (unsigned n = 0; n < size; ++n)
97         dirtyTiles[n]->swapBackBufferToFront();
98
99     m_client->tiledBackingStorePaintEnd(paintedArea);
100 }
101
102 void TiledBackingStore::paint(GraphicsContext* context, const IntRect& rect)
103 {
104     context->save();
105    
106     // Assumes the backing store is painted with the scale transform applied.
107     // Since tile content is already scaled, first revert the scaling from the painter.
108     context->scale(FloatSize(1.f / m_contentsScale, 1.f / m_contentsScale));
109    
110     IntRect dirtyRect = mapFromContents(rect);
111    
112     Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.topLeft());
113     Tile::Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.bottomRight());
114
115     for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
116         for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
117             Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
118             RefPtr<Tile> currentTile = tileAt(currentCoordinate);
119             if (currentTile && currentTile->isReadyToPaint())
120                 currentTile->paint(context, dirtyRect);
121             else {
122                 FloatRect tileRect = tileRectForCoordinate(currentCoordinate);
123                 FloatRect target = intersection(tileRect, FloatRect(rect));
124                 Tile::paintCheckerPattern(context, target);
125             }
126         }
127     }
128     context->restore();
129 }
130
131 void TiledBackingStore::viewportChanged(const IntRect& contentsViewport)
132 {
133     IntRect viewport = mapFromContents(contentsViewport);
134     if (m_viewport == viewport)
135         return;
136
137     m_viewport = viewport;
138
139     startTileCreationTimer();
140 }
141
142 void TiledBackingStore::setContentsScale(float scale)
143 {
144     if (m_contentsScale == scale)
145         return;
146     m_contentsScale = scale;
147
148     invalidate(m_client->tiledBackingStoreContentsRect());
149 }
150    
151 double TiledBackingStore::tileDistance(const IntRect& viewport, const Tile::Coordinate& tileCoordinate)
152 {
153     if (viewport.intersects(tileRectForCoordinate(tileCoordinate)))
154         return 0;
155    
156     IntPoint viewCenter = viewport.location() + IntSize(viewport.width() / 2, viewport.height() / 2);
157     Tile::Coordinate centerCoordinate = tileCoordinateForPoint(viewCenter);
158    
159     // Manhattan distance, biased so that vertical distances are shorter.
160     const double horizontalBias = 1.3;
161     return abs(centerCoordinate.y() - tileCoordinate.y()) + horizontalBias * abs(centerCoordinate.x() - tileCoordinate.x());
162 }
163
164 void TiledBackingStore::createTiles()
165 {
166     if (m_contentsFrozen)
167         return;
168
169     if (m_viewport.isEmpty())
170         return;
171
172     // Remove tiles that extend outside the current contents rect.
173     dropOverhangingTiles();
174
175     // FIXME: Make configurable/adapt to memory.
176     IntRect keepRect = m_viewport;
177     keepRect.inflateX(m_viewport.width());
178     keepRect.inflateY(3 * m_viewport.height());
179     keepRect.intersect(contentsRect());
180    
181     dropTilesOutsideRect(keepRect);
182    
183     IntRect coverRect = m_viewport;
184     coverRect.inflateX(m_viewport.width() / 2);
185     coverRect.inflateY(2 * m_viewport.height());
186     coverRect.intersect(contentsRect());
187    
188     // Search for the tile position closest to the viewport center that does not yet contain a tile.
189     // Which position is considered the closest depends on the tileDistance function.
190     double shortestDistance = std::numeric_limits<double>::infinity();
191     Vector<Tile::Coordinate> tilesToCreate;
192     unsigned requiredTileCount = 0;
193     Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.topLeft());
194     Tile::Coordinate bottomRight = tileCoordinateForPoint(coverRect.bottomRight());
195     for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
196         for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
197             Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
198             if (tileAt(currentCoordinate))
199                 continue;
200             ++requiredTileCount;
201             // Distance is 0 for all currently visible tiles.
202             double distance = tileDistance(m_viewport, currentCoordinate);
203             if (distance > shortestDistance)
204                 continue;
205             if (distance < shortestDistance) {
206                 tilesToCreate.clear();
207                 shortestDistance = distance;
208             }
209             tilesToCreate.append(currentCoordinate);
210         }
211     }
212    
213     // Now construct the tile(s)
214     unsigned tilesToCreateCount = tilesToCreate.size();
215     for (unsigned n = 0; n < tilesToCreateCount; ++n) {
216         Tile::Coordinate coordinate = tilesToCreate[n];
217         setTile(coordinate, Tile::create(this, coordinate));
218     }
219     requiredTileCount -= tilesToCreateCount;
220    
221     // Paint the content of the newly created tiles
222     if (tilesToCreateCount)
223         updateTileBuffers();
224
225     // Keep creating tiles until the whole coverRect is covered.
226     if (requiredTileCount)
227         m_tileCreationTimer->startOneShot(0);
228 }
229
230 void TiledBackingStore::dropOverhangingTiles()
231 {    
232     IntRect contentsRect = this->contentsRect();
233
234     Vector<Tile::Coordinate> tilesToRemove;
235     TileMap::iterator end = m_tiles.end();
236     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
237         Tile::Coordinate tileCoordinate = it->second->coordinate();
238         IntRect tileRect = it->second->rect();
239         IntRect expectedTileRect = tileRectForCoordinate(tileCoordinate);
240         if (expectedTileRect != tileRect || !contentsRect.contains(tileRect))
241             tilesToRemove.append(tileCoordinate);
242     }
243     unsigned removeCount = tilesToRemove.size();
244     for (unsigned n = 0; n < removeCount; ++n)
245         removeTile(tilesToRemove[n]);
246 }
247
248 void TiledBackingStore::dropTilesOutsideRect(const IntRect& keepRect)
249 {
250     FloatRect keepRectF = keepRect;
251
252     Vector<Tile::Coordinate> toRemove;
253     TileMap::iterator end = m_tiles.end();
254     for (TileMap::iterator it = m_tiles.begin(); it != end; ++it) {
255         Tile::Coordinate coordinate = it->second->coordinate();
256         FloatRect tileRect = it->second->rect();
257         if (!tileRect.intersects(keepRectF))
258             toRemove.append(coordinate);
259     }
260     unsigned removeCount = toRemove.size();
261     for (unsigned n = 0; n < removeCount; ++n)
262         removeTile(toRemove[n]);
263 }
264
265 PassRefPtr<Tile> TiledBackingStore::tileAt(const Tile::Coordinate& coordinate) const
266 {
267     return m_tiles.get(coordinate);
268 }
269
270 void TiledBackingStore::setTile(const Tile::Coordinate& coordinate, PassRefPtr<Tile> tile)
271 {
272     m_tiles.set(coordinate, tile);
273 }
274
275 void TiledBackingStore::removeTile(const Tile::Coordinate& coordinate)
276 {
277     m_tiles.remove(coordinate);
278 }
279
280 IntRect TiledBackingStore::mapToContents(const IntRect& rect) const
281 {
282     return enclosingIntRect(FloatRect(rect.x() / m_contentsScale,
283         rect.y() / m_contentsScale,
284         rect.width() / m_contentsScale,
285         rect.height() / m_contentsScale));
286 }
287
288 IntRect TiledBackingStore::mapFromContents(const IntRect& rect) const
289 {
290     return enclosingIntRect(FloatRect(rect.x() * m_contentsScale,
291         rect.y() * m_contentsScale,
292         rect.width() * m_contentsScale,
293         rect.height() * m_contentsScale));
294 }
295
296 IntRect TiledBackingStore::contentsRect() const
297 {
298     return mapFromContents(m_client->tiledBackingStoreContentsRect());
299 }
300
301 IntRect TiledBackingStore::tileRectForCoordinate(const Tile::Coordinate& coordinate) const
302 {
303     IntRect rect(coordinate.x() * m_tileSize.width(),
304         coordinate.y() * m_tileSize.height(),
305         m_tileSize.width(),
306         m_tileSize.height());
307
308     rect.intersect(contentsRect());
309     return rect;
310 }
311    
312 Tile::Coordinate TiledBackingStore::tileCoordinateForPoint(const IntPoint& point) const
313 {
314     int x = point.x() / m_tileSize.width();
315     int y = point.y() / m_tileSize.height();
316     return Tile::Coordinate(std::max(x, 0), std::max(y, 0));
317 }
318
319
320 void TiledBackingStore::startTileBufferUpdateTimer()
321 {
322     if (m_tileBufferUpdateTimer->isActive() || m_contentsFrozen)
323         return;
324     m_tileBufferUpdateTimer->startOneShot(0);
325 }
326
327 void TiledBackingStore::tileBufferUpdateTimerFired(TileTimer*)
328 {
329     updateTileBuffers();
330 }
331
332 void TiledBackingStore::startTileCreationTimer()
333 {
334     if (m_tileCreationTimer->isActive() || m_contentsFrozen)
335         return;
336     m_tileCreationTimer->startOneShot(0);
337 }
338
339 void TiledBackingStore::tileCreationTimerFired(TileTimer*)
340 {
341     createTiles();
342 }
343
344 void TiledBackingStore::setContentsFrozen(bool freeze)
345 {
346     if (m_contentsFrozen == freeze)
347         return;
348
349     m_contentsFrozen = freeze;
350
351     // Restart the timers. There might be pending invalidations that
352     // were not painted or created because tiles are not created or
353     // painted when in frozen state.
354     if (!m_contentsFrozen) {
355         startTileCreationTimer();
356         startTileBufferUpdateTimer();
357     }
358     // stopping is handled when the timers fire
359 }
360
361 }
362
363 #endif
364
Note: See TracBrowser for help on using the browser.