@Override public void tbsPaint(Canvas canvas, Rect rect) { canvas.translate(-rect.getLeft(), -rect.getTop()); float contentsScale = mBS.getContentsScale(); LinearGradient mLinearGradient = new LinearGradient( 0, 0, 500 * contentsScale, 500 * contentsScale, new int[] { android.graphics.Color.RED, android.graphics.Color.GREEN, android.graphics.Color.BLUE, android.graphics.Color.WHITE }, null, Shader.TileMode.REPEAT); Paint paint = new Paint(); paint.setShader(mLinearGradient); canvas.clipRect(rect.getLeft(), rect.getTop(), rect.getRight(), rect.getBottom()); canvas.drawRect( mContentsRect.getLeft() * contentsScale, mContentsRect.getTop() * contentsScale, mContentsRect.getRight() * contentsScale, mContentsRect.getBottom() * contentsScale, paint); }
public Rect mapToContents(final Rect rect) { int l = (int) (rect.getLeft() / mContentsScale); int t = (int) (rect.getTop() / mContentsScale); int r = (int) (l + rect.getWidth() / mContentsScale + 0.5f); int b = (int) (t + rect.getHeight() / mContentsScale + 0.5f); return new Rect(l, t, r - l, b - t); }
private Rect mapFromContents(final Rect rect, float contentsScale) { int l = (int) (rect.getLeft() * contentsScale); int t = (int) (rect.getTop() * contentsScale); int r = (int) (l + rect.getWidth() * contentsScale + 0.5f); int b = (int) (t + rect.getHeight() * contentsScale + 0.5f); return new Rect(l, t, r - l, b - t); }
public double tileDistance(final Rect viewport, final int tileX, final int tileY) { if (viewport.intersectsWith(tileRectForCoordinate(tileX, tileY))) return 0; Coordinate centerCoordinate = tileCoordinateForPoint(viewport.centerX(), viewport.centerY()); return Math.max( Math.abs(centerCoordinate.getY() - tileY), Math.abs(centerCoordinate.getX() - tileX)); }
public void coverWithTilesIfNeeded(BSTimerTask task) { Rect visibleRect = visibleRect(); Rect rect = mapFromContents(mClient.tbsGetContentsRect()); boolean didChange = !mTrajectoryVector.equals(mPendingTrajectoryVector) || !mVisibleRect.equals(visibleRect) || !mRect.equals(rect); if (didChange || mPendingTileCreation) createTiles(task); }
public Rect tileRectForCoordinate(final int tileX, final int tileY) { Rect rect = new Rect( tileX * mTileSize.getWidth(), tileY * mTileSize.getHeight(), mTileSize.getWidth(), mTileSize.getHeight()); rect.intersect(mRect); return rect; }
public void setSize(int width, int height) { mWidth = width; mHeight = height; float scaleW = mWidth / (float) mContentsRect.getWidth(); float scaleH = mHeight / (float) mContentsRect.getHeight(); if (scaleW > scaleH) { mBS.setContentsScale(scaleH); } else { mBS.setContentsScale(scaleW); } }
private void setKeepRect(final Rect keepRect) { // Drop tiles outside the new keepRect. RectF keepRectF = keepRect.toRectF(); List<Coordinate> toRemove = new ArrayList<Coordinate>(); ; Iterator<Entry<Coordinate, ITile>> it = mMainTiles.iterator(); while (it.hasNext()) { Entry<Coordinate, ITile> entry = it.next(); Coordinate coordinate = entry.getValue().coordinate(); RectF tileRect = entry.getValue().rect().toRectF(); if (!tileRect.intersectsWith(keepRectF)) toRemove.add(coordinate); } int removeCount = toRemove.size(); for (int n = 0; n < removeCount; ++n) removeTile(toRemove.get(n)); keepRect.copyTo(mKeepRect); }
private boolean resizeEdgeTiles() { boolean wasResized = false; List<Coordinate> tilesToRemove = new ArrayList<Coordinate>(); Iterator<Entry<Coordinate, ITile>> it = mMainTiles.iterator(); while (it.hasNext()) { Entry<Coordinate, ITile> entry = it.next(); Coordinate tileCoordinate = entry.getValue().coordinate(); Rect tileRect = entry.getValue().rect(); Rect expectedTileRect = tileRectForCoordinate(tileCoordinate); if (expectedTileRect.isEmpty()) tilesToRemove.add(tileCoordinate); else if (!expectedTileRect.equals(tileRect)) { entry.getValue().resize(expectedTileRect.getWidth(), expectedTileRect.getHeight()); wasResized = true; } } int removeCount = tilesToRemove.size(); for (int n = 0; n < removeCount; ++n) removeTile(tilesToRemove.get(n)); return wasResized; }
public RenderLayerPh_GL(TextureBuffer textureBuffer) { TiledBackingStoreBackendGL backend = new TiledBackingStoreBackendGL(); mBS = new TiledBackingStore(this, backend, textureBuffer); backend.init(mBS); mContentsRect.setRect(0, 0, 4000, 3000); mBS.setContentsFrozen(true); setPosition(200, 200); setSize(1000, 1000); }
private float coverageRatio(final Rect contentsRect) { Rect dirtyRect = mapFromContents(contentsRect); float rectArea = dirtyRect.getWidth() * dirtyRect.getHeight(); float coverArea = 0.0f; Coordinate topLeft = tileCoordinateForPoint(dirtyRect.getLeft(), dirtyRect.getTop()); Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.getRight(), dirtyRect.getBottom()); for (int yCoordinate = topLeft.getY(); yCoordinate <= bottomRight.getY(); ++yCoordinate) { for (int xCoordinate = topLeft.getX(); xCoordinate <= bottomRight.getX(); ++xCoordinate) { ITile currentTile = getTileAt(xCoordinate, yCoordinate); if (currentTile != null && currentTile.isReadyToPaint()) { Rect coverRect = Rect.intersect(dirtyRect, currentTile.rect()); coverArea += coverRect.getWidth() * coverRect.getHeight(); } } } return coverArea / rectArea; }
private void setCoverRect(final Rect rt) { rt.copyTo(mCoverRect); }
private void computeCoverAndKeepRect(final Rect visibleRect, Rect coverRect, Rect keepRect) { visibleRect.copyTo(coverRect); visibleRect.copyTo(keepRect); // If we cover more that the actual viewport we can be smart about which tiles we choose to // render. if (mCoverAreaMultiplier > 1) { // The initial cover area covers equally in each direction, according to the // coverAreaMultiplier. coverRect.inflate( (int) (visibleRect.getWidth() * (mCoverAreaMultiplier - 1) / 2), (int) (visibleRect.getHeight() * (mCoverAreaMultiplier - 1) / 2)); coverRect.copyTo(keepRect); if (mPendingTrajectoryVector.getX() != 0 || mPendingTrajectoryVector.getY() != 0) { // A null trajectory vector (no motion) means that tiles for the coverArea will be created. // A non-null trajectory vector will shrink the covered rect to visibleRect plus its // expansion from its // center toward the cover area edges in the direction of the given vector. // E.g. if visibleRect == (10,10)5x5 and coverAreaMultiplier == 3.0: // a (0,0) trajectory vector will create tiles intersecting (5,5)15x15, // a (1,0) trajectory vector will create tiles intersecting (10,10)10x5, // and a (1,1) trajectory vector will create tiles intersecting (10,10)10x10. // Multiply the vector by the distance to the edge of the cover area. float trajectoryVectorMultiplier = (mCoverAreaMultiplier - 1) / 2; // Unite the visible rect with a "ghost" of the visible rect moved in the direction of the // trajectory vector. visibleRect.copyTo(coverRect); coverRect.offset( (int) (coverRect.getWidth() * mTrajectoryVector.getX() * trajectoryVectorMultiplier), (int) (coverRect.getHeight() * mTrajectoryVector.getY() * trajectoryVectorMultiplier)); coverRect.composite(visibleRect); } assert (keepRect.contains(coverRect)); } adjustForContentsRect(coverRect); // The keep rect is an inflated version of the cover rect, inflated in tile dimensions. keepRect.composite(coverRect); keepRect.inflate(mTileSize.getWidth() / 2, mTileSize.getHeight() / 2); keepRect.intersect(mRect); assert (coverRect.isEmpty() || keepRect.contains(coverRect)); }
private void createTiles(BSTimerTask task) { // Guard here as as these can change before the timer fires. if (isBackingStoreUpdatesSuspended()) return; // Update our backing store geometry. final Rect previousRect = mRect.clone(); mapFromContents(mClient.tbsGetContentsRect()).copyTo(mRect); mPendingTrajectoryVector.copyTo(mTrajectoryVector); visibleRect().copyTo(mVisibleRect); if (mRect.isEmpty()) { setCoverRect(new Rect()); setKeepRect(new Rect()); return; } /* We must compute cover and keep rects using the visibleRect, instead of the rect intersecting the visibleRect with m_rect, * because TBS can be used as a backing store of GraphicsLayer and the visible rect usually does not intersect with m_rect. * In the below case, the intersecting rect is an empty. * * +---------------+ * | | * | m_rect | * | +-------|-----------------------+ * | | HERE | cover or keep | * +---------------+ rect | * | +---------+ | * | | visible | | * | | rect | | * | +---------+ | * | | * | | * +-------------------------------+ * * We must create or keep the tiles in the HERE region. */ Rect coverRect = new Rect(); Rect keepRect = new Rect(); computeCoverAndKeepRect(mVisibleRect, coverRect, keepRect); setCoverRect(coverRect); setKeepRect(keepRect); if (coverRect.isEmpty()) return; // Resize tiles at the edge in case the contents size has changed, but only do so // after having dropped tiles outside the keep rect. boolean didResizeTiles = false; if (!previousRect.equals(mRect)) didResizeTiles = resizeEdgeTiles(); // Search for the tile position closest to the viewport center that does not yet contain a tile. // Which position is considered the closest depends on the tileDistance function. double shortestDistance = Double.POSITIVE_INFINITY; List<Coordinate> tilesToCreate = new ArrayList<Coordinate>(); int requiredTileCount = 0; // Cover areas (in tiles) with minimum distance from the visible rect. If the visible rect is // not covered already it will be covered first in one go, due to the distance being 0 for tiles // inside the visible rect. Coordinate topLeft = tileCoordinateForPoint(coverRect.getLeft(), coverRect.getTop()); Coordinate bottomRight = tileCoordinateForPoint(coverRect.getRight(), coverRect.getBottom()); for (int yCoordinate = topLeft.getY(); yCoordinate <= bottomRight.getY(); ++yCoordinate) { for (int xCoordinate = topLeft.getX(); xCoordinate <= bottomRight.getX(); ++xCoordinate) { if (getTileAt(xCoordinate, yCoordinate) != null) continue; ++requiredTileCount; double distance = tileDistance(mVisibleRect, xCoordinate, yCoordinate); if (distance > shortestDistance) continue; if (distance < shortestDistance) { tilesToCreate.clear(); shortestDistance = distance; } tilesToCreate.add(new Coordinate(xCoordinate, yCoordinate)); } } // Now construct the tile(s) within the shortest distance. int tilesToCreateCount = tilesToCreate.size(); for (int n = 0; n < tilesToCreateCount; ++n) { Coordinate coordinate = tilesToCreate.get(n); setTile(coordinate, mBackend.createTile(coordinate)); } requiredTileCount -= tilesToCreateCount; // Paint the content of the newly created tiles or resized tiles. if (tilesToCreateCount != 0 || didResizeTiles) updateTileBuffers(task); // Re-call createTiles on a timer to cover the visible area with the newest shortest distance. mPendingTileCreation = requiredTileCount != 0; if (mPendingTileCreation) { if (!mCommitTileUpdatesOnIdleEventLoop) { mClient.tbsHasPendingTileCreation(); return; } Log.d( "tt", "start BSUpdate scheduleTask in createTiles func scale " + mContentsScale + " pending scale " + mPendingScale); if (task == null || !task.cancelled()) startBSUpdateTask(TILE_CREATION_DELAY_MS); } }
public void removeAllNonVisibleTiles() { Rect boundedVisibleRect = mapFromContents(Rect.intersect(mClient.tbsGetVisibleRect(), mClient.tbsGetContentsRect())); setKeepRect(boundedVisibleRect); }
private void adjustForContentsRect(Rect rect) { Rect bounds = mRect; Size candidateSize = new Size(rect.getWidth(), rect.getHeight()); rect.intersect(bounds); if (rect.getWidth() == candidateSize.getWidth() && rect.getHeight() == candidateSize.getHeight()) return; /* * In the following case, there is no intersection of the contents rect and the cover rect. * Thus the latter should not be inflated. * * +---------------+ * | m_rect | * +---------------+ * * +-------------------------------+ * | cover rect | * | +---------+ | * | | visible | | * | | rect | | * | +---------+ | * +-------------------------------+ */ if (rect.isEmpty()) return; // Try to create a cover rect of the same size as the candidate, but within content bounds. int pixelsCovered = candidateSize.getWidth() * candidateSize.getHeight(); if (rect.getWidth() < candidateSize.getWidth()) rect.inflate(0, ((pixelsCovered / rect.getWidth()) - rect.getHeight()) / 2); if (rect.getHeight() < candidateSize.getHeight()) rect.inflate(((pixelsCovered / rect.getHeight()) - rect.getWidth()) / 2, 0); rect.intersect(bounds); }
public void invalidate(final Rect contentsDirtyRect) { Rect dirtyRect = new Rect(mapFromContents(contentsDirtyRect)); Rect keepRectFitToTileSize = tileRectForCoordinate(tileCoordinateForPoint(mKeepRect.getLeft(), mKeepRect.getTop())); keepRectFitToTileSize.composite( tileRectForCoordinate(tileCoordinateForPoint(mKeepRect.getRight(), mKeepRect.getBottom()))); // Only iterate on the part of the rect that we know we might have tiles. Rect coveredDirtyRect = Rect.intersect(dirtyRect, keepRectFitToTileSize); Coordinate topLeft = tileCoordinateForPoint(coveredDirtyRect.getLeft(), coveredDirtyRect.getTop()); Coordinate bottomRight = tileCoordinateForPoint(coveredDirtyRect.getRight(), coveredDirtyRect.getBottom()); for (int yCoordinate = topLeft.getY(); yCoordinate <= bottomRight.getY(); ++yCoordinate) { for (int xCoordinate = topLeft.getX(); xCoordinate <= bottomRight.getX(); ++xCoordinate) { ITile currentTile = getTileAt(xCoordinate, yCoordinate); if (currentTile == null) continue; // Pass the full rect to each tile as coveredDirtyRect might not // contain them completely and we don't want partial tile redraws. currentTile.invalidate(dirtyRect); } } startTileBufferUpdateTask(); }
public void paint(GLCanvas canvas, int offsetX, int offsetY, final Rect rect) { synchronized (mScaleBufferTiles) { Iterator<Entry<Coordinate, ITile>> it = mScaleBufferTiles.iterator(); while (it.hasNext()) { Entry<Coordinate, ITile> entry = it.next(); ITile currentTile = entry.getValue(); if (currentTile != null && currentTile.isReadyToPaint()) { float scale = mPendingScale == 0 ? mContentsScale : mPendingScale; currentTile.paint(canvas, offsetX, offsetY, null, scale / mScaleBufferTiles.getScale()); } } } Rect dirtyRect = mapFromContents(rect, mScaleBufferTiles.getScale()); Coordinate topLeft = tileCoordinateForPoint(dirtyRect.getLeft(), dirtyRect.getTop()); Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.getRight(), dirtyRect.getBottom()); // for (int yCoordinate = topLeft.getY(); yCoordinate <= bottomRight.getY(); // ++yCoordinate) { // for (int xCoordinate = topLeft.getX(); xCoordinate <= bottomRight.getX(); // ++xCoordinate) { // Coordinate currentCoordinate = new Coordinate(xCoordinate, yCoordinate); // ITile currentTile = mScaleBufferTiles.get(currentCoordinate); // if (currentTile != null && currentTile.isReadyToPaint()) { // currentTile.paint(canvas, offsetX, offsetY, dirtyRect, mPendingScale / // mScaleBufferTiles.getScale()); // } else { // Rect tileRect = tileRectForCoordinate(currentCoordinate); // Rect target = Rect.intersect(tileRect, dirtyRect); // if (target == null || target.isEmpty()) // continue; // //// float scaleFactor = mOldScale == 0 ? 1 : mContentsScale / mOldScale; //// float left = offsetX + target.getLeft() * scaleFactor; //// float top = offsetY + target.getTop() * scaleFactor; //// float width = target.getWidth() * scaleFactor; //// float height = target.getHeight() * scaleFactor; // // canvas.drawTexture(mTextureBuffer.getCheckerTextureID(canvas.getGL(), // (int)width, (int)height), left, top, width, height); // } // // } // } dirtyRect = mapFromContents(rect); topLeft = tileCoordinateForPoint(dirtyRect.getLeft(), dirtyRect.getTop()); bottomRight = tileCoordinateForPoint(dirtyRect.getRight(), dirtyRect.getBottom()); for (int yCoordinate = topLeft.getY(); yCoordinate <= bottomRight.getY(); ++yCoordinate) { for (int xCoordinate = topLeft.getX(); xCoordinate <= bottomRight.getX(); ++xCoordinate) { Coordinate currentCoordinate = new Coordinate(xCoordinate, yCoordinate); ITile currentTile = mMainTiles.get(currentCoordinate); if (currentTile != null && currentTile.isReadyToPaint()) { float scaleFactor = mPendingScale == 0 ? 1 : mPendingScale / mContentsScale; currentTile.paint(canvas, offsetX, offsetY, dirtyRect, scaleFactor); } else { Rect tileRect = tileRectForCoordinate(currentCoordinate); Rect target = Rect.intersect(tileRect, dirtyRect); if (target == null || target.isEmpty()) continue; float scaleFactor = mPendingScale == 0 ? 1 : mPendingScale / mContentsScale; float left = offsetX + target.getLeft() * scaleFactor; float top = offsetY + target.getTop() * scaleFactor; float width = target.getWidth() * scaleFactor; float height = target.getHeight() * scaleFactor; // // canvas.drawTexture(mTextureBuffer.getCheckerTextureID(canvas.getGL(), (int)width, // (int)height), left, top, width, height); } } } }
public boolean visibleAreaIsCovered() { Rect boundedVisibleContentsRect = Rect.intersect(mClient.tbsGetVisibleRect(), mClient.tbsGetContentsRect()); return coverageRatio(boundedVisibleContentsRect) == 1.0f; }
@Override public Rect tbsGetVisibleRect() { Rect visible = new Rect(-mX, -mY, 2560, 1600); visible.intersect(new Rect(0, 0, mWidth, mHeight)); float scale = mWidth / (float) mContentsRect.getWidth(); visible.setLeft((int) (visible.getLeft() / scale)); visible.setTop((int) (visible.getTop() / scale)); visible.setWidth((int) (visible.getWidth() / scale)); visible.setHeight((int) (visible.getHeight() / scale)); return visible; }