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