OSDN Git Service

Swap tiled page tiles on zoom
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / TiledTexture.cpp
index 1aac44d..91d6897 100644 (file)
 #include "PaintedSurface.h"
 #include "PaintTileOperation.h"
 #include "SkCanvas.h"
-
-#ifdef DEBUG
+#include "SkPicture.h"
 
 #include <cutils/log.h>
 #include <wtf/CurrentTime.h>
 #include <wtf/text/CString.h>
 
+#undef XLOGC
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TiledTexture", __VA_ARGS__)
+
+#ifdef DEBUG
+
 #undef XLOG
 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledTexture", __VA_ARGS__)
 
 
 namespace WebCore {
 
-void TiledTexture::prepare(GLWebViewState* state, bool repaint)
+TiledTexture::~TiledTexture()
 {
-    if (!m_surface)
-        return;
-
-    if (!m_surface->layer())
-        return;
-
-    // first, how many tiles do we need
-    IntRect visibleArea = m_surface->visibleArea();
-    IntRect area(visibleArea.x() * m_surface->scale(),
-                 visibleArea.y() * m_surface->scale(),
-                 ceilf(visibleArea.width() * m_surface->scale()),
-                 ceilf(visibleArea.height() * m_surface->scale()));
+    SkSafeUnref(m_paintingPicture);
+#ifdef DEBUG_COUNT
+    ClassTracker::instance()->decrement("TiledTexture");
+#endif
+    removeTiles();
+}
 
+bool TiledTexture::ready()
+{
+    bool tilesAllReady = true;
+    bool tilesVisible = false;
     for (unsigned int i = 0; i < m_tiles.size(); i++) {
         BaseTile* tile = m_tiles[i];
-        tile->setUsedLevel(-1);
-        if (!m_dirtyRegion.isEmpty())
-            tile->markAsDirty(1, m_dirtyRegion);
+        if (tile->isTileVisible(m_area)) {
+            tilesVisible = true;
+            if (!tile->isTileReady()) {
+                tilesAllReady = false;
+                break;
+            }
+        }
     }
-    m_dirtyRegion.setEmpty();
+    // For now, if no textures are available, consider ourselves as ready
+    // in order to unblock the zooming process.
+    // FIXME: have a better system -- maybe keeping the last scale factor
+    // able to fully render everything
+    XLOG("TT %p, ready %d, visible %d, texturesRemain %d",
+         this, tilesAllReady, tilesVisible,
+         TilesManager::instance()->layerTexturesRemain());
+
+    return !TilesManager::instance()->layerTexturesRemain()
+            || !tilesVisible || tilesAllReady;
+}
+
+void TiledTexture::swapTiles()
+{
+    int swaps = 0;
+    for (unsigned int i = 0; i < m_tiles.size(); i++)
+        if (m_tiles[i]->swapTexturesIfNeeded())
+            swaps++;
+    XLOG("TT %p swapping, swaps = %d", this, swaps);
+}
+
+IntRect TiledTexture::computeTilesArea(IntRect& visibleArea, float scale)
+{
+    IntRect computedArea;
+    IntRect area(visibleArea.x() * scale,
+                 visibleArea.y() * scale,
+                 ceilf(visibleArea.width() * scale),
+                 ceilf(visibleArea.height() * scale));
+
+    XLOG("TT %p prepare, scale %f, area %d x %d", this, scale, area.width(), area.height());
 
     if (area.width() == 0 && area.height() == 0) {
-        m_area.setWidth(0);
-        m_area.setHeight(0);
-        return;
+        computedArea.setWidth(0);
+        computedArea.setHeight(0);
+        return computedArea;
     }
 
     int tileWidth = TilesManager::instance()->layerTileWidth();
     int tileHeight = TilesManager::instance()->layerTileHeight();
 
-    m_area.setX(area.x() / tileWidth);
-    m_area.setY(area.y() / tileHeight);
+    computedArea.setX(area.x() / tileWidth);
+    computedArea.setY(area.y() / tileHeight);
     float right = (area.x() + area.width()) / (float) tileWidth;
     float bottom = (area.y() + area.height()) / (float) tileHeight;
-    m_area.setWidth(ceilf(right) - m_area.x());
-    m_area.setHeight(ceilf(bottom) - m_area.y());
+    computedArea.setWidth(ceilf(right) - computedArea.x());
+    computedArea.setHeight(ceilf(bottom) - computedArea.y());
+    return computedArea;
+}
 
-    XLOG("for TiledTexture %x, we have a visible area of %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles",
-         this,
+void TiledTexture::prepare(GLWebViewState* state, float scale, bool repaint,
+                           bool startFastSwap, IntRect& visibleArea)
+{
+    if (!m_surface)
+        return;
+
+    // first, how many tiles do we need
+    m_area = computeTilesArea(visibleArea, scale);
+    if (m_area.isEmpty())
+        return;
+
+    XLOG("for TiledTexture %p, we prepare with scale %.2f, have a visible area of "
+         " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles",
+         this, scale,
          visibleArea.x(), visibleArea.y(),
          visibleArea.width(), visibleArea.height(),
          m_area.x(), m_area.y(),
@@ -100,10 +149,19 @@ void TiledTexture::prepare(GLWebViewState* state, bool repaint)
     bool goingDown = m_prevTileY < m_area.y();
     m_prevTileY = m_area.y();
 
-    if (m_surface->scale() != m_prevScale)
-        TilesManager::instance()->removeOperationsForFilter(new ScaleFilter(m_surface->scale()));
+    if (scale != m_scale)
+        TilesManager::instance()->removeOperationsForFilter(new ScaleFilter(this, scale));
+
+    m_scale = scale;
 
-    m_prevScale = m_surface->scale();
+    // apply dirty region to affected tiles
+    if (!m_dirtyRegion.isEmpty()) {
+        for (unsigned int i = 0; i < m_tiles.size(); i++) {
+            // TODO: don't mark all tiles dirty
+            m_tiles[i]->markAsDirty(1, m_dirtyRegion);
+        }
+    }
+    m_dirtyRegion.setEmpty();
 
     for (int i = 0; i < m_area.width(); i++) {
         if (goingDown) {
@@ -118,9 +176,16 @@ void TiledTexture::prepare(GLWebViewState* state, bool repaint)
     }
 }
 
-void TiledTexture::markAsDirty(const SkRegion& dirtyArea)
+void TiledTexture::update(const SkRegion& invalRegion, SkPicture* picture)
 {
-    m_dirtyRegion.op(dirtyArea, SkRegion::kUnion_Op);
+    XLOG("TT %p update, current region empty %d, new empty %d, painting picture %p",
+          this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty(), picture);
+    m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
+
+    android::Mutex::Autolock lock(m_paintingPictureSync);
+    SkSafeRef(picture);
+    SkSafeUnref(m_paintingPicture);
+    m_paintingPicture = picture;
 }
 
 void TiledTexture::prepareTile(bool repaint, int x, int y)
@@ -130,19 +195,17 @@ void TiledTexture::prepareTile(bool repaint, int x, int y)
         tile = new BaseTile(true);
         m_tiles.append(tile);
     }
-    tile->reserveTexture();
-    if (!tile->texture())
-        return;
 
-    tile->setContents(this, x, y, m_surface->scale());
-    tile->setUsedLevel(0);
+    XLOG("preparing tile %p at %d, %d, painter is this %p", tile, x, y, this);
+    tile->setContents(this, x, y, m_scale);
 
-    bool schedule = false;
-    if (tile->isDirty())
-        schedule = true;
+    // TODO: move below (which is largely the same for layers / tiled page) into
+    // prepare() function
 
-    LayerAndroid* layer = m_surface->layer();
-    if (schedule && layer && !tile->isRepaintPending()) {
+    if (tile->isDirty() || !tile->frontTexture())
+        tile->reserveTexture();
+    bool hasPicture = m_paintingPicture != 0; // safely read on UI thread, since only UI thread writes
+    if (tile->backTexture() && tile->isDirty() && !tile->isRepaintPending() && hasPicture) {
         PaintTileOperation *operation = new PaintTileOperation(tile, m_surface);
         TilesManager::instance()->scheduleOperation(operation);
     }
@@ -158,76 +221,80 @@ BaseTile* TiledTexture::getTile(int x, int y)
     return 0;
 }
 
+int TiledTexture::nbTextures(IntRect& area, float scale)
+{
+    IntRect computedTilesArea = computeTilesArea(area, scale);
+    return computedTilesArea.width() * computedTilesArea.height();
+}
+
 bool TiledTexture::draw()
 {
+    XLOG("TT %p draw", this);
+
 #ifdef DEBUG
     TilesManager::instance()->getTilesTracker()->trackLayer();
 #endif
 
-    bool askRedraw = false;
     if (m_area.width() == 0 || m_area.height() == 0)
-        return askRedraw;
+        return false;
 
 #ifdef DEBUG
     TilesManager::instance()->getTilesTracker()->trackVisibleLayer();
 #endif
 
-    float m_invScale = 1 / m_surface->scale();
+    float m_invScale = 1 / m_scale;
     const float tileWidth = TilesManager::layerTileWidth() * m_invScale;
     const float tileHeight = TilesManager::layerTileHeight() * m_invScale;
-    XLOG("draw tile %x, tiles %d", this, m_tiles.size());
-    for (unsigned int i = 0; i <m_tiles.size(); i++) {
+
+    bool askRedraw = false;
+    for (unsigned int i = 0; i < m_tiles.size(); i++) {
         BaseTile* tile = m_tiles[i];
-        if (tile->x() >= m_area.x()
-            && tile->x() < m_area.x() + m_area.width()
-            && tile->y() >= m_area.y()
-            && tile->y() < m_area.y() + m_area.height()) {
+
+        if (tile->isTileVisible(m_area)) {
+            askRedraw |= !tile->isTileReady();
             SkRect rect;
             rect.fLeft = tile->x() * tileWidth;
             rect.fTop = tile->y() * tileHeight;
             rect.fRight = rect.fLeft + tileWidth;
             rect.fBottom = rect.fTop + tileHeight;
-            XLOG(" - [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f [ready: %d] dirty: %d", i, this, tile->painter(), tile, tile->x(), tile->y(), tile->scale(), tile->isTileReady(), tile->isDirty());
-            askRedraw |= !tile->isTileReady();
-            tile->draw(m_surface->opacity(), rect, m_surface->scale());
+            XLOG("- [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
+                 i, this, tile->painter(), tile, tile->x(), tile->y(),
+                 tile->scale(), m_scale, tile->isTileReady(), tile->isDirty());
+            tile->draw(m_surface->opacity(), rect, m_scale);
 #ifdef DEBUG
-            TilesManager::instance()->getTilesTracker()->track(tile->isTileReady(), tile->texture());
+            TilesManager::instance()->getTilesTracker()->track(tile->isTileReady(), tile->backTexture());
 #endif
         }
     }
+
+    // need to redraw if some visible tile wasn't ready
     return askRedraw;
 }
 
 bool TiledTexture::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
 {
-    if (!m_surface)
+    m_paintingPictureSync.lock();
+    SkPicture* picture = m_paintingPicture;
+    SkSafeRef(picture);
+    m_paintingPictureSync.unlock();
+
+    if (!picture) {
+        XLOG("TT %p COULDNT PAINT, NO PICTURE", this);
         return false;
+    }
 
-    XLOG("painting scheduled tile(%x : %d, %d, %.2f, %x) for %x",
-         tile, tile->x(), tile->y(), tile->scale(), tile->painter(), this);
-    return m_surface->paint(tile, canvas, pictureUsed);
-}
+    XLOG("TT %p painting with picture %p", this, picture);
 
-void TiledTexture::paintExtra(SkCanvas* canvas)
-{
-    m_surface->paintExtra(canvas);
-}
+    canvas->drawPicture(*picture);
 
-const TransformationMatrix* TiledTexture::transform()
-{
-    return m_surface->transform();
-}
+    SkSafeUnref(picture);
 
-void TiledTexture::beginPaint()
-{
-    if (m_surface)
-        m_surface->beginPaint();
+    return true;
 }
 
-void TiledTexture::endPaint()
+const TransformationMatrix* TiledTexture::transform()
 {
-    if (m_surface)
-        m_surface->endPaint();
+    return m_surface->transform();
 }
 
 void TiledTexture::removeTiles()
@@ -235,16 +302,108 @@ void TiledTexture::removeTiles()
     for (unsigned int i = 0; i < m_tiles.size(); i++) {
         delete m_tiles[i];
     }
+    m_tiles.clear();
+}
+
+void TiledTexture::discardTextures()
+{
+    for (unsigned int i = 0; i < m_tiles.size(); i++)
+        m_tiles[i]->discardTextures();
 }
 
 bool TiledTexture::owns(BaseTileTexture* texture)
 {
     for (unsigned int i = 0; i < m_tiles.size(); i++) {
         BaseTile* tile = m_tiles[i];
-        if (tile->texture() == texture)
+        if (tile->frontTexture() == texture)
+            return true;
+        if (tile->backTexture() == texture)
             return true;
     }
     return false;
 }
 
+DualTiledTexture::DualTiledTexture(PaintedSurface* surface)
+{
+    m_textureA = new TiledTexture(surface);
+    m_textureB = new TiledTexture(surface);
+    m_frontTexture = m_textureA;
+    m_backTexture = m_textureB;
+    m_scale = -1;
+    m_futureScale = -1;
+    m_zooming = false;
+}
+
+DualTiledTexture::~DualTiledTexture()
+{
+    delete m_textureA;
+    delete m_textureB;
+}
+
+void DualTiledTexture::prepare(GLWebViewState* state, float scale, bool repaint,
+                               bool startFastSwap, IntRect& visibleArea)
+{
+    // If we are zooming, we will use the previously used area, to prevent the
+    // frontTexture to try to allocate more tiles than what it has already
+    if (!m_zooming)
+        m_preZoomVisibleArea = visibleArea;
+
+    if (m_futureScale != scale) {
+        m_futureScale = scale;
+        m_zoomUpdateTime = WTF::currentTime() + DualTiledTexture::s_zoomUpdateDelay;
+        m_zooming = true;
+    }
+
+    XLOG("Preparing DTT %p with scale %.2f, m_scale %.2f, futureScale: %.2f, zooming: %d",
+          this, scale, m_scale, m_futureScale, m_zooming);
+
+    if (m_scale > 0)
+        m_frontTexture->prepare(state, m_scale, repaint, startFastSwap, m_preZoomVisibleArea);
+
+    // If we had a scheduled update
+    if (m_zooming && m_zoomUpdateTime < WTF::currentTime()) {
+        m_backTexture->prepare(state, m_futureScale, repaint, startFastSwap, visibleArea);
+        if (m_backTexture->ready()) {
+            m_backTexture->swapTiles();
+            swap();
+            m_zooming = false;
+        }
+    }
+}
+
+void DualTiledTexture::swap()
+{
+    m_frontTexture = m_frontTexture == m_textureA ? m_textureB : m_textureA;
+    m_backTexture = m_backTexture == m_textureA ? m_textureB : m_textureA;
+    m_scale = m_futureScale;
+    m_backTexture->discardTextures();
+}
+
+bool DualTiledTexture::draw()
+{
+    bool needsRepaint = m_frontTexture->draw();
+    needsRepaint |= m_zooming;
+    needsRepaint |= (m_scale <= 0);
+    return needsRepaint;
+}
+
+void DualTiledTexture::update(const SkRegion& dirtyArea, SkPicture* picture)
+{
+    m_backTexture->update(dirtyArea, picture);
+    m_frontTexture->update(dirtyArea, picture);
+}
+
+void DualTiledTexture::swapTiles()
+{
+    m_backTexture->swapTiles();
+    m_frontTexture->swapTiles();
+}
+
+bool DualTiledTexture::owns(BaseTileTexture* texture)
+{
+    bool owns = m_textureA->owns(texture);
+    owns |= m_textureB->owns(texture);
+    return owns;
+}
+
 } // namespace WebCore