package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.util.FloatMath;
+import android.support.v4.util.LongSparseArray;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
import com.android.gallery3d.app.GalleryContext;
-import com.android.gallery3d.common.ApiHelper;
-import com.android.gallery3d.common.LongSparseArray;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DecodeUtils;
+import com.android.photos.data.GalleryBitmapPool;
+import com.android.gallery3d.glrenderer.GLCanvas;
+import com.android.gallery3d.glrenderer.UploadedTexture;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.ThreadPool;
import com.android.gallery3d.util.ThreadPool.CancelListener;
@SuppressWarnings("unused")
private static final String TAG = "TileImageView";
-
- // TILE_SIZE must be 2^N - 2. We put one pixel border in each side of the
- // texture to avoid seams between tiles.
- private static final int TILE_SIZE = 254;
- private static final int TILE_BORDER = 1;
- private static final int BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2;
private static final int UPLOAD_LIMIT = 1;
- private static final BitmapPool sTilePool =
- ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER
- ? new BitmapPool(BITMAP_SIZE, BITMAP_SIZE, 128)
- : null;
+ // TILE_SIZE must be 2^N
+ private static int sTileSize;
/*
* This is the tile state in the CPU side.
private static final int STATE_RECYCLING = 0x20;
private static final int STATE_RECYCLED = 0x40;
- private Model mModel;
+ private TileSource mModel;
private ScreenNail mScreenNail;
protected int mLevelCount; // cache the value of mScaledBitmaps.length
private final ThreadPool mThreadPool;
private boolean mBackgroundTileUploaded;
- public static interface Model {
+ public static interface TileSource {
public int getLevelCount();
public ScreenNail getScreenNail();
public int getImageWidth();
// The tile returned by this method can be specified this way: Assuming
// the image size is (width, height), first take the intersection of (0,
- // 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). Then
- // extend this intersection region by borderSize pixels on each side. If
+ // 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). If
// in extending the region, we found some part of the region are outside
// the image, those pixels are filled with black.
//
// still refers to the coordinate on the original image.
//
// The method would be called in another thread.
- public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize, BitmapPool pool);
+ public Bitmap getTile(int level, int x, int y, int tileSize);
+ }
+
+ public static boolean isHighResolution(Context context) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ WindowManager wm = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getMetrics(metrics);
+ return metrics.heightPixels > 2048 || metrics.widthPixels > 2048;
}
public TileImageView(GalleryContext context) {
mThreadPool = context.getThreadPool();
mTileDecoder = mThreadPool.submit(new TileDecoder());
+ if (sTileSize == 0) {
+ if (isHighResolution(context.getAndroidContext())) {
+ sTileSize = 512 ;
+ } else {
+ sTileSize = 256;
+ }
+ }
}
- public void setModel(Model model) {
+ public void setModel(TileSource model) {
mModel = model;
if (model != null) notifyModelInvalidated();
}
}
for (int i = fromLevel; i < endLevel; ++i) {
- int size = TILE_SIZE << i;
+ int size = sTileSize << i;
Rect r = range[i - fromLevel];
for (int y = r.top, bottom = r.bottom; y < bottom; y += size) {
for (int x = r.left, right = r.right; x < right; x += size) {
protected synchronized void invalidateTiles() {
mDecodeQueue.clean();
mUploadQueue.clean();
+
// TODO disable decoder
int n = mActiveTiles.size();
for (int i = 0; i < n; i++) {
int height = (int) Math.ceil(Math.max(
Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h)));
- int left = (int) FloatMath.floor(cX - width / (2f * scale));
- int top = (int) FloatMath.floor(cY - height / (2f * scale));
- int right = (int) FloatMath.ceil(left + width / scale);
- int bottom = (int) FloatMath.ceil(top + height / scale);
+ int left = (int) Math.floor(cX - width / (2f * scale));
+ int top = (int) Math.floor(cY - height / (2f * scale));
+ int right = (int) Math.ceil(left + width / scale);
+ int bottom = (int) Math.ceil(top + height / scale);
// align the rectangle to tile boundary
- int size = TILE_SIZE << level;
+ int size = sTileSize << level;
left = Math.max(0, size * (left / size));
top = Math.max(0, size * (top / size));
right = Math.min(mImageWidth, right);
}
}
setScreenNail(null);
- if (sTilePool != null) sTilePool.clear();
}
public void prepareTextures() {
mScreenNail.noDraw();
}
- int size = (TILE_SIZE << level);
+ int size = (sTileSize << level);
float length = size * mScale;
Rect r = mTileRange;
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
private void uploadBackgroundTiles(GLCanvas canvas) {
if (tile.mTileState == STATE_RECYCLING) {
tile.mTileState = STATE_RECYCLED;
if (tile.mDecodedTile != null) {
- if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+ GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
tile.mDecodedTile = null;
}
mRecycledQueue.push(tile);
}
tile.mTileState = STATE_RECYCLED;
if (tile.mDecodedTile != null) {
- if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+ GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
tile.mDecodedTile = null;
}
mRecycledQueue.push(tile);
@Override
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
- if (renderRequested) return false;
+ // Skips uploading if there is a pending rendering request.
+ // Returns true to keep uploading in next rendering loop.
+ if (renderRequested) return true;
int quota = UPLOAD_LIMIT;
- Tile tile;
- while (true) {
+ Tile tile = null;
+ while (quota > 0) {
synchronized (TileImageView.this) {
tile = mUploadQueue.pop();
}
- if (tile == null || quota <= 0) break;
+ if (tile == null) break;
if (!tile.isContentValid()) {
+ boolean hasBeenLoaded = tile.isLoaded();
Utils.assertTrue(tile.mTileState == STATE_DECODED);
tile.updateContent(canvas);
+ if (!hasBeenLoaded) tile.draw(canvas, 0, 0);
--quota;
}
}
- mActive.set(tile != null);
+ if (tile == null) mActive.set(false);
return tile != null;
}
}
RectF source = mSourceRect;
RectF target = mTargetRect;
target.set(x, y, x + length, y + length);
- source.set(0, 0, TILE_SIZE, TILE_SIZE);
+ source.set(0, 0, sTileSize, sTileSize);
Tile tile = getTile(tx, ty, level);
if (tile != null) {
if (drawTile(tile, canvas, source, target)) return;
}
if (mScreenNail != null) {
- int size = TILE_SIZE << level;
+ int size = sTileSize << level;
float scaleX = (float) mScreenNail.getWidth() / mImageWidth;
float scaleY = (float) mScreenNail.getHeight() / mImageHeight;
source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX,
}
}
- // TODO: avoid drawing the unused part of the textures.
static boolean drawTile(
Tile tile, GLCanvas canvas, RectF source, RectF target) {
while (true) {
if (tile.isContentValid()) {
- // offset source rectangle for the texture border.
- source.offset(TILE_BORDER, TILE_BORDER);
canvas.drawTexture(tile, source, target);
return true;
}
source.left /= 2f;
source.right /= 2f;
} else {
- source.left = (TILE_SIZE + source.left) / 2f;
- source.right = (TILE_SIZE + source.right) / 2f;
+ source.left = (sTileSize + source.left) / 2f;
+ source.right = (sTileSize + source.right) / 2f;
}
if (tile.mY == parent.mY) {
source.top /= 2f;
source.bottom /= 2f;
} else {
- source.top = (TILE_SIZE + source.top) / 2f;
- source.bottom = (TILE_SIZE + source.bottom) / 2f;
+ source.top = (sTileSize + source.top) / 2f;
+ source.bottom = (sTileSize + source.bottom) / 2f;
}
tile = parent;
}
@Override
protected void onFreeBitmap(Bitmap bitmap) {
- if (sTilePool != null) sTilePool.recycle(bitmap);
+ GalleryBitmapPool.getInstance().put(bitmap);
}
boolean decode() {
// by (1 << mTilelevel) from a region in the original image.
try {
mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
- mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER, sTilePool));
+ mTileLevel, mX, mY, sTileSize));
} catch (Throwable t) {
Log.w(TAG, "fail to decode tile", t);
}
// We need to override the width and height, so that we won't
// draw beyond the boundaries.
- int rightEdge = ((mImageWidth - mX) >> mTileLevel) + TILE_BORDER;
- int bottomEdge = ((mImageHeight - mY) >> mTileLevel) + TILE_BORDER;
- setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge));
+ int rightEdge = ((mImageWidth - mX) >> mTileLevel);
+ int bottomEdge = ((mImageHeight - mY) >> mTileLevel);
+ setSize(Math.min(sTileSize, rightEdge), Math.min(sTileSize, bottomEdge));
Bitmap bitmap = mDecodedTile;
mDecodedTile = null;
// boundary).
@Override
public int getTextureWidth() {
- return TILE_SIZE + TILE_BORDER * 2;
+ return sTileSize;
}
@Override
public int getTextureHeight() {
- return TILE_SIZE + TILE_BORDER * 2;
+ return sTileSize;
}
public void update(int x, int y, int level) {
public Tile getParentTile() {
if (mTileLevel + 1 == mLevelCount) return null;
- int size = TILE_SIZE << (mTileLevel + 1);
+ int size = sTileSize << (mTileLevel + 1);
int x = size * (mX / size);
int y = size * (mY / size);
return getTile(x, y, mTileLevel + 1);
@Override
public String toString() {
return String.format("tile(%s, %s, %s / %s)",
- mX / TILE_SIZE, mY / TILE_SIZE, mLevel, mLevelCount);
+ mX / sTileSize, mY / sTileSize, mLevel, mLevelCount);
}
}