summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java')
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java290
1 files changed, 290 insertions, 0 deletions
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java b/android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
new file mode 100644
index 000000000..bdef70221
--- /dev/null
+++ b/android/source/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java
@@ -0,0 +1,290 @@
+package org.mozilla.gecko.gfx;
+
+import android.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.util.Log;
+
+import org.libreoffice.LOKitShell;
+import org.libreoffice.TileIdentifier;
+import org.mozilla.gecko.util.FloatUtils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public abstract class ComposedTileLayer extends Layer implements ComponentCallbacks2 {
+ private static final String LOGTAG = ComposedTileLayer.class.getSimpleName();
+
+ protected final List<SubTile> tiles = new ArrayList<SubTile>();
+
+ protected final IntSize tileSize;
+ private final ReadWriteLock tilesReadWriteLock = new ReentrantReadWriteLock();
+ private final Lock tilesReadLock = tilesReadWriteLock.readLock();
+ private final Lock tilesWriteLock = tilesReadWriteLock.writeLock();
+
+ protected RectF currentViewport = new RectF();
+ protected float currentZoom = 1.0f;
+ protected RectF currentPageRect = new RectF();
+
+ private long reevaluationNanoTime = 0;
+
+ public ComposedTileLayer(Context context) {
+ context.registerComponentCallbacks(this);
+ this.tileSize = new IntSize(256, 256);
+ }
+
+ protected static RectF roundToTileSize(RectF input, IntSize tileSize) {
+ float minX = ((int) (input.left / tileSize.width)) * tileSize.width;
+ float minY = ((int) (input.top / tileSize.height)) * tileSize.height;
+ float maxX = ((int) (input.right / tileSize.width) + 1) * tileSize.width;
+ float maxY = ((int) (input.bottom / tileSize.height) + 1) * tileSize.height;
+ return new RectF(minX, minY, maxX, maxY);
+ }
+
+ protected static RectF inflate(RectF rect, IntSize inflateSize) {
+ RectF newRect = new RectF(rect);
+ newRect.left -= inflateSize.width;
+ newRect.left = newRect.left < 0.0f ? 0.0f : newRect.left;
+
+ newRect.top -= inflateSize.height;
+ newRect.top = newRect.top < 0.0f ? 0.0f : newRect.top;
+
+ newRect.right += inflateSize.width;
+ newRect.bottom += inflateSize.height;
+
+ return newRect;
+ }
+
+ protected static RectF normalizeRect(RectF rect, float sourceFactor, float targetFactor) {
+ return new RectF(
+ (rect.left / sourceFactor) * targetFactor,
+ (rect.top / sourceFactor) * targetFactor,
+ (rect.right / sourceFactor) * targetFactor,
+ (rect.bottom / sourceFactor) * targetFactor);
+ }
+
+ public void invalidate() {
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ tile.invalidate();
+ }
+ tilesReadLock.unlock();
+ }
+
+ @Override
+ public void beginTransaction() {
+ super.beginTransaction();
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ tile.beginTransaction();
+ }
+ tilesReadLock.unlock();
+ }
+
+ @Override
+ public void endTransaction() {
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ tile.endTransaction();
+ }
+ tilesReadLock.unlock();
+ super.endTransaction();
+ }
+
+ @Override
+ public void draw(RenderContext context) {
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ if (RectF.intersects(tile.getBounds(context), context.viewport)) {
+ tile.draw(context);
+ }
+ }
+ tilesReadLock.unlock();
+ }
+
+ @Override
+ protected void performUpdates(RenderContext context) {
+ super.performUpdates(context);
+
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ tile.beginTransaction();
+ tile.refreshTileMetrics();
+ tile.endTransaction();
+ tile.performUpdates(context);
+ }
+ tilesReadLock.unlock();
+ }
+
+ @Override
+ public Region getValidRegion(RenderContext context) {
+ Region validRegion = new Region();
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ validRegion.op(tile.getValidRegion(context), Region.Op.UNION);
+ }
+ tilesReadLock.unlock();
+ return validRegion;
+ }
+
+ @Override
+ public void setResolution(float newResolution) {
+ super.setResolution(newResolution);
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ tile.setResolution(newResolution);
+ }
+ tilesReadLock.unlock();
+ }
+
+ public void reevaluateTiles(ImmutableViewportMetrics viewportMetrics, DisplayPortMetrics mDisplayPort) {
+ RectF newViewPort = getViewPort(viewportMetrics);
+ float newZoom = getZoom(viewportMetrics);
+
+ // When
+ if (newZoom <= 0.0 || Float.isNaN(newZoom)) {
+ return;
+ }
+
+ if (currentViewport.equals(newViewPort) && FloatUtils.fuzzyEquals(currentZoom, newZoom)) {
+ return;
+ }
+
+ long currentReevaluationNanoTime = System.nanoTime();
+ if ((currentReevaluationNanoTime - reevaluationNanoTime) < 25 * 1000000) {
+ return;
+ }
+
+ reevaluationNanoTime = currentReevaluationNanoTime;
+
+ currentViewport = newViewPort;
+ currentZoom = newZoom;
+ currentPageRect = viewportMetrics.getPageRect();
+
+ LOKitShell.sendTileReevaluationRequest(this);
+ }
+
+ protected abstract RectF getViewPort(ImmutableViewportMetrics viewportMetrics);
+
+ protected abstract float getZoom(ImmutableViewportMetrics viewportMetrics);
+
+ protected abstract int getTilePriority();
+
+ private boolean containsTilesMatching(float x, float y, float currentZoom) {
+ tilesReadLock.lock();
+ try {
+ for (SubTile tile : tiles) {
+ if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ tilesReadLock.unlock();
+ }
+ }
+
+ public void addNewTiles(List<SubTile> newTiles) {
+ for (float y = currentViewport.top; y < currentViewport.bottom; y += tileSize.height) {
+ if (y > currentPageRect.height()) {
+ continue;
+ }
+ for (float x = currentViewport.left; x < currentViewport.right; x += tileSize.width) {
+ if (x > currentPageRect.width()) {
+ continue;
+ }
+ if (!containsTilesMatching(x, y, currentZoom)) {
+ TileIdentifier tileId = new TileIdentifier((int) x, (int) y, currentZoom, tileSize);
+ SubTile tile = createNewTile(tileId);
+ newTiles.add(tile);
+ }
+ }
+ }
+ }
+
+ public void clearMarkedTiles() {
+ tilesWriteLock.lock();
+ Iterator<SubTile> iterator = tiles.iterator();
+ while (iterator.hasNext()) {
+ SubTile tile = iterator.next();
+ if (tile.markedForRemoval) {
+ tile.destroy();
+ iterator.remove();
+ }
+ }
+ tilesWriteLock.unlock();
+ }
+
+ public void markTiles() {
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ if (FloatUtils.fuzzyEquals(tile.id.zoom, currentZoom)) {
+ RectF tileRect = tile.id.getRectF();
+ if (!RectF.intersects(currentViewport, tileRect)) {
+ tile.markForRemoval();
+ }
+ } else {
+ tile.markForRemoval();
+ }
+ }
+ tilesReadLock.unlock();
+ }
+
+ public void clearAndReset() {
+ tilesWriteLock.lock();
+ tiles.clear();
+ tilesWriteLock.unlock();
+ currentViewport = new RectF();
+ }
+
+ private SubTile createNewTile(TileIdentifier tileId) {
+ SubTile tile = new SubTile(tileId);
+ tile.beginTransaction();
+ tilesWriteLock.lock();
+ tiles.add(tile);
+ tilesWriteLock.unlock();
+ return tile;
+ }
+
+ public boolean isStillValid(TileIdentifier tileId) {
+ return RectF.intersects(currentViewport, tileId.getRectF()) || currentViewport.contains(tileId.getRectF());
+ }
+
+ /**
+ * Invalidate tiles which intersect the input rect
+ */
+ public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF cssRect) {
+ RectF zoomedRect = RectUtils.scale(cssRect, currentZoom);
+ tilesReadLock.lock();
+ for (SubTile tile : tiles) {
+ if (!tile.markedForRemoval && RectF.intersects(zoomedRect, tile.id.getRectF())) {
+ tilesToInvalidate.add(tile);
+ }
+ }
+ tilesReadLock.unlock();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ @Override
+ public void onLowMemory() {
+ Log.i(LOGTAG, "onLowMemory");
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ if (level >= 15 /*TRIM_MEMORY_RUNNING_CRITICAL*/) {
+ Log.i(LOGTAG, "Trimming memory - TRIM_MEMORY_RUNNING_CRITICAL");
+ } else if (level >= 10 /*TRIM_MEMORY_RUNNING_LOW*/) {
+ Log.i(LOGTAG, "Trimming memory - TRIM_MEMORY_RUNNING_LOW");
+ }
+ }
+}