summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/ui')
-rw-r--r--src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java2
-rw-r--r--src/com/android/gallery3d/ui/BasicTexture.java4
-rw-r--r--src/com/android/gallery3d/ui/BitmapScreenNail.java191
-rw-r--r--src/com/android/gallery3d/ui/GLCanvas.java7
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java82
-rw-r--r--src/com/android/gallery3d/ui/GLRoot.java3
-rw-r--r--src/com/android/gallery3d/ui/GLView.java39
-rw-r--r--src/com/android/gallery3d/ui/PhotoView.java25
-rw-r--r--src/com/android/gallery3d/ui/PositionController.java2
-rw-r--r--src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java2
-rw-r--r--src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java3
-rw-r--r--src/com/android/gallery3d/ui/TileImageView.java7
-rw-r--r--src/com/android/gallery3d/ui/TileImageViewAdapter.java32
-rw-r--r--src/com/android/gallery3d/ui/TiledScreenNail.java216
-rw-r--r--src/com/android/gallery3d/ui/TiledTexture.java298
15 files changed, 668 insertions, 245 deletions
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
index d5337f00d..9b3f29f2b 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
@@ -71,7 +71,7 @@ public class AlbumSetSlotRenderer extends AbstractSlotRenderer {
mWaitLoadingTexture = new ColorTexture(mPlaceholderColor);
mWaitLoadingTexture.setSize(1, 1);
mCameraOverlay = new ResourceTexture(activity,
- R.drawable.frame_overlay_gallery_camera);
+ R.drawable.ic_cameraalbum_overlay);
}
public void setPressedIndex(int index) {
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 7b8e30de4..99cf0571c 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -42,8 +42,8 @@ abstract class BasicTexture implements Texture {
protected int mWidth = UNSPECIFIED;
protected int mHeight = UNSPECIFIED;
- private int mTextureWidth;
- private int mTextureHeight;
+ protected int mTextureWidth;
+ protected int mTextureHeight;
private boolean mHasBorder;
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index bf31bcbdf..741eefbe3 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -19,209 +19,40 @@ package com.android.gallery3d.ui;
import android.graphics.Bitmap;
import android.graphics.RectF;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-import com.android.gallery3d.data.MediaItem;
-
-// This is a ScreenNail wraps a Bitmap. There are some extra functions:
-//
-// - If we need to draw before the bitmap is available, we draw a rectange of
-// placeholder color (gray).
-//
-// - When the the bitmap is available, and we have drawn the placeholder color
-// before, we will do a fade-in animation.
public class BitmapScreenNail implements ScreenNail {
- @SuppressWarnings("unused")
- private static final String TAG = "BitmapScreenNail";
-
- // The duration of the fading animation in milliseconds
- private static final int DURATION = 180;
-
- private static int sMaxSide = 640;
-
- // These are special values for mAnimationStartTime
- private static final long ANIMATION_NOT_NEEDED = -1;
- private static final long ANIMATION_NEEDED = -2;
- private static final long ANIMATION_DONE = -3;
-
- private int mWidth;
- private int mHeight;
- private Bitmap mBitmap;
- private BitmapTexture mTexture;
- private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+ private final BitmapTexture mBitmapTexture;
public BitmapScreenNail(Bitmap bitmap) {
- mWidth = bitmap.getWidth();
- mHeight = bitmap.getHeight();
- mBitmap = bitmap;
- // We create mTexture lazily, so we don't incur the cost if we don't
- // actually need it.
- }
-
- public BitmapScreenNail(int width, int height) {
- setSize(width, height);
- }
-
- // This gets overridden by bitmap_screennail_placeholder
- // in GalleryUtils.initialize
- private static int mPlaceholderColor = 0xFF222222;
- private static boolean mDrawPlaceholder = true;
-
- public static void setPlaceholderColor(int color) {
- mPlaceholderColor = color;
- }
-
- private void setSize(int width, int height) {
- if (width == 0 || height == 0) {
- width = sMaxSide;
- height = sMaxSide * 3 / 4;
- }
- float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
- mWidth = Math.round(scale * width);
- mHeight = Math.round(scale * height);
- }
-
- private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
- if (pool == null || bitmap == null) return;
- pool.recycle(bitmap);
- }
-
- // Combines the two ScreenNails.
- // Returns the used one and recycle the unused one.
- public ScreenNail combine(ScreenNail other) {
- if (other == null) {
- return this;
- }
-
- if (!(other instanceof BitmapScreenNail)) {
- recycle();
- return other;
- }
-
- // Now both are BitmapScreenNail. Move over the information about width,
- // height, and Bitmap, then recycle the other.
- BitmapScreenNail newer = (BitmapScreenNail) other;
- mWidth = newer.mWidth;
- mHeight = newer.mHeight;
- if (newer.mBitmap != null) {
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = newer.mBitmap;
- newer.mBitmap = null;
-
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- }
-
- newer.recycle();
- return this;
- }
-
- public void updatePlaceholderSize(int width, int height) {
- if (mBitmap != null) return;
- if (width == 0 || height == 0) return;
- setSize(width, height);
+ mBitmapTexture = new BitmapTexture(bitmap);
}
@Override
public int getWidth() {
- return mWidth;
+ return mBitmapTexture.getWidth();
}
@Override
public int getHeight() {
- return mHeight;
+ return mBitmapTexture.getHeight();
}
@Override
- public void noDraw() {
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ mBitmapTexture.draw(canvas, x, y, width, height);
}
@Override
- public void recycle() {
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = null;
- }
-
- public static void disableDrawPlaceholder() {
- mDrawPlaceholder = false;
- }
-
- public static void enableDrawPlaceholder() {
- mDrawPlaceholder = true;
+ public void noDraw() {
+ // do nothing
}
@Override
- public void draw(GLCanvas canvas, int x, int y, int width, int height) {
- if (mBitmap == null) {
- if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
- mAnimationStartTime = ANIMATION_NEEDED;
- }
- if(mDrawPlaceholder) {
- canvas.fillRect(x, y, width, height, mPlaceholderColor);
- }
- return;
- }
-
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- if (mAnimationStartTime == ANIMATION_NEEDED) {
- mAnimationStartTime = now();
- }
-
- if (isAnimating()) {
- canvas.drawMixed(mTexture, mPlaceholderColor, getRatio(), x, y,
- width, height);
- } else {
- mTexture.draw(canvas, x, y, width, height);
- }
+ public void recycle() {
+ mBitmapTexture.recycle();
}
@Override
public void draw(GLCanvas canvas, RectF source, RectF dest) {
- if (mBitmap == null) {
- canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
- mPlaceholderColor);
- return;
- }
-
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- canvas.drawTexture(mTexture, source, dest);
- }
-
- public boolean isAnimating() {
- if (mAnimationStartTime < 0) return false;
- if (now() - mAnimationStartTime >= DURATION) {
- mAnimationStartTime = ANIMATION_DONE;
- return false;
- }
- return true;
- }
-
- private static long now() {
- return AnimationTime.get();
- }
-
- private float getRatio() {
- float r = (float)(now() - mAnimationStartTime) / DURATION;
- return Utils.clamp(1.0f - r, 0.0f, 1.0f);
- }
-
- public boolean isShowingPlaceholder() {
- return (mBitmap == null) || isAnimating();
- }
-
- public static void setMaxSide(int size) {
- sMaxSide = size;
+ canvas.drawTexture(mBitmapTexture, source, dest);
}
}
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index e3a32ef08..6f8baef7e 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -99,6 +99,13 @@ public interface GLCanvas {
public void drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int w, int h);
+ // Draw a region of a texture and a specified color to the specified
+ // rectangle. The actual color used is from * (1 - ratio) + to * ratio.
+ // The region of the texture is defined by parameter "src". The target
+ // rectangle is specified by parameter "target".
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target);
+
// Gets the underlying GL instance. This is used only when direct access to
// GL is needed.
public GL11 getGLInstance();
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index d83daf3e4..45903b3cd 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -415,7 +415,7 @@ public class GLCanvasImpl implements GLCanvas {
// This function changes the source coordinate to the texture coordinates.
// It also clips the source and target coordinates if it is beyond the
// bound of the texture.
- private void convertCoordinate(RectF source, RectF target,
+ private static void convertCoordinate(RectF source, RectF target,
BasicTexture texture) {
int width = texture.getWidth();
@@ -465,23 +465,7 @@ public class GLCanvasImpl implements GLCanvas {
color[3] = alpha;
}
- private void drawMixed(BasicTexture from, int toColor,
- float ratio, int x, int y, int width, int height, float alpha) {
- // change from 0 to 0.01f to prevent getting divided by zero below
- if (ratio <= 0.01f) {
- drawTexture(from, x, y, width, height, alpha);
- return;
- } else if (ratio >= 1) {
- fillRect(x, y, width, height, toColor);
- return;
- }
-
- mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
- || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
-
- final GL11 gl = mGL;
- if (!bindTexture(from)) return;
-
+ private void setMixedColor(int toColor, float ratio, float alpha) {
//
// The formula we want:
// alpha * ((1 - ratio) * from + ratio * to)
@@ -495,9 +479,6 @@ public class GLCanvasImpl implements GLCanvas {
float combo = alpha * (1 - ratio);
float scale = alpha * ratio / (1 - combo);
- // Interpolate the RGB and alpha values between both textures.
- mGLState.setTexEnvMode(GL11.GL_COMBINE);
-
// Specify the interpolation factor via the alpha component of
// GL_TEXTURE_ENV_COLORs.
// RGB component are get from toColor and will used as SRC1
@@ -505,6 +486,7 @@ public class GLCanvasImpl implements GLCanvas {
setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
((toColor >>> 8) & 0xff) * colorScale,
(toColor & 0xff) * colorScale, combo);
+ GL11 gl = mGL;
gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
@@ -522,6 +504,64 @@ public class GLCanvasImpl implements GLCanvas {
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+ }
+
+ @Override
+ public void drawMixed(BasicTexture from, int toColor, float ratio,
+ RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) return;
+
+ if (ratio <= 0.01f) {
+ drawTexture(from, source, target);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+ return;
+ }
+
+ float alpha = mAlpha;
+
+ // Copy the input to avoid changing it.
+ mDrawTextureSourceRect.set(source);
+ mDrawTextureTargetRect.set(target);
+ source = mDrawTextureSourceRect;
+ target = mDrawTextureTargetRect;
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ if (!bindTexture(from)) return;
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ setMixedColor(toColor, ratio, alpha);
+ convertCoordinate(source, target, from);
+ setTextureCoords(source);
+ textureRect(target.left, target.top, target.width(), target.height());
+ mGLState.setTexEnvMode(GL11.GL_REPLACE);
+ }
+
+ private void drawMixed(BasicTexture from, int toColor,
+ float ratio, int x, int y, int width, int height, float alpha) {
+ // change from 0 to 0.01f to prevent getting divided by zero below
+ if (ratio <= 0.01f) {
+ drawTexture(from, x, y, width, height, alpha);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(x, y, width, height, toColor);
+ return;
+ }
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ final GL11 gl = mGL;
+ if (!bindTexture(from)) return;
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ setMixedColor(toColor, ratio, alpha);
+
drawBoundTexture(from, x, y, width, height);
mGLState.setTexEnvMode(GL11.GL_REPLACE);
}
diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java
index 1651b4361..13b610b23 100644
--- a/src/com/android/gallery3d/ui/GLRoot.java
+++ b/src/com/android/gallery3d/ui/GLRoot.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Matrix;
import com.android.gallery3d.anim.CanvasAnimation;
@@ -45,4 +46,6 @@ public interface GLRoot {
public void freeze();
public void unfreeze();
public void setLightsOutMode(boolean enabled);
+
+ public Context getContext();
}
diff --git a/src/com/android/gallery3d/ui/GLView.java b/src/com/android/gallery3d/ui/GLView.java
index 3924c6e9d..b91b71212 100644
--- a/src/com/android/gallery3d/ui/GLView.java
+++ b/src/com/android/gallery3d/ui/GLView.java
@@ -21,6 +21,7 @@ import android.os.SystemClock;
import android.view.MotionEvent;
import com.android.gallery3d.anim.CanvasAnimation;
+import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.common.Utils;
import java.util.ArrayList;
@@ -77,6 +78,11 @@ public class GLView {
protected int mScrollHeight = 0;
protected int mScrollWidth = 0;
+ public static final int ANIM_TIME_OPENING = 400;
+ private RawTexture mFadeOutTexture;
+ private float [] mBackgroundColor;
+ private StateTransitionAnimation mTransition = new StateTransitionAnimation(ANIM_TIME_OPENING);
+
public void startAnimation(CanvasAnimation animation) {
GLRoot root = getGLRoot();
if (root == null) throw new IllegalStateException();
@@ -217,13 +223,46 @@ public class GLView {
}
protected void render(GLCanvas canvas) {
+ if (mTransition.calculate(AnimationTime.get())) invalidate();
+ canvas.save();
renderBackground(canvas);
+ if (mTransition.isActive()) mTransition.applyForegroundTransformation(this, canvas);
for (int i = 0, n = getComponentCount(); i < n; ++i) {
renderChild(canvas, getComponent(i));
}
+ canvas.restore();
+ }
+
+ public void setFadeOutTexture(RawTexture texture) {
+ mFadeOutTexture = texture;
+ if (mFadeOutTexture != null) {
+ TiledScreenNail.disableDrawPlaceholder();
+ }
+ mTransition.start();
+ }
+
+ public float [] getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ public void setBackgroundColor(float [] color) {
+ mBackgroundColor = color;
}
protected void renderBackground(GLCanvas view) {
+ if (mBackgroundColor != null) {
+ view.clearBuffer(mBackgroundColor);
+ }
+ if (mFadeOutTexture != null) {
+ if (!mTransition.isActive()) {
+ mFadeOutTexture.recycle();
+ mFadeOutTexture = null;
+ TiledScreenNail.enableDrawPlaceholder();
+ } else {
+ mTransition.applyBackground(this, view, mFadeOutTexture);
+ return;
+ }
+ }
}
protected void renderChild(GLCanvas canvas, GLView component) {
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 5978159e4..edd7c72e3 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -17,6 +17,7 @@
package com.android.gallery3d.ui;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -546,10 +547,20 @@ public class PhotoView extends GLView {
}
private int getPanoramaRotation() {
- // Panorama only support rotations of 0 and 90, so if it is greater
- // than that flip the output surface texture to compensate
- if (mDisplayRotation > 180)
+ // This function is magic
+ // The issue here is that Pano makes bad assumptions about rotation and
+ // orientation. The first is it assumes only two rotations are possible,
+ // 0 and 90. Thus, if display rotation is >= 180, we invert the output.
+ // The second is that it assumes landscape is a 90 rotation from portrait,
+ // however on landscape devices this is not true. Thus, if we are in portrait
+ // on a landscape device, we need to invert the output
+ int orientation = getGLRoot().getContext().getResources().getConfiguration().orientation;
+ boolean invertPortrait = (orientation == Configuration.ORIENTATION_PORTRAIT
+ && (mDisplayRotation == 90 || mDisplayRotation == 270));
+ boolean invert = (mDisplayRotation >= 180);
+ if (invert != invertPortrait) {
return (mCompensation + 180) % 360;
+ }
return mCompensation;
}
@@ -839,8 +850,8 @@ public class PhotoView extends GLView {
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
@Override
@@ -1781,8 +1792,8 @@ public class PhotoView extends GLView {
MediaItem item = mModel.getMediaItem(i);
if (item == null) continue;
ScreenNail sc = mModel.getScreenNail(i);
- if (!(sc instanceof BitmapScreenNail)
- || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
+ if (!(sc instanceof TiledScreenNail)
+ || ((TiledScreenNail) sc).isShowingPlaceholder()) continue;
// Now, sc is BitmapScreenNail and is not showing placeholder
Rect rect = new Rect(getPhotoRect(i));
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index fffa7e038..0111847eb 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -67,7 +67,7 @@ class PositionController {
SNAPBACK_ANIMATION_TIME, // ANIM_KIND_SNAPBACK
400, // ANIM_KIND_SLIDE
300, // ANIM_KIND_ZOOM
- PhotoPage.ANIM_TIME_OPENING, // ANIM_KIND_OPENING
+ GLView.ANIM_TIME_OPENING, // ANIM_KIND_OPENING
0, // ANIM_KIND_FLING (the duration is calculated dynamically)
0, // ANIM_KIND_FLING_X (see the comment above)
0, // ANIM_KIND_DELETE (the duration is calculated dynamically)
diff --git a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
index 812e831e1..7cab753c3 100644
--- a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
+++ b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
@@ -6,7 +6,7 @@ import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
public class PreparePageFadeoutTexture implements OnGLIdleListener {
- private static final long TIMEOUT = FadeTexture.DURATION;
+ private static final long TIMEOUT = 500;
public static final String KEY_FADE_TEXTURE = "fade_texture";
private RawTexture mTexture;
diff --git a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
index 1930e3877..7cb894845 100644
--- a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
+++ b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
@@ -109,6 +109,7 @@ public abstract class SurfaceTextureScreenNail implements ScreenNail,
canvas.translate(cx, cy);
canvas.scale(1, -1, 1);
canvas.translate(-cx, -cy);
+ updateTransformMatrix(mTransform);
canvas.drawTexture(mExtTexture, mTransform, x, y, width, height);
canvas.restore();
}
@@ -119,6 +120,8 @@ public abstract class SurfaceTextureScreenNail implements ScreenNail,
throw new UnsupportedOperationException();
}
+ protected void updateTransformMatrix(float[] matrix) {}
+
@Override
abstract public void noDraw();
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index 5ce06bec4..8f26981fe 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -462,8 +462,8 @@ public class TileImageView extends GLView {
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
private void uploadBackgroundTiles(GLCanvas canvas) {
@@ -575,8 +575,10 @@ public class TileImageView extends GLView {
}
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;
}
}
@@ -621,7 +623,6 @@ public class TileImageView extends GLView {
}
}
- // TODO: avoid drawing the unused part of the textures.
static boolean drawTile(
Tile tile, GLCanvas canvas, RectF source, RectF target) {
while (true) {
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 08d337921..45e2ce218 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -40,51 +40,25 @@ public class TileImageViewAdapter implements TileImageView.Model {
public TileImageViewAdapter() {
}
- public TileImageViewAdapter(
- Bitmap bitmap, BitmapRegionDecoder regionDecoder) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mRegionDecoder = regionDecoder;
- mImageWidth = regionDecoder.getWidth();
- mImageHeight = regionDecoder.getHeight();
- mLevelCount = calculateLevelCount();
- }
-
public synchronized void clear() {
- updateScreenNail(null, false);
+ mScreenNail = null;
mImageWidth = 0;
mImageHeight = 0;
mLevelCount = 0;
mRegionDecoder = null;
}
- public synchronized void setScreenNail(Bitmap bitmap, int width, int height) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mImageWidth = width;
- mImageHeight = height;
- mRegionDecoder = null;
- mLevelCount = 0;
- }
-
+ // Caller is responsible to recycle the ScreenNail
public synchronized void setScreenNail(
ScreenNail screenNail, int width, int height) {
Utils.checkNotNull(screenNail);
- updateScreenNail(screenNail, false);
+ mScreenNail = screenNail;
mImageWidth = width;
mImageHeight = height;
mRegionDecoder = null;
mLevelCount = 0;
}
- private void updateScreenNail(ScreenNail screenNail, boolean own) {
- if (mScreenNail != null && mOwnScreenNail) {
- mScreenNail.recycle();
- }
- mScreenNail = screenNail;
- mOwnScreenNail = own;
- }
-
public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) {
mRegionDecoder = Utils.checkNotNull(decoder);
mImageWidth = decoder.getWidth();
diff --git a/src/com/android/gallery3d/ui/TiledScreenNail.java b/src/com/android/gallery3d/ui/TiledScreenNail.java
new file mode 100644
index 000000000..d2b34e3bf
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledScreenNail.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
+import com.android.gallery3d.data.MediaItem;
+
+// This is a ScreenNail wraps a Bitmap. There are some extra functions:
+//
+// - If we need to draw before the bitmap is available, we draw a rectange of
+// placeholder color (gray).
+//
+// - When the the bitmap is available, and we have drawn the placeholder color
+// before, we will do a fade-in animation.
+public class TiledScreenNail implements ScreenNail {
+ @SuppressWarnings("unused")
+ private static final String TAG = "TiledScreenNail";
+
+ // The duration of the fading animation in milliseconds
+ private static final int DURATION = 180;
+
+ private static int sMaxSide = 640;
+
+ // These are special values for mAnimationStartTime
+ private static final long ANIMATION_NOT_NEEDED = -1;
+ private static final long ANIMATION_NEEDED = -2;
+ private static final long ANIMATION_DONE = -3;
+
+ private int mWidth;
+ private int mHeight;
+ private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+
+ private Bitmap mBitmap;
+ private TiledTexture mTexture;
+
+ public TiledScreenNail(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ mBitmap = bitmap;
+ mTexture = new TiledTexture(bitmap);
+ }
+
+ public TiledScreenNail(int width, int height) {
+ setSize(width, height);
+ }
+
+ // This gets overridden by bitmap_screennail_placeholder
+ // in GalleryUtils.initialize
+ private static int mPlaceholderColor = 0xFF222222;
+ private static boolean mDrawPlaceholder = true;
+
+ public static void setPlaceholderColor(int color) {
+ mPlaceholderColor = color;
+ }
+
+ private void setSize(int width, int height) {
+ if (width == 0 || height == 0) {
+ width = sMaxSide;
+ height = sMaxSide * 3 / 4;
+ }
+ float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
+ mWidth = Math.round(scale * width);
+ mHeight = Math.round(scale * height);
+ }
+
+ private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
+ if (pool == null || bitmap == null) return;
+ pool.recycle(bitmap);
+ }
+
+ // Combines the two ScreenNails.
+ // Returns the used one and recycle the unused one.
+ public ScreenNail combine(ScreenNail other) {
+ if (other == null) {
+ return this;
+ }
+
+ if (!(other instanceof TiledScreenNail)) {
+ recycle();
+ return other;
+ }
+
+ // Now both are TiledScreenNail. Move over the information about width,
+ // height, and Bitmap, then recycle the other.
+ TiledScreenNail newer = (TiledScreenNail) other;
+ mWidth = newer.mWidth;
+ mHeight = newer.mHeight;
+ if (newer.mTexture != null) {
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ if (mTexture != null) mTexture.recycle();
+ mBitmap = newer.mBitmap;
+ mTexture = newer.mTexture;
+ newer.mBitmap = null;
+ newer.mTexture = null;
+ }
+ newer.recycle();
+ return this;
+ }
+
+ public void updatePlaceholderSize(int width, int height) {
+ if (mBitmap != null) return;
+ if (width == 0 || height == 0) return;
+ setSize(width, height);
+ }
+
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void noDraw() {
+ }
+
+ @Override
+ public void recycle() {
+ if (mTexture != null) {
+ mTexture.recycle();
+ mTexture = null;
+ }
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ mBitmap = null;
+ }
+
+ public static void disableDrawPlaceholder() {
+ mDrawPlaceholder = false;
+ }
+
+ public static void enableDrawPlaceholder() {
+ mDrawPlaceholder = true;
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ if (mTexture == null || !mTexture.isReady()) {
+ if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
+ mAnimationStartTime = ANIMATION_NEEDED;
+ }
+ if(mDrawPlaceholder) {
+ canvas.fillRect(x, y, width, height, mPlaceholderColor);
+ }
+ return;
+ }
+
+ if (mAnimationStartTime == ANIMATION_NEEDED) {
+ mAnimationStartTime = AnimationTime.get();
+ }
+
+ if (isAnimating()) {
+ mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
+ width, height);
+ } else {
+ mTexture.draw(canvas, x, y, width, height);
+ }
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, RectF source, RectF dest) {
+ if (mTexture == null || !mTexture.isReady()) {
+ canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
+ mPlaceholderColor);
+ return;
+ }
+
+ mTexture.draw(canvas, source, dest);
+ }
+
+ public boolean isAnimating() {
+ if (mAnimationStartTime < 0) return false;
+ if (AnimationTime.get() - mAnimationStartTime >= DURATION) {
+ mAnimationStartTime = ANIMATION_DONE;
+ return false;
+ }
+ return true;
+ }
+
+ private float getRatio() {
+ float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
+ return Utils.clamp(1.0f - r, 0.0f, 1.0f);
+ }
+
+ public boolean isShowingPlaceholder() {
+ return (mBitmap == null) || isAnimating();
+ }
+
+ public TiledTexture getTexture() {
+ return mTexture;
+ }
+
+ public static void setMaxSide(int size) {
+ sMaxSide = size;
+ }
+}
diff --git a/src/com/android/gallery3d/ui/TiledTexture.java b/src/com/android/gallery3d/ui/TiledTexture.java
new file mode 100644
index 000000000..6e9ad9ea8
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledTexture.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+
+import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+// This class is similar to BitmapTexture, except the bitmap is
+// split into tiles. By doing so, we may increase the time required to
+// upload the whole bitmap but we reduce the time of uploading each tile
+// so it make the animation more smooth and prevents jank.
+public class TiledTexture {
+ private static final int CONTENT_SIZE = 254;
+ private static final int BORDER_SIZE = 1;
+ private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
+ private static final int INIT_CAPACITY = 8;
+
+ private static Tile sFreeTileHead = null;
+ private static final Object sFreeTileLock = new Object();
+
+ private static Bitmap sUploadBitmap;
+ private static Canvas sCanvas;
+ private static Paint sPaint;
+
+ private int mUploadIndex = 0;
+
+ private final Tile[] mTiles;
+ private final int mWidth;
+ private final int mHeight;
+ private final RectF mSrcRect = new RectF();
+ private final RectF mDestRect = new RectF();
+
+ public static class Uploader implements OnGLIdleListener {
+ private final ArrayDeque<TiledTexture> mTextures =
+ new ArrayDeque<TiledTexture>(INIT_CAPACITY);
+
+ private final GLRoot mGlRoot;
+ private boolean mIsQueued = false;
+
+ public Uploader(GLRoot glRoot) {
+ mGlRoot = glRoot;
+ }
+
+ public synchronized void clear() {
+ mTextures.clear();
+ }
+
+ public synchronized void addTexture(TiledTexture t) {
+ if (t.isReady()) return;
+ mTextures.addLast(t);
+
+ if (mIsQueued) return;
+ mIsQueued = true;
+ mGlRoot.addOnGLIdleListener(this);
+ }
+
+
+ @Override
+ public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
+ ArrayDeque<TiledTexture> deque = mTextures;
+ synchronized (this) {
+ if (!deque.isEmpty()) {
+ TiledTexture t = deque.peekFirst();
+ if (t.uploadNextTile(canvas)) {
+ deque.removeFirst();
+ mGlRoot.requestRender();
+ }
+ }
+ mIsQueued = !mTextures.isEmpty();
+
+ // return true to keep this listener in the queue
+ return mIsQueued;
+ }
+ }
+ }
+
+ private static class Tile extends UploadedTexture {
+ public int offsetX;
+ public int offsetY;
+ public Bitmap bitmap;
+ public Tile nextFreeTile;
+ public int contentWidth;
+ public int contentHeight;
+
+ @Override
+ public void setSize(int width, int height) {
+ contentWidth = width;
+ contentHeight = height;
+ mWidth = width + 2 * BORDER_SIZE;
+ mHeight = height + 2 * BORDER_SIZE;
+ mTextureWidth = TILE_SIZE;
+ mTextureHeight = TILE_SIZE;
+ }
+
+ @Override
+ protected Bitmap onGetBitmap() {
+ int x = BORDER_SIZE - offsetX;
+ int y = BORDER_SIZE - offsetY;
+ int r = bitmap.getWidth() - x;
+ int b = bitmap.getHeight() - y ;
+ sCanvas.drawBitmap(bitmap, x, y, null);
+ bitmap = null;
+
+ // draw borders if need
+ if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
+ if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
+ if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
+ if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
+
+ return sUploadBitmap;
+ }
+
+ @Override
+ protected void onFreeBitmap(Bitmap bitmap) {
+ // do nothing
+ }
+ }
+
+ private static void freeTile(Tile tile) {
+ tile.invalidateContent();
+ tile.bitmap = null;
+ synchronized (sFreeTileLock) {
+ tile.nextFreeTile = sFreeTileHead;
+ sFreeTileHead = tile;
+ }
+ }
+
+ private static Tile obtainTile() {
+ synchronized (sFreeTileLock) {
+ Tile result = sFreeTileHead;
+ if (result == null) return new Tile();
+ sFreeTileHead = result.nextFreeTile;
+ result.nextFreeTile = null;
+ return result;
+ }
+ }
+
+ private boolean uploadNextTile(GLCanvas canvas) {
+ if (mUploadIndex == mTiles.length) return true;
+ Tile next = mTiles[mUploadIndex++];
+ boolean hasBeenLoad = next.isLoaded();
+ next.updateContent(canvas);
+
+ // It will take some time for a texture to be drawn for the first
+ // time. When scrolling, we need to draw several tiles on the screen
+ // at the same time. It may cause a UI jank even these textures has
+ // been uploaded.
+ if (!hasBeenLoad) next.draw(canvas, 0, 0);
+ return mUploadIndex == mTiles.length;
+ }
+
+ public TiledTexture(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ ArrayList<Tile> list = new ArrayList<Tile>();
+
+ for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
+ for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
+ Tile tile = obtainTile();
+ tile.offsetX = x;
+ tile.offsetY = y;
+ tile.bitmap = bitmap;
+ tile.setSize(
+ Math.min(CONTENT_SIZE, mWidth - x),
+ Math.min(CONTENT_SIZE, mHeight - y));
+ list.add(tile);
+ }
+ }
+ mTiles = list.toArray(new Tile[list.size()]);
+ }
+
+ public boolean isReady() {
+ return mUploadIndex == mTiles.length;
+ }
+
+ public void recycle() {
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ freeTile(mTiles[i]);
+ }
+ }
+
+ public static void freeResources() {
+ sUploadBitmap = null;
+ sCanvas = null;
+ sPaint = null;
+ }
+
+ public static void prepareResources() {
+ sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
+ sCanvas = new Canvas(sUploadBitmap);
+ sPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ sPaint.setColor(Color.TRANSPARENT);
+ sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ }
+
+ // We want to draw the "source" on the "target".
+ // This method is to find the "output" rectangle which is
+ // the corresponding area of the "src".
+ // (x,y) target
+ // (x0,y0) source +---------------+
+ // +----------+ | |
+ // | src | | output |
+ // | +--+ | linear map | +----+ |
+ // | +--+ | ----------> | | | |
+ // | | by (scaleX, scaleY) | +----+ |
+ // +----------+ | |
+ // Texture +---------------+
+ // Canvas
+ private static void mapRect(RectF output,
+ RectF src, float x0, float y0, float x, float y, float scaleX,
+ float scaleY) {
+ output.set(x + (src.left - x0) * scaleX,
+ y + (src.top - y0) * scaleY,
+ x + (src.right - x0) * scaleX,
+ y + (src.bottom - y0) * scaleY);
+ }
+
+ // Draws a mixed color of this texture and a specified color onto the
+ // a rectangle. The used color is: from * (1 - ratio) + to * ratio.
+ public void drawMixed(GLCanvas canvas, int color, float ratio,
+ int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws the texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws a sub region of this texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, RectF source, RectF target) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float x0 = source.left;
+ float y0 = source.top;
+ float x = target.left;
+ float y = target.top;
+ float scaleX = target.width() / source.width();
+ float scaleY = target.height() / source.height();
+
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ if (!src.intersect(source)) continue;
+ mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, src, dest);
+ }
+ }
+}