summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/gallery3d/app/PhotoDataAdapter.java41
-rw-r--r--src/com/android/gallery3d/ui/BasicTexture.java4
-rw-r--r--src/com/android/gallery3d/ui/BitmapScreenNail.java49
-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/TileImageView.java3
-rw-r--r--src/com/android/gallery3d/ui/TiledTexture.java298
-rw-r--r--tests/src/com/android/gallery3d/ui/GLCanvasStub.java2
8 files changed, 430 insertions, 56 deletions
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index 66f2874f7..5ab022ac6 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -35,6 +35,7 @@ import com.android.gallery3d.ui.PhotoView;
import com.android.gallery3d.ui.ScreenNail;
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.ui.TileImageViewAdapter;
+import com.android.gallery3d.ui.TiledTexture;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
import com.android.gallery3d.util.MediaSetUtils;
@@ -59,8 +60,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
private static final int MSG_RUN_OBJECT = 3;
private static final int MSG_UPDATE_IMAGE_REQUESTS = 4;
- private static final int MIN_LOAD_COUNT = 8;
- private static final int DATA_CACHE_SIZE = 32;
+ private static final int MIN_LOAD_COUNT = 16;
+ private static final int DATA_CACHE_SIZE = 256;
private static final int SCREEN_NAIL_MAX = PhotoView.SCREEN_NAIL_MAX;
private static final int IMAGE_CACHE_SIZE = 2 * SCREEN_NAIL_MAX + 1;
@@ -162,6 +163,7 @@ public class PhotoDataAdapter implements PhotoPage.Model {
private DataListener mDataListener;
private final SourceListener mSourceListener = new SourceListener();
+ private final TiledTexture.Uploader mUploader;
// The path of the current viewing item will be stored in mItemPath.
// If mItemPath is not null, mCurrentIndex is only a hint for where we
@@ -183,6 +185,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
Arrays.fill(mChanges, MediaObject.INVALID_DATA_VERSION);
+ mUploader = new TiledTexture.Uploader(activity.getGLRoot());
+
mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
@SuppressWarnings("unchecked")
@Override
@@ -321,6 +325,7 @@ public class PhotoDataAdapter implements PhotoPage.Model {
}
}
updateImageRequests();
+ updateScreenNailUploadQueue();
}
private void updateFullImage(Path path, Future<BitmapRegionDecoder> future) {
@@ -345,6 +350,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
@Override
public void resume() {
mIsActive = true;
+ TiledTexture.prepareResources();
+
mSource.addContentListener(mSourceListener);
updateImageCache();
updateImageRequests();
@@ -371,6 +378,9 @@ public class PhotoDataAdapter implements PhotoPage.Model {
}
mImageCache.clear();
mTileProvider.clear();
+
+ mUploader.clear();
+ TiledTexture.freeResources();
}
private MediaItem getItem(int index) {
@@ -394,6 +404,7 @@ public class PhotoDataAdapter implements PhotoPage.Model {
updateImageCache();
updateImageRequests();
updateTileProvider();
+ updateScreenNailUploadQueue();
if (mDataListener != null) {
mDataListener.onPhotoChanged(index, mItemPath);
@@ -402,6 +413,32 @@ public class PhotoDataAdapter implements PhotoPage.Model {
fireDataChange();
}
+ private void uploadScreenNail(int offset) {
+ int index = mCurrentIndex + offset;
+ if (index < mActiveStart || index >= mActiveEnd) return;
+
+ MediaItem item = getItem(index);
+ if (item == null) return;
+
+ ImageEntry e = mImageCache.get(item.getPath());
+ if (e == null) return;
+
+ ScreenNail s = e.screenNail;
+ if (s instanceof BitmapScreenNail) {
+ TiledTexture t = ((BitmapScreenNail) s).getTexture();
+ if (t != null && !t.isReady()) mUploader.addTexture(t);
+ }
+ }
+
+ private void updateScreenNailUploadQueue() {
+ mUploader.clear();
+ uploadScreenNail(0);
+ for (int i = 1; i < IMAGE_CACHE_SIZE; ++i) {
+ uploadScreenNail(i);
+ uploadScreenNail(-i);
+ }
+ }
+
@Override
public void moveTo(int index) {
updateCurrentIndex(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..9b629160c 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -46,16 +46,16 @@ public class BitmapScreenNail implements ScreenNail {
private int mWidth;
private int mHeight;
- private Bitmap mBitmap;
- private BitmapTexture mTexture;
private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+ private Bitmap mBitmap;
+ private TiledTexture mTexture;
+
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.
+ mTexture = new TiledTexture(bitmap);
}
public BitmapScreenNail(int width, int height) {
@@ -103,17 +103,14 @@ public class BitmapScreenNail implements ScreenNail {
BitmapScreenNail newer = (BitmapScreenNail) other;
mWidth = newer.mWidth;
mHeight = newer.mHeight;
- if (newer.mBitmap != null) {
+ if (newer.mTexture != null) {
recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ if (mTexture != null) mTexture.recycle();
mBitmap = newer.mBitmap;
+ mTexture = newer.mTexture;
newer.mBitmap = null;
-
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
+ newer.mTexture = null;
}
-
newer.recycle();
return this;
}
@@ -158,7 +155,7 @@ public class BitmapScreenNail implements ScreenNail {
@Override
public void draw(GLCanvas canvas, int x, int y, int width, int height) {
- if (mBitmap == null) {
+ if (mTexture == null || !mTexture.isReady()) {
if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
mAnimationStartTime = ANIMATION_NEEDED;
}
@@ -168,16 +165,12 @@ public class BitmapScreenNail implements ScreenNail {
return;
}
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
if (mAnimationStartTime == ANIMATION_NEEDED) {
- mAnimationStartTime = now();
+ mAnimationStartTime = AnimationTime.get();
}
if (isAnimating()) {
- canvas.drawMixed(mTexture, mPlaceholderColor, getRatio(), x, y,
+ mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
width, height);
} else {
mTexture.draw(canvas, x, y, width, height);
@@ -186,34 +179,26 @@ public class BitmapScreenNail implements ScreenNail {
@Override
public void draw(GLCanvas canvas, RectF source, RectF dest) {
- if (mBitmap == null) {
+ if (mTexture == null || !mTexture.isReady()) {
canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
mPlaceholderColor);
return;
}
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- canvas.drawTexture(mTexture, source, dest);
+ mTexture.draw(canvas, source, dest);
}
public boolean isAnimating() {
if (mAnimationStartTime < 0) return false;
- if (now() - mAnimationStartTime >= DURATION) {
+ if (AnimationTime.get() - 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;
+ float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
return Utils.clamp(1.0f - r, 0.0f, 1.0f);
}
@@ -221,6 +206,10 @@ public class BitmapScreenNail implements ScreenNail {
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/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/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index 5ce06bec4..18a7af849 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -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/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);
+ }
+ }
+}
diff --git a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
index 2f2d753e0..5a08b8599 100644
--- a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
+++ b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
@@ -83,4 +83,6 @@ public class GLCanvasStub implements GLCanvas {
public void dumpStatisticsAndClear() {}
public void beginRenderTarget(RawTexture texture) {}
public void endRenderTarget() {}
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target) {}
}