OSDN Git Service

Merge "Fix repaint when we toggle the inverted mode - add a boolean return in WebView...
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / TiledPage.cpp
1 /*
2  * Copyright 2010, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TiledPage.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "GLUtils.h"
32 #include "IntRect.h"
33 #include "PaintTileOperation.h"
34 #include "TilesManager.h"
35
36 #ifdef DEBUG
37
38 #include <cutils/log.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/text/CString.h>
41
42 #undef XLOG
43 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
44
45 #else
46
47 #undef XLOG
48 #define XLOG(...)
49
50 #endif // DEBUG
51
52 namespace WebCore {
53
54 using namespace android;
55
56 TiledPage::TiledPage(int id, GLWebViewState* state)
57     : m_baseTiles(0)
58     , m_baseTileSize(0)
59     , m_id(id)
60     , m_scale(1)
61     , m_invScale(1)
62     , m_glWebViewState(state)
63     , m_latestPictureInval(0)
64     , m_prepare(false)
65 {
66     m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
67 #ifdef DEBUG_COUNT
68     ClassTracker::instance()->increment("TiledPage");
69 #endif
70 }
71
72 void TiledPage::updateBaseTileSize()
73 {
74     // This value must be at least 1 greater than the max number of allowed
75     // textures. This is because prepare() asks for a tile before it reserves
76     // a texture for that tile. If all textures are currently in use by the
77     // page then there will be no available tile and having the extra tile
78     // ensures that this does not happen. After claiming the extra tile the call
79     // to reserveTexture() will cause some other tile in the page to lose it's
80     // texture and become available, thus ensuring that we always have at least
81     // one tile that is available.
82     int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
83     if (baseTileSize > m_baseTileSize)
84         m_baseTileSize = baseTileSize;
85 }
86
87 TiledPage::~TiledPage()
88 {
89     // In order to delete the page we must ensure that none of its BaseTiles are
90     // currently painting or scheduled to be painted by the TextureGenerator
91     TilesManager::instance()->removeOperationsForPage(this);
92     delete[] m_baseTiles;
93 #ifdef DEBUG_COUNT
94     ClassTracker::instance()->decrement("TiledPage");
95 #endif
96 }
97
98 BaseTile* TiledPage::getBaseTile(int x, int y) const
99 {
100     for (int j = 0; j < m_baseTileSize; j++) {
101         BaseTile& tile = m_baseTiles[j];
102         if (tile.x() == x && tile.y() == y && !tile.isAvailable())
103             return &tile;
104     }
105     return 0;
106 }
107
108 void TiledPage::setUsable(bool usable)
109 {
110     for (int j = 0; j < m_baseTileSize; j++) {
111         BaseTile& tile = m_baseTiles[j];
112         tile.setUsable(usable);
113     }
114     return;
115 }
116
117 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
118 {
119     // Given the current scale level we need to mark the appropriate tiles as dirty
120     const float invTileContentWidth = m_scale / TilesManager::tileWidth();
121     const float invTileContentHeight = m_scale / TilesManager::tileHeight();
122
123     const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
124     const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
125     const int lastDirtyTileX = static_cast<int>(ceilf(inval.maxX() * invTileContentWidth));
126     const int lastDirtyTileY = static_cast<int>(ceilf(inval.maxY() * invTileContentHeight));
127
128     XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
129     // We defer marking the tile as dirty until the next time we need to prepare
130     // to draw.
131     m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
132     m_invalTilesRegion.op(inval.x(), inval.y(), inval.maxX(), inval.maxY(), SkRegion::kUnion_Op);
133     m_latestPictureInval = pictureCount;
134 }
135
136 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
137 {
138     if (y < 0)
139         return;
140
141     for (int i = 0; i < tilesInRow; i++) {
142         int x = firstTileX;
143
144         // If we are goingLeft, we want to schedule the tiles starting from the
145         // right (and to the left if not). This is because tiles are appended to
146         // the list and the texture uploader goes through the set front to back.
147         if (goingLeft)
148             x += (tilesInRow - 1) - i;
149         else
150             x += i;
151
152         if (x < 0)
153             continue;
154
155         BaseTile* currentTile = 0;
156         BaseTile* availableTile = 0;
157         for (int j = 0; j < m_baseTileSize; j++) {
158             BaseTile& tile = m_baseTiles[j];
159             if (tile.x() == x && tile.y() == y) {
160                 currentTile = &tile;
161                 break;
162             }
163             if (!availableTile && tile.isAvailable())
164                 availableTile = &tile;
165         }
166
167         if (!currentTile && availableTile) {
168             currentTile = availableTile;
169         }
170
171         if (currentTile) {
172             currentTile->setGLWebViewState(m_glWebViewState);
173             currentTile->setContents(this, x, y, m_scale);
174             currentTile->setPage(this);
175
176             // ensure there is a texture associated with the tile and then check to
177             // see if the texture is dirty and in need of repainting
178             currentTile->reserveTexture();
179             updateTileUsedLevel(tileBounds, *currentTile);
180             if (currentTile->isDirty() && !currentTile->isRepaintPending()) {
181                 PaintTileOperation *operation = new PaintTileOperation(currentTile);
182                 TilesManager::instance()->scheduleOperation(operation);
183             } else if (currentTile->isDirty()) {
184                 XLOG("Tile %dx%d is dirty, but awaiting repaint", currentTile->x(), currentTile->y());
185             }
186         }
187     }
188 }
189
190 void TiledPage::updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile)
191 {
192     const int lastTileX = tileBounds.fRight - 1;
193     const int lastTileY = tileBounds.fBottom - 1;
194
195     // set the used level of the tile (e.g. distance from the viewport)
196     int dx = 0;
197     int dy = 0;
198
199     if (tileBounds.fLeft > tile.x())
200         dx = tileBounds.fLeft - tile.x();
201     else if (lastTileX < tile.x())
202         dx = tile.x() - lastTileX;
203
204     if (tileBounds.fTop > tile.y())
205         dy = tileBounds.fTop - tile.y();
206     else if (lastTileY < tile.y())
207         dy = tile.y() - lastTileY;
208
209     int d = std::max(dx, dy);
210
211     tile.setUsedLevel(d);
212 }
213
214 void TiledPage::updateTileState(const SkIRect& tileBounds)
215 {
216     if (!m_glWebViewState || tileBounds.isEmpty()) {
217         m_invalRegion.setEmpty();
218         m_invalTilesRegion.setEmpty();
219         return;
220     }
221
222     for (int x = 0; x < m_baseTileSize; x++) {
223
224         BaseTile& tile = m_baseTiles[x];
225
226         // if the tile no longer has a texture then proceed to the next tile
227         if (tile.isAvailable())
228             continue;
229
230         // if the tile is in the dirty region then we must invalidate it
231         if (m_invalRegion.contains(tile.x(), tile.y()))
232             tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
233
234         updateTileUsedLevel(tileBounds, tile);
235     }
236
237     // clear the invalidated region as all tiles within that region have now
238     // been marked as dirty.
239     m_invalRegion.setEmpty();
240     m_invalTilesRegion.setEmpty();
241 }
242
243 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds)
244 {
245     if (!m_glWebViewState)
246         return;
247
248     TilesManager::instance()->gatherTextures();
249     // update the tiles distance from the viewport
250     updateTileState(tileBounds);
251     m_prepare = true;
252     m_scrollingDown = goingDown;
253
254     int firstTileX = tileBounds.fLeft;
255     int firstTileY = tileBounds.fTop;
256     int nbTilesWidth = tileBounds.width();
257     int nbTilesHeight = tileBounds.height();
258
259     int lastTileX = tileBounds.fRight - 1;
260     int lastTileY = tileBounds.fBottom - 1;
261
262     // Expand number of tiles to allow tiles outside of viewport to be prepared for
263     // smoother scrolling.
264     int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
265     int nMaxTilesPerPage = m_baseTileSize / 2;
266     int expandX = m_glWebViewState->expandedTileBoundsX();
267     int expandY = m_glWebViewState->expandedTileBoundsY();
268
269     firstTileX -= expandX;
270     lastTileX += expandX;
271     nbTilesWidth += expandX * 2;
272
273     firstTileY -= expandY;
274     lastTileY += expandY;
275     nbTilesHeight += expandY * 2;
276
277     m_expandedTileBounds.fLeft = firstTileX;
278     m_expandedTileBounds.fTop = firstTileY;
279     m_expandedTileBounds.fRight = lastTileX;
280     m_expandedTileBounds.fBottom = lastTileY;
281
282     for (int i = 0; i < nbTilesHeight; i++)
283         prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
284 }
285
286 bool TiledPage::ready(const SkIRect& tileBounds, float scale)
287 {
288     if (!m_glWebViewState)
289         return false;
290
291     if (!m_invalRegion.isEmpty() && !m_prepare)
292         return false;
293
294     if (m_scale != scale)
295         return false;
296
297     for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
298         for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
299             BaseTile* t = getBaseTile(x, y);
300             if (!t || !t->isTileReady())
301                 return false;
302         }
303     }
304     m_prepare = false;
305     return true;
306 }
307
308 void TiledPage::draw(float transparency, const SkIRect& tileBounds)
309 {
310     if (!m_glWebViewState)
311         return;
312
313     const float tileWidth = TilesManager::tileWidth() * m_invScale;
314     const float tileHeight = TilesManager::tileHeight() * m_invScale;
315
316     SkIRect actualTileBounds = tileBounds;
317     actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
318     actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
319     actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
320     actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();
321
322     for (int j = 0; j < m_baseTileSize; j++) {
323         BaseTile& tile = m_baseTiles[j];
324         bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
325         if (tileInView) {
326
327             SkRect rect;
328             rect.fLeft = tile.x() * tileWidth;
329             rect.fTop = tile.y() * tileHeight;
330             rect.fRight = rect.fLeft + tileWidth;
331             rect.fBottom = rect.fTop + tileHeight;
332
333             tile.draw(transparency, rect, m_scale);
334         }
335
336         TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
337     }
338 }
339
340 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
341 {
342     if (!m_glWebViewState)
343         return false;
344
345     *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
346     return true;
347 }
348
349 void TiledPage::paintExtra(SkCanvas* canvas)
350 {
351 }
352
353 TiledPage* TiledPage::sibling()
354 {
355     if (!m_glWebViewState)
356         return 0;
357     return m_glWebViewState->sibling(this);
358 }
359
360 } // namespace WebCore
361
362 #endif // USE(ACCELERATED_COMPOSITING)