Commit d5c316b5 authored by Mehran Mahmoudi's avatar Mehran Mahmoudi Committed by Commit Bot

[Paint Preview] Support resizing the viewport in PlayerFrameMediator

This fixes a problem where only the initial call to
PlayerFrameMediator#setLayoutDimension was respected. We need to resize
the view port when a call to this method is made, without altering
the bitmap matrix.

Bug: 1079352
Change-Id: I7fd64988028dfb0d9e170abbef5306fbb228d683
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2188211
Commit-Queue: Mehran Mahmoudi <mahmoudi@chromium.org>
Reviewed-by: default avatarCalder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#766496}
parent e6e5bf58
......@@ -15,6 +15,8 @@ import javax.annotation.Nonnull;
* on a {@link Canvas}.
*/
class PlayerFrameBitmapPainter {
private int mTileWidth;
private int mTileHeight;
private Bitmap[][] mBitmapMatrix;
private Rect mViewPort = new Rect();
private Rect mDrawBitmapSrc = new Rect();
......@@ -25,6 +27,11 @@ class PlayerFrameBitmapPainter {
mInvalidateCallback = invalidateCallback;
}
void updateTileDimensions(int[] tileDimensions) {
mTileWidth = tileDimensions[0];
mTileHeight = tileDimensions[1];
}
void updateViewPort(int left, int top, int right, int bottom) {
mViewPort.set(left, top, right, bottom);
mInvalidateCallback.run();
......@@ -39,20 +46,16 @@ class PlayerFrameBitmapPainter {
* Draws bitmaps on a given {@link Canvas} for the current viewport.
*/
void onDraw(Canvas canvas) {
if (mBitmapMatrix == null) {
return;
}
if (mBitmapMatrix == null) return;
if (mViewPort.isEmpty()) {
return;
}
if (mViewPort.isEmpty()) return;
if (mTileWidth <= 0 || mTileHeight <= 0) return;
final int tileHeight = mViewPort.height();
final int tileWidth = mViewPort.width();
final int rowStart = mViewPort.top / tileHeight;
final int rowEnd = (int) Math.ceil((double) mViewPort.bottom / tileHeight);
final int colStart = mViewPort.left / tileWidth;
final int colEnd = (int) Math.ceil((double) mViewPort.right / tileWidth);
final int rowStart = mViewPort.top / mTileHeight;
final int rowEnd = (int) Math.ceil((double) mViewPort.bottom / mTileHeight);
final int colStart = mViewPort.left / mTileWidth;
final int colEnd = (int) Math.ceil((double) mViewPort.right / mTileWidth);
if (rowEnd > mBitmapMatrix.length || colEnd > mBitmapMatrix[rowEnd - 1].length) {
return;
}
......@@ -65,17 +68,17 @@ class PlayerFrameBitmapPainter {
}
// Calculate the portion of this tileBitmap that is visible in mViewPort.
int bitmapLeft = Math.max(mViewPort.left - (col * tileWidth), 0);
int bitmapTop = Math.max(mViewPort.top - (row * tileHeight), 0);
int bitmapLeft = Math.max(mViewPort.left - (col * mTileWidth), 0);
int bitmapTop = Math.max(mViewPort.top - (row * mTileHeight), 0);
int bitmapRight =
Math.min(tileWidth, bitmapLeft + mViewPort.right - (col * tileWidth));
Math.min(mTileWidth, bitmapLeft + mViewPort.right - (col * mTileWidth));
int bitmapBottom =
Math.min(tileHeight, bitmapTop + mViewPort.bottom - (row * tileHeight));
Math.min(mTileHeight, bitmapTop + mViewPort.bottom - (row * mTileHeight));
mDrawBitmapSrc.set(bitmapLeft, bitmapTop, bitmapRight, bitmapBottom);
// Calculate the portion of the canvas that tileBitmap is gonna be drawn on.
int canvasLeft = Math.max((col * tileWidth) - mViewPort.left, 0);
int canvasTop = Math.max((row * tileHeight) - mViewPort.top, 0);
int canvasLeft = Math.max((col * mTileWidth) - mViewPort.left, 0);
int canvasTop = Math.max((row * mTileHeight) - mViewPort.top, 0);
int canvasRight = canvasLeft + mDrawBitmapSrc.width();
int canvasBottom = canvasTop + mDrawBitmapSrc.height();
mDrawBitmapDst.set(canvasLeft, canvasTop, canvasRight, canvasBottom);
......
......@@ -57,6 +57,8 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
private final Rect mViewportRect = new Rect();
/** Rect used for requesting a new bitmap from Paint Preview compositor. */
private final Rect mBitmapRequestRect = new Rect();
/** Dimension of tiles for each scale factor. */
private final Map<Float, int[]> mTileDimensions = new HashMap<>();
/**
* A scale factor cache of matrices of bitmaps that make up the content of this frame at a
* given scale factor.
......@@ -96,39 +98,42 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
@Override
public void setLayoutDimensions(int width, int height) {
// Set initial scale so that content width fits within the layout dimensions.
float scaleFactor = ((float) width) / ((float) mContentWidth);
initializeViewPort(width, height, scaleFactor);
float initialScaleFactor = ((float) width) / ((float) mContentWidth);
updateViewportSize(width, height, mScaleFactor == 0f ? initialScaleFactor : mScaleFactor);
}
void initializeViewPort(int width, int height, float scaleFactor) {
// If the dimensions of mViewportRect has been set, we don't need to do anything.
if (!mViewportRect.isEmpty() || width <= 0 || height <= 0) return;
void updateViewportSize(int width, int height, float scaleFactor) {
if (width <= 0 || height <= 0) return;
mViewportRect.set(0, 0, width, height);
updateViewport(0, 0, scaleFactor);
mViewportRect.set(mViewportRect.left, mViewportRect.top, mViewportRect.left + width,
mViewportRect.top + height);
moveViewport(0, 0, scaleFactor);
}
/**
* Called when either the view port of the scale factor should be changed. Updates the view port
* Called when the view port is moved or the scale factor is changed. Updates the view port
* and requests bitmap tiles for portion of the view port that don't have bitmap tiles.
* @param distanceX The horizontal distance that the view port should be moved by.
* @param distanceY The vertical distance that the view port should be moved by.
* @param scaleFactor The new scale factor.
*/
private void updateViewport(int distanceX, int distanceY, float scaleFactor) {
private void moveViewport(int distanceX, int distanceY, float scaleFactor) {
// Initialize the bitmap matrix for this scale factor if we haven't already.
int[] tileDimensions = mTileDimensions.get(scaleFactor);
Bitmap[][] bitmapMatrix = mBitmapMatrix.get(scaleFactor);
boolean[][] pendingBitmapRequests = mPendingBitmapRequests.get(scaleFactor);
boolean[][] requiredBitmaps = mRequiredBitmaps.get(scaleFactor);
if (bitmapMatrix == null) {
// Each tile is as big as the view port. Here we determine the number of columns and
// rows for the current scale factor.
// Each tile is as big as the initial view port. Here we determine the number of
// columns and rows for the current scale factor.
int rows = (int) Math.ceil((mContentHeight * scaleFactor) / mViewportRect.height());
int cols = (int) Math.ceil((mContentWidth * scaleFactor) / mViewportRect.width());
tileDimensions = new int[] {mViewportRect.width(), mViewportRect.height()};
bitmapMatrix = new Bitmap[rows][cols];
mBitmapMatrix.put(scaleFactor, bitmapMatrix);
pendingBitmapRequests = new boolean[rows][cols];
requiredBitmaps = new boolean[rows][cols];
mTileDimensions.put(scaleFactor, tileDimensions);
mBitmapMatrix.put(scaleFactor, bitmapMatrix);
mPendingBitmapRequests.put(scaleFactor, pendingBitmapRequests);
mRequiredBitmaps.put(scaleFactor, requiredBitmaps);
}
......@@ -142,6 +147,7 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
// Update mViewportRect and let the view know. PropertyModelChangeProcessor is smart about
// this and will only update the view if mViewportRect is actually changed.
mViewportRect.offset(distanceX, distanceY);
mModel.set(PlayerFrameProperties.TILE_DIMENSIONS, tileDimensions);
mModel.set(PlayerFrameProperties.VIEWPORT, mViewportRect);
// Clear the required bitmaps matrix. It will be updated in #requestBitmapForTile.
......@@ -152,8 +158,8 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
}
// Request bitmaps for tiles inside the view port that don't already have a bitmap.
final int tileWidth = mViewportRect.width();
final int tileHeight = mViewportRect.height();
final int tileWidth = tileDimensions[0];
final int tileHeight = tileDimensions[1];
final int colStart = mViewportRect.left / tileWidth;
final int colEnd = (int) Math.ceil((double) mViewportRect.right / tileWidth);
final int rowStart = mViewportRect.top / tileHeight;
......@@ -251,7 +257,7 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
/**
* Called on scroll events from the user. Checks if scrolling is possible, and if so, calls
* {@link #updateViewport}.
* {@link #moveViewport}.
* @param distanceX Horizontal scroll distance in pixels.
* @param distanceY Vertical scroll distance in pixels.
* @return Whether the scrolling was possible and view port was updated.
......@@ -281,7 +287,7 @@ class PlayerFrameMediator implements PlayerFrameViewDelegate {
if (validDistanceX == 0 && validDistanceY == 0) return false;
updateViewport(validDistanceX, validDistanceY, mScaleFactor);
moveViewport(validDistanceX, validDistanceY, mScaleFactor);
return true;
}
......
......@@ -21,6 +21,9 @@ class PlayerFrameProperties {
/** A matrix of bitmap tiles that collectively make the entire content. */
static final PropertyModel.WritableObjectPropertyKey<Bitmap[][]> BITMAP_MATRIX =
new PropertyModel.WritableObjectPropertyKey<>(true);
/** The dimensions of each bitmap tile in the current bitmap matrix. */
static final PropertyModel.WritableObjectPropertyKey<int[]> TILE_DIMENSIONS =
new PropertyModel.WritableObjectPropertyKey<>();
/**
* Contains the current user-visible content window. The view should use this to draw the
* appropriate bitmap tiles from {@link #BITMAP_MATRIX}.
......@@ -34,5 +37,6 @@ class PlayerFrameProperties {
*/
static final PropertyModel.WritableObjectPropertyKey<List<Pair<View, Rect>>> SUBFRAME_VIEWS =
new PropertyModel.WritableObjectPropertyKey<>(true);
static final PropertyKey[] ALL_KEYS = {BITMAP_MATRIX, VIEWPORT, SUBFRAME_VIEWS};
static final PropertyKey[] ALL_KEYS = {
BITMAP_MATRIX, TILE_DIMENSIONS, VIEWPORT, SUBFRAME_VIEWS};
}
......@@ -82,6 +82,10 @@ class PlayerFrameView extends FrameLayout {
mBitmapPainter.updateBitmapMatrix(bitmapMatrix);
}
void updateTileDimensions(int[] tileDimensions) {
mBitmapPainter.updateTileDimensions(tileDimensions);
}
@Override
protected void onDraw(Canvas canvas) {
mBitmapPainter.onDraw(canvas);
......
......@@ -16,6 +16,8 @@ class PlayerFrameViewBinder {
static void bind(PropertyModel model, PlayerFrameView view, PropertyKey key) {
if (key.equals(PlayerFrameProperties.BITMAP_MATRIX)) {
view.updateBitmapMatrix(model.get(PlayerFrameProperties.BITMAP_MATRIX));
} else if (key.equals(PlayerFrameProperties.TILE_DIMENSIONS)) {
view.updateTileDimensions(model.get(PlayerFrameProperties.TILE_DIMENSIONS));
} else if (key.equals(PlayerFrameProperties.VIEWPORT)) {
Rect viewPort = model.get(PlayerFrameProperties.VIEWPORT);
view.updateViewPort(viewPort.left, viewPort.top, viewPort.right, viewPort.bottom);
......
......@@ -99,6 +99,7 @@ public class PlayerFrameBitmapPainterTest {
PlayerFrameBitmapPainter painter =
new PlayerFrameBitmapPainter(Mockito.mock(Runnable.class));
painter.updateBitmapMatrix(generateMockBitmapMatrix(2, 3));
painter.updateTileDimensions(new int[] {10, -5});
painter.updateViewPort(0, 5, 10, -10);
MockCanvas canvas = new MockCanvas();
......@@ -106,6 +107,7 @@ public class PlayerFrameBitmapPainterTest {
canvas.assertNumberOfBitmapDraws(0);
// Update the view port so it is covered by 2 bitmap tiles.
painter.updateTileDimensions(new int[] {10, 10});
painter.updateViewPort(0, 5, 10, 15);
painter.onDraw(canvas);
canvas.assertNumberOfBitmapDraws(2);
......@@ -121,6 +123,7 @@ public class PlayerFrameBitmapPainterTest {
painter.updateBitmapMatrix(new Bitmap[0][0]);
// This view port is covered by 2 bitmap tiles, so there should be 2 draw operations on
// the canvas.
painter.updateTileDimensions(new int[] {10, 10});
painter.updateViewPort(0, 5, 10, 15);
MockCanvas canvas = new MockCanvas();
......@@ -153,6 +156,7 @@ public class PlayerFrameBitmapPainterTest {
bitmaps[1][1] = bitmap11;
painter.updateBitmapMatrix(bitmaps);
painter.updateTileDimensions(new int[] {10, 15});
painter.updateViewPort(5, 10, 15, 25);
// Make sure the invalidator was called after updating the bitmap matrix and the view port.
......
......@@ -206,7 +206,7 @@ public class PlayerFrameMediatorTest {
@Test
public void testBitmapRequest() {
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
// Requests for bitmaps in all tiles that are visible in the view port as well as their
// adjacent tiles should've been made.
......@@ -323,7 +323,7 @@ public class PlayerFrameMediatorTest {
@Test
public void testRequiredBitmapMatrix() {
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
boolean[][] expectedRequiredBitmaps = new boolean[6][6];
......@@ -448,7 +448,7 @@ public class PlayerFrameMediatorTest {
public void testBitmapRequestResponse() {
// Sets the bitmap tile size to 150x200 and triggers bitmap request for the upper left tile
// and its adjacent tiles.
mMediator.initializeViewPort(150, 200, 1f);
mMediator.updateViewportSize(150, 200, 1f);
// Create mock bitmaps for response.
Bitmap bitmap00 = Mockito.mock(Bitmap.class);
......@@ -511,7 +511,7 @@ public class PlayerFrameMediatorTest {
@Test
public void testViewPortOnScrollBy() {
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
Rect expectedViewPort = new Rect(0, 0, 100, 200);
// Scroll right and down by a within bounds amount. Both scroll directions should be
......@@ -594,7 +594,7 @@ public class PlayerFrameMediatorTest {
mMediator.addSubFrame(subFrame3.first, subFrame3.second);
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
List<Pair<View, Rect>> expectedVisibleViews = new ArrayList<>();
expectedVisibleViews.add(subFrame1);
expectedVisibleViews.add(subFrame2);
......@@ -623,7 +623,7 @@ public class PlayerFrameMediatorTest {
@Test
public void testViewPortOnFling() {
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
Rect expectedViewPort = new Rect(0, 0, 100, 200);
mMediator.onFling(100, 0);
......@@ -670,7 +670,7 @@ public class PlayerFrameMediatorTest {
@Test
public void testOnClick() {
// Initial view port setup.
mMediator.initializeViewPort(100, 200, 1f);
mMediator.updateViewportSize(100, 200, 1f);
List<ClickedPoint> expectedClickedPoints = new ArrayList<>();
// No scrolling has happened yet.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment