summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/graphics/android/TileGrid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/android/TileGrid.cpp')
-rw-r--r--Source/WebCore/platform/graphics/android/TileGrid.cpp371
1 files changed, 371 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/android/TileGrid.cpp b/Source/WebCore/platform/graphics/android/TileGrid.cpp
new file mode 100644
index 0000000..19a1815
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/TileGrid.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "TileGrid"
+#define LOG_NDEBUG 1
+
+#include "config.h"
+#include "TileGrid.h"
+
+#include "AndroidLog.h"
+#include "GLWebViewState.h"
+#include "PaintTileOperation.h"
+#include "Tile.h"
+#include "TilesManager.h"
+
+#include <wtf/CurrentTime.h>
+
+#define EXPANDED_BOUNDS_INFLATE 1
+#define EXPANDED_PREFETCH_BOUNDS_Y_INFLATE 1
+
+namespace WebCore {
+
+TileGrid::TileGrid(bool isBaseSurface)
+ : m_prevTileY(0)
+ , m_scale(1)
+ , m_isBaseSurface(isBaseSurface)
+{
+ m_dirtyRegion.setEmpty();
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->increment("TileGrid");
+#endif
+}
+
+TileGrid::~TileGrid()
+{
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("TileGrid");
+#endif
+ removeTiles();
+}
+
+bool TileGrid::isReady()
+{
+ bool tilesAllReady = true;
+ bool tilesVisible = false;
+ for (unsigned int i = 0; i < m_tiles.size(); i++) {
+ Tile* tile = m_tiles[i];
+ if (tile->isTileVisible(m_area)) {
+ tilesVisible = true;
+ if (!tile->isTileReady()) {
+ tilesAllReady = false;
+ break;
+ }
+ }
+ }
+ // 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
+ ALOGV("TT %p, ready %d, visible %d, texturesRemain %d",
+ this, tilesAllReady, tilesVisible,
+ TilesManager::instance()->layerTexturesRemain());
+
+ return !TilesManager::instance()->layerTexturesRemain()
+ || !tilesVisible || tilesAllReady;
+}
+
+bool TileGrid::isMissingContent()
+{
+ for (unsigned int i = 0; i < m_tiles.size(); i++)
+ if (m_tiles[i]->isTileVisible(m_area) && !m_tiles[i]->frontTexture())
+ return true;
+ return false;
+}
+
+void TileGrid::swapTiles()
+{
+ int swaps = 0;
+ for (unsigned int i = 0; i < m_tiles.size(); i++)
+ if (m_tiles[i]->swapTexturesIfNeeded())
+ swaps++;
+ ALOGV("TT %p swapping, swaps = %d", this, swaps);
+}
+
+IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale)
+{
+ IntRect computedArea;
+ IntRect area(contentArea.x() * scale,
+ contentArea.y() * scale,
+ ceilf(contentArea.width() * scale),
+ ceilf(contentArea.height() * scale));
+
+ ALOGV("TT %p prepare, scale %f, area %d x %d", this, scale, area.width(), area.height());
+
+ if (area.width() == 0 && area.height() == 0) {
+ computedArea.setWidth(0);
+ computedArea.setHeight(0);
+ return computedArea;
+ }
+
+ int tileWidth = TilesManager::tileWidth();
+ int tileHeight = TilesManager::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;
+ computedArea.setWidth(ceilf(right) - computedArea.x());
+ computedArea.setHeight(ceilf(bottom) - computedArea.y());
+ return computedArea;
+}
+
+void TileGrid::prepareGL(GLWebViewState* state, float scale,
+ const IntRect& prepareArea, const IntRect& unclippedArea,
+ TilePainter* painter, bool isLowResPrefetch, bool useExpandPrefetch)
+{
+ // first, how many tiles do we need
+ m_area = computeTilesArea(prepareArea, scale);
+ if (m_area.isEmpty())
+ return;
+
+ ALOGV("prepare TileGrid %p with scale %.2f, prepareArea "
+ " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles",
+ this, scale,
+ prepareArea.x(), prepareArea.y(),
+ prepareArea.width(), prepareArea.height(),
+ m_area.x(), m_area.y(),
+ m_area.width(), m_area.height());
+
+ bool goingDown = m_prevTileY < m_area.y();
+ m_prevTileY = m_area.y();
+
+ if (scale != m_scale)
+ TilesManager::instance()->removeOperationsForFilter(new ScaleFilter(painter, m_scale));
+
+ m_scale = scale;
+
+ // apply dirty region to affected tiles
+ if (!m_dirtyRegion.isEmpty()) {
+ for (unsigned int i = 0; i < m_tiles.size(); i++)
+ m_tiles[i]->markAsDirty(m_dirtyRegion);
+
+ // log inval region for the base surface
+ if (m_isBaseSurface && TilesManager::instance()->getProfiler()->enabled()) {
+ SkRegion::Iterator iterator(m_dirtyRegion);
+ while (!iterator.done()) {
+ SkIRect r = iterator.rect();
+ TilesManager::instance()->getProfiler()->nextInval(r, scale);
+ iterator.next();
+ }
+ }
+ m_dirtyRegion.setEmpty();
+ }
+
+ // prepare standard bounds (clearing ExpandPrefetch flag)
+ for (int i = 0; i < m_area.width(); i++) {
+ if (goingDown) {
+ for (int j = 0; j < m_area.height(); j++)
+ prepareTile(m_area.x() + i, m_area.y() + j,
+ painter, state, isLowResPrefetch, false);
+ } else {
+ for (int j = m_area.height() - 1; j >= 0; j--)
+ prepareTile(m_area.x() + i, m_area.y() + j,
+ painter, state, isLowResPrefetch, false);
+ }
+ }
+
+ // prepare expanded bounds
+ if (useExpandPrefetch) {
+ IntRect fullArea = computeTilesArea(unclippedArea, scale);
+ IntRect expandedArea = m_area;
+ expandedArea.inflate(EXPANDED_BOUNDS_INFLATE);
+
+ if (isLowResPrefetch)
+ expandedArea.inflate(EXPANDED_PREFETCH_BOUNDS_Y_INFLATE);
+
+ // clip painting area to content
+ expandedArea.intersect(fullArea);
+
+ for (int i = expandedArea.x(); i < expandedArea.maxX(); i++)
+ for (int j = expandedArea.y(); j < expandedArea.maxY(); j++)
+ if (!m_area.contains(i, j))
+ prepareTile(i, j, painter, state, isLowResPrefetch, true);
+ }
+}
+
+void TileGrid::markAsDirty(const SkRegion& invalRegion)
+{
+ ALOGV("TT %p markAsDirty, current region empty %d, new empty %d",
+ this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty());
+ m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
+}
+
+void TileGrid::prepareTile(int x, int y, TilePainter* painter,
+ GLWebViewState* state, bool isLowResPrefetch, bool isExpandPrefetch)
+{
+ Tile* tile = getTile(x, y);
+ if (!tile) {
+ bool isLayerTile = !m_isBaseSurface;
+ tile = new Tile(isLayerTile);
+ m_tiles.append(tile);
+ }
+
+ ALOGV("preparing tile %p at %d, %d, painter is %p", tile, x, y, painter);
+
+ tile->setContents(x, y, m_scale, isExpandPrefetch);
+
+ // TODO: move below (which is largely the same for layers / tiled page) into
+ // prepareGL() function
+
+ if (tile->isDirty() || !tile->frontTexture())
+ tile->reserveTexture();
+
+ if (tile->backTexture() && tile->isDirty() && !tile->isRepaintPending()) {
+ ALOGV("painting TT %p's tile %d %d for LG %p", this, x, y, painter);
+ PaintTileOperation *operation = new PaintTileOperation(tile, painter,
+ state, isLowResPrefetch);
+ TilesManager::instance()->scheduleOperation(operation);
+ }
+}
+
+Tile* TileGrid::getTile(int x, int y)
+{
+ for (unsigned int i = 0; i <m_tiles.size(); i++) {
+ Tile* tile = m_tiles[i];
+ if (tile->x() == x && tile->y() == y)
+ return tile;
+ }
+ return 0;
+}
+
+int TileGrid::nbTextures(IntRect& area, float scale)
+{
+ IntRect tileBounds = computeTilesArea(area, scale);
+ int numberTextures = tileBounds.width() * tileBounds.height();
+
+ // add the number of dirty tiles in the bounds, as they take up double
+ // textures for double buffering
+ for (unsigned int i = 0; i <m_tiles.size(); i++) {
+ Tile* tile = m_tiles[i];
+ if (tile->isDirty()
+ && tile->x() >= tileBounds.x() && tile->x() <= tileBounds.maxX()
+ && tile->y() >= tileBounds.y() && tile->y() <= tileBounds.maxY())
+ numberTextures++;
+ }
+ return numberTextures;
+}
+
+void TileGrid::drawGL(const IntRect& visibleArea, float opacity,
+ const TransformationMatrix* transform,
+ const Color* background)
+{
+ m_area = computeTilesArea(visibleArea, m_scale);
+ if (m_area.width() == 0 || m_area.height() == 0)
+ return;
+
+ float invScale = 1 / m_scale;
+ const float tileWidth = TilesManager::tileWidth() * invScale;
+ const float tileHeight = TilesManager::tileHeight() * invScale;
+
+ int drawn = 0;
+
+ SkRegion missingRegion;
+ bool translucentBaseSurface =
+ background ? (background->hasAlpha() && background->alpha() > 0) : false;
+ if (translucentBaseSurface) {
+ SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(),
+ m_area.width(), m_area.height());
+ missingRegion = SkRegion(totalArea);
+ }
+
+ for (unsigned int i = 0; i < m_tiles.size(); i++) {
+ Tile* tile = m_tiles[i];
+
+ bool tileInView = tile->isTileVisible(m_area);
+ if (tileInView) {
+ SkRect rect;
+ rect.fLeft = tile->x() * tileWidth;
+ rect.fTop = tile->y() * tileHeight;
+ rect.fRight = rect.fLeft + tileWidth;
+ rect.fBottom = rect.fTop + tileHeight;
+ ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
+ tile, tile->isLayerTile(), tile->x(), tile->y(),
+ tile->scale(), m_scale, tile->isTileReady(), tile->isDirty());
+
+ bool success = tile->drawGL(opacity, rect, m_scale, transform);
+ if (translucentBaseSurface && success) {
+ // Cut the successful drawn tile area from the missing region.
+ missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1),
+ SkRegion::kDifference_Op);
+ }
+ if (tile->frontTexture())
+ drawn++;
+ }
+
+ if (translucentBaseSurface)
+ TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView);
+ }
+
+ // Draw missing Regions with blend turned on
+ if (translucentBaseSurface)
+ drawMissingRegion(missingRegion, opacity, background);
+
+ ALOGV("TT %p drew %d tiles, scale %f",
+ this, drawn, m_scale);
+}
+
+void TileGrid::drawMissingRegion(const SkRegion& region, float opacity,
+ const Color* background)
+{
+ SkRegion::Iterator iterator(region);
+ const float tileWidth = TilesManager::tileWidth() / m_scale;
+ const float tileHeight = TilesManager::tileHeight() / m_scale;
+ ShaderProgram* shader = TilesManager::instance()->shader();
+ while (!iterator.done()) {
+ SkIRect r = iterator.rect();
+ SkRect rect;
+ rect.fLeft = r.x() * tileWidth;
+ rect.fTop = r.y() * tileHeight;
+ rect.fRight = rect.fLeft + tileWidth * r.width();
+ rect.fBottom = rect.fTop + tileHeight * r.height();
+ ALOGV("draw tile x y, %d %d (%d %d) opacity %f", r.x(), r.y(),
+ r.width(), r.height(), opacity);
+ // Skia is using pre-multiplied color.
+ Color postAlpha = Color(background->red() * background->alpha() / 255,
+ background->green() * background->alpha() / 255,
+ background->blue() * background->alpha() / 255,
+ background->alpha() );
+ shader->drawQuad(rect, 0, opacity, postAlpha);
+ iterator.next();
+ }
+}
+
+void TileGrid::removeTiles()
+{
+ for (unsigned int i = 0; i < m_tiles.size(); i++) {
+ delete m_tiles[i];
+ }
+ m_tiles.clear();
+}
+
+void TileGrid::discardTextures()
+{
+ ALOGV("TT %p discarding textures", this);
+ for (unsigned int i = 0; i < m_tiles.size(); i++)
+ m_tiles[i]->discardTextures();
+}
+
+} // namespace WebCore