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/ActionModeHandler.java30
-rw-r--r--src/com/android/gallery3d/ui/BasicTexture.java5
-rw-r--r--src/com/android/gallery3d/ui/CropView.java801
-rw-r--r--src/com/android/gallery3d/ui/DetailsHelper.java12
-rw-r--r--src/com/android/gallery3d/ui/DialogDetailsView.java46
-rw-r--r--src/com/android/gallery3d/ui/ExtTexture.java34
-rw-r--r--src/com/android/gallery3d/ui/GLCanvas.java200
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java154
-rw-r--r--src/com/android/gallery3d/ui/GLES20Canvas.java1068
-rw-r--r--src/com/android/gallery3d/ui/GLId.java29
-rw-r--r--src/com/android/gallery3d/ui/GLIdImpl.java68
-rw-r--r--src/com/android/gallery3d/ui/GLRootView.java4
-rw-r--r--src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java31
-rw-r--r--src/com/android/gallery3d/ui/GestureRecognizer.java4
-rw-r--r--src/com/android/gallery3d/ui/MenuExecutor.java25
-rw-r--r--src/com/android/gallery3d/ui/NinePatchTexture.java42
-rw-r--r--src/com/android/gallery3d/ui/PhotoView.java20
-rw-r--r--src/com/android/gallery3d/ui/PopupList.java2
-rw-r--r--src/com/android/gallery3d/ui/PositionController.java8
-rw-r--r--src/com/android/gallery3d/ui/RawTexture.java40
-rw-r--r--src/com/android/gallery3d/ui/SlideshowView.java9
-rw-r--r--src/com/android/gallery3d/ui/UploadedTexture.java53
22 files changed, 1632 insertions, 1053 deletions
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index 7191599ad..d90fc20d0 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -17,21 +17,21 @@
package com.android.gallery3d.ui;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Handler;
-import android.view.ActionMode;
-import android.view.ActionMode.Callback;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
-import android.widget.ShareActionProvider;
-import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.ActionMode.Callback;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
+import com.actionbarsherlock.widget.ShareActionProvider.OnShareTargetSelectedListener;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.common.ApiHelper;
@@ -129,7 +129,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
}
public void startActionMode() {
- Activity a = mActivity;
+ SherlockActivity a = mActivity;
mActionMode = a.startActionMode(this);
View customView = LayoutInflater.from(a).inflate(
R.layout.action_mode, null);
@@ -408,6 +408,15 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
// Pass1: Deal with unexpanded media object list for menu operation.
ArrayList<MediaObject> selected = getSelectedMediaObjects(jc);
if (selected == null) {
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mMenuTask = null;
+ if (jc.isCancelled()) return;
+ // Disable all the operations when no item is selected
+ MenuExecutor.updateMenuOperation(mMenu, 0);
+ }
+ });
return null;
}
final int operation = computeMenuOptions(selected);
@@ -466,7 +475,12 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
mMenuExecutor.pause();
}
+ public void destroy() {
+ mMenuExecutor.destroy();
+ }
+
public void resume() {
if (mSelectionManager.inSelectionMode()) updateSupportedOperation();
+ mMenuExecutor.resume();
}
}
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 99cf0571c..38686d59f 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -36,7 +36,7 @@ abstract class BasicTexture implements Texture {
// Log a warning if a texture is larger along a dimension
private static final int MAX_TEXTURE_SIZE = 4096;
- protected int mId;
+ protected int mId = -1;
protected int mState;
protected int mWidth = UNSPECIFIED;
@@ -165,8 +165,9 @@ abstract class BasicTexture implements Texture {
private void freeResource() {
GLCanvas canvas = mCanvasRef;
- if (canvas != null && isLoaded()) {
+ if (canvas != null && mId != -1) {
canvas.unloadTexture(this);
+ mId = -1; // Don't free it again.
}
mState = STATE_UNLOADED;
setAssociatedCanvas(null);
diff --git a/src/com/android/gallery3d/ui/CropView.java b/src/com/android/gallery3d/ui/CropView.java
deleted file mode 100644
index 1890c7630..000000000
--- a/src/com/android/gallery3d/ui/CropView.java
+++ /dev/null
@@ -1,801 +0,0 @@
-/*
- * Copyright (C) 2010 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.PointF;
-import android.graphics.RectF;
-import android.media.FaceDetector;
-import android.os.Handler;
-import android.os.Message;
-import android.util.FloatMath;
-import android.view.MotionEvent;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Toast;
-
-import com.android.gallery3d.R;
-import com.android.gallery3d.anim.Animation;
-import com.android.gallery3d.app.AbstractGalleryActivity;
-import com.android.gallery3d.common.Utils;
-
-import java.util.ArrayList;
-
-import javax.microedition.khronos.opengles.GL11;
-
-/**
- * The activity can crop specific region of interest from an image.
- */
-public class CropView extends GLView {
- @SuppressWarnings("unused")
- private static final String TAG = "CropView";
-
- private static final int FACE_PIXEL_COUNT = 120000; // around 400x300
-
- private static final int COLOR_OUTLINE = 0xFF008AFF;
- private static final int COLOR_FACE_OUTLINE = 0xFF000000;
-
- private static final float OUTLINE_WIDTH = 3f;
-
- private static final int SIZE_UNKNOWN = -1;
- private static final int TOUCH_TOLERANCE = 30;
-
- private static final float MIN_SELECTION_LENGTH = 16f;
- public static final float UNSPECIFIED = -1f;
-
- private static final int MAX_FACE_COUNT = 3;
- private static final float FACE_EYE_RATIO = 2f;
-
- private static final int ANIMATION_DURATION = 1250;
-
- private static final int MOVE_LEFT = 1;
- private static final int MOVE_TOP = 2;
- private static final int MOVE_RIGHT = 4;
- private static final int MOVE_BOTTOM = 8;
- private static final int MOVE_BLOCK = 16;
-
- private static final float MAX_SELECTION_RATIO = 0.8f;
- private static final float MIN_SELECTION_RATIO = 0.4f;
- private static final float SELECTION_RATIO = 0.60f;
- private static final int ANIMATION_TRIGGER = 64;
-
- private static final int MSG_UPDATE_FACES = 1;
-
- private float mAspectRatio = UNSPECIFIED;
- private float mSpotlightRatioX = 0;
- private float mSpotlightRatioY = 0;
-
- private Handler mMainHandler;
-
- private FaceHighlightView mFaceDetectionView;
- private HighlightRectangle mHighlightRectangle;
- private TileImageView mImageView;
- private AnimationController mAnimation = new AnimationController();
-
- private int mImageWidth = SIZE_UNKNOWN;
- private int mImageHeight = SIZE_UNKNOWN;
-
- private AbstractGalleryActivity mActivity;
-
- private GLPaint mPaint = new GLPaint();
- private GLPaint mFacePaint = new GLPaint();
-
- private int mImageRotation;
-
- public CropView(AbstractGalleryActivity activity) {
- mActivity = activity;
- mImageView = new TileImageView(activity);
- mFaceDetectionView = new FaceHighlightView();
- mHighlightRectangle = new HighlightRectangle();
-
- addComponent(mImageView);
- addComponent(mFaceDetectionView);
- addComponent(mHighlightRectangle);
-
- mHighlightRectangle.setVisibility(GLView.INVISIBLE);
-
- mPaint.setColor(COLOR_OUTLINE);
- mPaint.setLineWidth(OUTLINE_WIDTH);
-
- mFacePaint.setColor(COLOR_FACE_OUTLINE);
- mFacePaint.setLineWidth(OUTLINE_WIDTH);
-
- mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
- @Override
- public void handleMessage(Message message) {
- Utils.assertTrue(message.what == MSG_UPDATE_FACES);
- ((DetectFaceTask) message.obj).updateFaces();
- }
- };
- }
-
- public void setAspectRatio(float ratio) {
- mAspectRatio = ratio;
- }
-
- public void setSpotlightRatio(float ratioX, float ratioY) {
- mSpotlightRatioX = ratioX;
- mSpotlightRatioY = ratioY;
- }
-
- @Override
- public void onLayout(boolean changed, int l, int t, int r, int b) {
- int width = r - l;
- int height = b - t;
-
- mFaceDetectionView.layout(0, 0, width, height);
- mHighlightRectangle.layout(0, 0, width, height);
- mImageView.layout(0, 0, width, height);
- if (mImageHeight != SIZE_UNKNOWN) {
- mAnimation.initialize();
- if (mHighlightRectangle.getVisibility() == GLView.VISIBLE) {
- mAnimation.parkNow(
- mHighlightRectangle.mHighlightRect);
- }
- }
- }
-
- private boolean setImageViewPosition(int centerX, int centerY, float scale) {
- int inverseX = mImageWidth - centerX;
- int inverseY = mImageHeight - centerY;
- TileImageView t = mImageView;
- int rotation = mImageRotation;
- switch (rotation) {
- case 0: return t.setPosition(centerX, centerY, scale, 0);
- case 90: return t.setPosition(centerY, inverseX, scale, 90);
- case 180: return t.setPosition(inverseX, inverseY, scale, 180);
- case 270: return t.setPosition(inverseY, centerX, scale, 270);
- default: throw new IllegalArgumentException(String.valueOf(rotation));
- }
- }
-
- @Override
- public void render(GLCanvas canvas) {
- AnimationController a = mAnimation;
- if (a.calculate(AnimationTime.get())) invalidate();
- setImageViewPosition(a.getCenterX(), a.getCenterY(), a.getScale());
- super.render(canvas);
- }
-
- @Override
- public void renderBackground(GLCanvas canvas) {
- canvas.clearBuffer();
- }
-
- public RectF getCropRectangle() {
- if (mHighlightRectangle.getVisibility() == GLView.INVISIBLE) return null;
- RectF rect = mHighlightRectangle.mHighlightRect;
- RectF result = new RectF(rect.left * mImageWidth, rect.top * mImageHeight,
- rect.right * mImageWidth, rect.bottom * mImageHeight);
- return result;
- }
-
- public int getImageWidth() {
- return mImageWidth;
- }
-
- public int getImageHeight() {
- return mImageHeight;
- }
-
- private class FaceHighlightView extends GLView {
- private static final int INDEX_NONE = -1;
- private ArrayList<RectF> mFaces = new ArrayList<RectF>();
- private RectF mRect = new RectF();
- private int mPressedFaceIndex = INDEX_NONE;
-
- public void addFace(RectF faceRect) {
- mFaces.add(faceRect);
- invalidate();
- }
-
- private void renderFace(GLCanvas canvas, RectF face, boolean pressed) {
- GL11 gl = canvas.getGLInstance();
- if (pressed) {
- gl.glEnable(GL11.GL_STENCIL_TEST);
- gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
- gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
- }
-
- RectF r = mAnimation.mapRect(face, mRect);
- canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mFacePaint);
-
- if (pressed) {
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
- }
- }
-
- @Override
- protected void renderBackground(GLCanvas canvas) {
- ArrayList<RectF> faces = mFaces;
- for (int i = 0, n = faces.size(); i < n; ++i) {
- renderFace(canvas, faces.get(i), i == mPressedFaceIndex);
- }
-
- GL11 gl = canvas.getGLInstance();
- if (mPressedFaceIndex != INDEX_NONE) {
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- canvas.fillRect(0, 0, getWidth(), getHeight(), 0x66000000);
- gl.glDisable(GL11.GL_STENCIL_TEST);
- }
- }
-
- private void setPressedFace(int index) {
- if (mPressedFaceIndex == index) return;
- mPressedFaceIndex = index;
- invalidate();
- }
-
- private int getFaceIndexByPosition(float x, float y) {
- ArrayList<RectF> faces = mFaces;
- for (int i = 0, n = faces.size(); i < n; ++i) {
- RectF r = mAnimation.mapRect(faces.get(i), mRect);
- if (r.contains(x, y)) return i;
- }
- return INDEX_NONE;
- }
-
- @Override
- protected boolean onTouch(MotionEvent event) {
- float x = event.getX();
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE: {
- setPressedFace(getFaceIndexByPosition(x, y));
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- int index = mPressedFaceIndex;
- setPressedFace(INDEX_NONE);
- if (index != INDEX_NONE) {
- mHighlightRectangle.setRectangle(mFaces.get(index));
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- setVisibility(GLView.INVISIBLE);
- }
- }
- }
- return true;
- }
- }
-
- private class AnimationController extends Animation {
- private int mCurrentX;
- private int mCurrentY;
- private float mCurrentScale;
- private int mStartX;
- private int mStartY;
- private float mStartScale;
- private int mTargetX;
- private int mTargetY;
- private float mTargetScale;
-
- public AnimationController() {
- setDuration(ANIMATION_DURATION);
- setInterpolator(new DecelerateInterpolator(4));
- }
-
- public void initialize() {
- mCurrentX = mImageWidth / 2;
- mCurrentY = mImageHeight / 2;
- mCurrentScale = Math.min(2, Math.min(
- (float) getWidth() / mImageWidth,
- (float) getHeight() / mImageHeight));
- }
-
- public void startParkingAnimation(RectF highlight) {
- RectF r = mAnimation.mapRect(highlight, new RectF());
- int width = getWidth();
- int height = getHeight();
-
- float wr = r.width() / width;
- float hr = r.height() / height;
- final int d = ANIMATION_TRIGGER;
- if (wr >= MIN_SELECTION_RATIO && wr < MAX_SELECTION_RATIO
- && hr >= MIN_SELECTION_RATIO && hr < MAX_SELECTION_RATIO
- && r.left >= d && r.right < width - d
- && r.top >= d && r.bottom < height - d) return;
-
- mStartX = mCurrentX;
- mStartY = mCurrentY;
- mStartScale = mCurrentScale;
- calculateTarget(highlight);
- start();
- }
-
- public void parkNow(RectF highlight) {
- calculateTarget(highlight);
- forceStop();
- mStartX = mCurrentX = mTargetX;
- mStartY = mCurrentY = mTargetY;
- mStartScale = mCurrentScale = mTargetScale;
- }
-
- public void inverseMapPoint(PointF point) {
- float s = mCurrentScale;
- point.x = Utils.clamp(((point.x - getWidth() * 0.5f) / s
- + mCurrentX) / mImageWidth, 0, 1);
- point.y = Utils.clamp(((point.y - getHeight() * 0.5f) / s
- + mCurrentY) / mImageHeight, 0, 1);
- }
-
- public RectF mapRect(RectF input, RectF output) {
- float offsetX = getWidth() * 0.5f;
- float offsetY = getHeight() * 0.5f;
- int x = mCurrentX;
- int y = mCurrentY;
- float s = mCurrentScale;
- output.set(
- offsetX + (input.left * mImageWidth - x) * s,
- offsetY + (input.top * mImageHeight - y) * s,
- offsetX + (input.right * mImageWidth - x) * s,
- offsetY + (input.bottom * mImageHeight - y) * s);
- return output;
- }
-
- @Override
- protected void onCalculate(float progress) {
- mCurrentX = Math.round(mStartX + (mTargetX - mStartX) * progress);
- mCurrentY = Math.round(mStartY + (mTargetY - mStartY) * progress);
- mCurrentScale = mStartScale + (mTargetScale - mStartScale) * progress;
-
- if (mCurrentX == mTargetX && mCurrentY == mTargetY
- && mCurrentScale == mTargetScale) forceStop();
- }
-
- public int getCenterX() {
- return mCurrentX;
- }
-
- public int getCenterY() {
- return mCurrentY;
- }
-
- public float getScale() {
- return mCurrentScale;
- }
-
- private void calculateTarget(RectF highlight) {
- float width = getWidth();
- float height = getHeight();
-
- if (mImageWidth != SIZE_UNKNOWN) {
- float minScale = Math.min(width / mImageWidth, height / mImageHeight);
- float scale = Utils.clamp(SELECTION_RATIO * Math.min(
- width / (highlight.width() * mImageWidth),
- height / (highlight.height() * mImageHeight)), minScale, 2f);
- int centerX = Math.round(
- mImageWidth * (highlight.left + highlight.right) * 0.5f);
- int centerY = Math.round(
- mImageHeight * (highlight.top + highlight.bottom) * 0.5f);
-
- if (Math.round(mImageWidth * scale) > width) {
- int limitX = Math.round(width * 0.5f / scale);
- centerX = Math.round(
- (highlight.left + highlight.right) * mImageWidth / 2);
- centerX = Utils.clamp(centerX, limitX, mImageWidth - limitX);
- } else {
- centerX = mImageWidth / 2;
- }
- if (Math.round(mImageHeight * scale) > height) {
- int limitY = Math.round(height * 0.5f / scale);
- centerY = Math.round(
- (highlight.top + highlight.bottom) * mImageHeight / 2);
- centerY = Utils.clamp(centerY, limitY, mImageHeight - limitY);
- } else {
- centerY = mImageHeight / 2;
- }
- mTargetX = centerX;
- mTargetY = centerY;
- mTargetScale = scale;
- }
- }
-
- }
-
- private class HighlightRectangle extends GLView {
- private RectF mHighlightRect = new RectF(0.25f, 0.25f, 0.75f, 0.75f);
- private RectF mTempRect = new RectF();
- private PointF mTempPoint = new PointF();
-
- private ResourceTexture mArrow;
-
- private int mMovingEdges = 0;
- private float mReferenceX;
- private float mReferenceY;
-
- public HighlightRectangle() {
- mArrow = new ResourceTexture(mActivity.getAndroidContext(),
- R.drawable.camera_crop_holo);
- }
-
- public void setInitRectangle() {
- float targetRatio = mAspectRatio == UNSPECIFIED
- ? 1f
- : mAspectRatio * mImageHeight / mImageWidth;
- float w = SELECTION_RATIO / 2f;
- float h = SELECTION_RATIO / 2f;
- if (targetRatio > 1) {
- h = w / targetRatio;
- } else {
- w = h * targetRatio;
- }
- mHighlightRect.set(0.5f - w, 0.5f - h, 0.5f + w, 0.5f + h);
- }
-
- public void setRectangle(RectF faceRect) {
- mHighlightRect.set(faceRect);
- mAnimation.startParkingAnimation(faceRect);
- invalidate();
- }
-
- private void moveEdges(MotionEvent event) {
- float scale = mAnimation.getScale();
- float dx = (event.getX() - mReferenceX) / scale / mImageWidth;
- float dy = (event.getY() - mReferenceY) / scale / mImageHeight;
- mReferenceX = event.getX();
- mReferenceY = event.getY();
- RectF r = mHighlightRect;
-
- if ((mMovingEdges & MOVE_BLOCK) != 0) {
- dx = Utils.clamp(dx, -r.left, 1 - r.right);
- dy = Utils.clamp(dy, -r.top , 1 - r.bottom);
- r.top += dy;
- r.bottom += dy;
- r.left += dx;
- r.right += dx;
- } else {
- PointF point = mTempPoint;
- point.set(mReferenceX, mReferenceY);
- mAnimation.inverseMapPoint(point);
- float left = r.left + MIN_SELECTION_LENGTH / mImageWidth;
- float right = r.right - MIN_SELECTION_LENGTH / mImageWidth;
- float top = r.top + MIN_SELECTION_LENGTH / mImageHeight;
- float bottom = r.bottom - MIN_SELECTION_LENGTH / mImageHeight;
- if ((mMovingEdges & MOVE_RIGHT) != 0) {
- r.right = Utils.clamp(point.x, left, 1f);
- }
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(point.x, 0, right);
- }
- if ((mMovingEdges & MOVE_TOP) != 0) {
- r.top = Utils.clamp(point.y, 0, bottom);
- }
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(point.y, top, 1f);
- }
- if (mAspectRatio != UNSPECIFIED) {
- float targetRatio = mAspectRatio * mImageHeight / mImageWidth;
- if (r.width() / r.height() > targetRatio) {
- float height = r.width() / targetRatio;
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(r.top + height, top, 1f);
- } else {
- r.top = Utils.clamp(r.bottom - height, 0, bottom);
- }
- } else {
- float width = r.height() * targetRatio;
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(r.right - width, 0, right);
- } else {
- r.right = Utils.clamp(r.left + width, left, 1f);
- }
- }
- if (r.width() / r.height() > targetRatio) {
- float width = r.height() * targetRatio;
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(r.right - width, 0, right);
- } else {
- r.right = Utils.clamp(r.left + width, left, 1f);
- }
- } else {
- float height = r.width() / targetRatio;
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(r.top + height, top, 1f);
- } else {
- r.top = Utils.clamp(r.bottom - height, 0, bottom);
- }
- }
- }
- }
- invalidate();
- }
-
- private void setMovingEdges(MotionEvent event) {
- RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
- float x = event.getX();
- float y = event.getY();
-
- if (x > r.left + TOUCH_TOLERANCE && x < r.right - TOUCH_TOLERANCE
- && y > r.top + TOUCH_TOLERANCE && y < r.bottom - TOUCH_TOLERANCE) {
- mMovingEdges = MOVE_BLOCK;
- return;
- }
-
- boolean inVerticalRange = (r.top - TOUCH_TOLERANCE) <= y
- && y <= (r.bottom + TOUCH_TOLERANCE);
- boolean inHorizontalRange = (r.left - TOUCH_TOLERANCE) <= x
- && x <= (r.right + TOUCH_TOLERANCE);
-
- if (inVerticalRange) {
- boolean left = Math.abs(x - r.left) <= TOUCH_TOLERANCE;
- boolean right = Math.abs(x - r.right) <= TOUCH_TOLERANCE;
- if (left && right) {
- left = Math.abs(x - r.left) < Math.abs(x - r.right);
- right = !left;
- }
- if (left) mMovingEdges |= MOVE_LEFT;
- if (right) mMovingEdges |= MOVE_RIGHT;
- if (mAspectRatio != UNSPECIFIED && inHorizontalRange) {
- mMovingEdges |= (y >
- (r.top + r.bottom) / 2) ? MOVE_BOTTOM : MOVE_TOP;
- }
- }
- if (inHorizontalRange) {
- boolean top = Math.abs(y - r.top) <= TOUCH_TOLERANCE;
- boolean bottom = Math.abs(y - r.bottom) <= TOUCH_TOLERANCE;
- if (top && bottom) {
- top = Math.abs(y - r.top) < Math.abs(y - r.bottom);
- bottom = !top;
- }
- if (top) mMovingEdges |= MOVE_TOP;
- if (bottom) mMovingEdges |= MOVE_BOTTOM;
- if (mAspectRatio != UNSPECIFIED && inVerticalRange) {
- mMovingEdges |= (x >
- (r.left + r.right) / 2) ? MOVE_RIGHT : MOVE_LEFT;
- }
- }
- }
-
- @Override
- protected boolean onTouch(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- mReferenceX = event.getX();
- mReferenceY = event.getY();
- setMovingEdges(event);
- invalidate();
- return true;
- }
- case MotionEvent.ACTION_MOVE:
- moveEdges(event);
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- mMovingEdges = 0;
- mAnimation.startParkingAnimation(mHighlightRect);
- invalidate();
- return true;
- }
- }
- return true;
- }
-
- @Override
- protected void renderBackground(GLCanvas canvas) {
- RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
- drawHighlightRectangle(canvas, r);
-
- float centerY = (r.top + r.bottom) / 2;
- float centerX = (r.left + r.right) / 2;
- boolean notMoving = mMovingEdges == 0;
- if ((mMovingEdges & MOVE_RIGHT) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(r.right - mArrow.getWidth() / 2),
- Math.round(centerY - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_LEFT) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(r.left - mArrow.getWidth() / 2),
- Math.round(centerY - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_TOP) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(centerX - mArrow.getWidth() / 2),
- Math.round(r.top - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_BOTTOM) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(centerX - mArrow.getWidth() / 2),
- Math.round(r.bottom - mArrow.getHeight() / 2));
- }
- }
-
- private void drawHighlightRectangle(GLCanvas canvas, RectF r) {
- GL11 gl = canvas.getGLInstance();
- gl.glLineWidth(3.0f);
- gl.glEnable(GL11.GL_LINE_SMOOTH);
-
- gl.glEnable(GL11.GL_STENCIL_TEST);
- gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
- gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
-
- if (mSpotlightRatioX == 0 || mSpotlightRatioY == 0) {
- canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
- } else {
- float sx = r.width() * mSpotlightRatioX;
- float sy = r.height() * mSpotlightRatioY;
- float cx = r.centerX();
- float cy = r.centerY();
-
- canvas.fillRect(cx - sx / 2, cy - sy / 2, sx, sy, Color.TRANSPARENT);
- canvas.drawRect(cx - sx / 2, cy - sy / 2, sx, sy, mPaint);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
-
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
-
- canvas.drawRect(cx - sy / 2, cy - sx / 2, sy, sx, mPaint);
- canvas.fillRect(cx - sy / 2, cy - sx / 2, sy, sx, Color.TRANSPARENT);
- canvas.fillRect(r.left, r.top, r.width(), r.height(), 0x80000000);
- }
-
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
-
- canvas.fillRect(0, 0, getWidth(), getHeight(), 0xA0000000);
-
- gl.glDisable(GL11.GL_STENCIL_TEST);
- }
- }
-
- private class DetectFaceTask extends Thread {
- private final FaceDetector.Face[] mFaces = new FaceDetector.Face[MAX_FACE_COUNT];
- private final Bitmap mFaceBitmap;
- private int mFaceCount;
-
- public DetectFaceTask(Bitmap bitmap) {
- mFaceBitmap = bitmap;
- setName("face-detect");
- }
-
- @Override
- public void run() {
- Bitmap bitmap = mFaceBitmap;
- FaceDetector detector = new FaceDetector(
- bitmap.getWidth(), bitmap.getHeight(), MAX_FACE_COUNT);
- mFaceCount = detector.findFaces(bitmap, mFaces);
- mMainHandler.sendMessage(
- mMainHandler.obtainMessage(MSG_UPDATE_FACES, this));
- }
-
- private RectF getFaceRect(FaceDetector.Face face) {
- PointF point = new PointF();
- face.getMidPoint(point);
-
- int width = mFaceBitmap.getWidth();
- int height = mFaceBitmap.getHeight();
- float rx = face.eyesDistance() * FACE_EYE_RATIO;
- float ry = rx;
- float aspect = mAspectRatio;
- if (aspect != UNSPECIFIED) {
- if (aspect > 1) {
- rx = ry * aspect;
- } else {
- ry = rx / aspect;
- }
- }
-
- RectF r = new RectF(
- point.x - rx, point.y - ry, point.x + rx, point.y + ry);
- r.intersect(0, 0, width, height);
-
- if (aspect != UNSPECIFIED) {
- if (r.width() / r.height() > aspect) {
- float w = r.height() * aspect;
- r.left = (r.left + r.right - w) * 0.5f;
- r.right = r.left + w;
- } else {
- float h = r.width() / aspect;
- r.top = (r.top + r.bottom - h) * 0.5f;
- r.bottom = r.top + h;
- }
- }
-
- r.left /= width;
- r.right /= width;
- r.top /= height;
- r.bottom /= height;
- return r;
- }
-
- public void updateFaces() {
- if (mFaceCount > 1) {
- for (int i = 0, n = mFaceCount; i < n; ++i) {
- mFaceDetectionView.addFace(getFaceRect(mFaces[i]));
- }
- mFaceDetectionView.setVisibility(GLView.VISIBLE);
- Toast.makeText(mActivity.getAndroidContext(),
- R.string.multiface_crop_help, Toast.LENGTH_SHORT).show();
- } else if (mFaceCount == 1) {
- mFaceDetectionView.setVisibility(GLView.INVISIBLE);
- mHighlightRectangle.setRectangle(getFaceRect(mFaces[0]));
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- } else /*mFaceCount == 0*/ {
- mHighlightRectangle.setInitRectangle();
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- }
- }
- }
-
- public void setDataModel(TileImageView.Model dataModel, int rotation) {
- if (((rotation / 90) & 0x01) != 0) {
- mImageWidth = dataModel.getImageHeight();
- mImageHeight = dataModel.getImageWidth();
- } else {
- mImageWidth = dataModel.getImageWidth();
- mImageHeight = dataModel.getImageHeight();
- }
-
- mImageRotation = rotation;
-
- mImageView.setModel(dataModel);
- mAnimation.initialize();
- }
-
- public void detectFaces(Bitmap bitmap) {
- int rotation = mImageRotation;
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- float scale = FloatMath.sqrt((float) FACE_PIXEL_COUNT / (width * height));
-
- // faceBitmap is a correctly rotated bitmap, as viewed by a user.
- Bitmap faceBitmap;
- if (((rotation / 90) & 1) == 0) {
- int w = (Math.round(width * scale) & ~1); // must be even
- int h = Math.round(height * scale);
- faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
- Canvas canvas = new Canvas(faceBitmap);
- canvas.rotate(rotation, w / 2, h / 2);
- canvas.scale((float) w / width, (float) h / height);
- canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
- } else {
- int w = (Math.round(height * scale) & ~1); // must be even
- int h = Math.round(width * scale);
- faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
- Canvas canvas = new Canvas(faceBitmap);
- canvas.translate(w / 2, h / 2);
- canvas.rotate(rotation);
- canvas.translate(-h / 2, -w / 2);
- canvas.scale((float) w / height, (float) h / width);
- canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
- }
- new DetectFaceTask(faceBitmap).start();
- }
-
- public void initializeHighlightRectangle() {
- mHighlightRectangle.setInitRectangle();
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- }
-
- public void resume() {
- mImageView.prepareTextures();
- }
-
- public void pause() {
- mImageView.freeTextures();
- }
-}
-
diff --git a/src/com/android/gallery3d/ui/DetailsHelper.java b/src/com/android/gallery3d/ui/DetailsHelper.java
index 301601156..47296f655 100644
--- a/src/com/android/gallery3d/ui/DetailsHelper.java
+++ b/src/com/android/gallery3d/ui/DetailsHelper.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.ui;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.view.View.MeasureSpec;
import com.android.gallery3d.R;
@@ -44,6 +46,10 @@ public class DetailsHelper {
public void hide();
}
+ public interface ResolutionResolvingListener {
+ public void onResolutionAvailable(int width, int height);
+ }
+
public DetailsHelper(AbstractGalleryActivity activity, GLView rootPane, DetailsSource source) {
mContainer = new DialogDetailsView(activity, source);
}
@@ -75,6 +81,12 @@ public class DetailsHelper {
return sAddressResolver.resolveAddress(latlng, listener);
}
+ public static void resolveResolution(String path, ResolutionResolvingListener listener) {
+ Bitmap bitmap = BitmapFactory.decodeFile(path);
+ if (bitmap == null) return;
+ listener.onResolutionAvailable(bitmap.getWidth(), bitmap.getHeight());
+ }
+
public static void pause() {
if (sAddressResolver != null) sAddressResolver.cancel();
}
diff --git a/src/com/android/gallery3d/ui/DialogDetailsView.java b/src/com/android/gallery3d/ui/DialogDetailsView.java
index 8d96b821a..058c03654 100644
--- a/src/com/android/gallery3d/ui/DialogDetailsView.java
+++ b/src/com/android/gallery3d/ui/DialogDetailsView.java
@@ -37,6 +37,7 @@ import com.android.gallery3d.ui.DetailsAddressResolver.AddressResolvingListener;
import com.android.gallery3d.ui.DetailsHelper.CloseListener;
import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
import com.android.gallery3d.ui.DetailsHelper.DetailsViewContainer;
+import com.android.gallery3d.ui.DetailsHelper.ResolutionResolvingListener;
import java.util.ArrayList;
import java.util.Map.Entry;
@@ -111,9 +112,13 @@ public class DialogDetailsView implements DetailsViewContainer {
});
}
- private class DetailsAdapter extends BaseAdapter implements AddressResolvingListener {
+
+ private class DetailsAdapter extends BaseAdapter
+ implements AddressResolvingListener, ResolutionResolvingListener {
private final ArrayList<String> mItems;
private int mLocationIndex;
+ private int mWidthIndex = -1;
+ private int mHeightIndex = -1;
public DetailsAdapter(MediaDetails details) {
Context context = mActivity.getAndroidContext();
@@ -123,6 +128,8 @@ public class DialogDetailsView implements DetailsViewContainer {
}
private void setDetails(Context context, MediaDetails details) {
+ boolean resolutionIsValid = true;
+ String path = null;
for (Entry<Integer, Object> detail : details) {
String value;
switch (detail.getKey()) {
@@ -170,6 +177,26 @@ public class DialogDetailsView implements DetailsViewContainer {
}
break;
}
+ case MediaDetails.INDEX_WIDTH:
+ mWidthIndex = mItems.size();
+ value = detail.getValue().toString();
+ if (value.equalsIgnoreCase("0")) {
+ value = context.getString(R.string.unknown);
+ resolutionIsValid = false;
+ }
+ break;
+ case MediaDetails.INDEX_HEIGHT: {
+ mHeightIndex = mItems.size();
+ value = detail.getValue().toString();
+ if (value.equalsIgnoreCase("0")) {
+ value = context.getString(R.string.unknown);
+ resolutionIsValid = false;
+ }
+ break;
+ }
+ case MediaDetails.INDEX_PATH:
+ // Get the path and then fall through to the default case
+ path = detail.getValue().toString();
default: {
Object valueObj = detail.getValue();
// This shouldn't happen, log its key to help us diagnose the problem.
@@ -189,6 +216,9 @@ public class DialogDetailsView implements DetailsViewContainer {
context, key), value);
}
mItems.add(value);
+ if (!resolutionIsValid) {
+ DetailsHelper.resolveResolution(path, this);
+ }
}
}
@@ -235,6 +265,20 @@ public class DialogDetailsView implements DetailsViewContainer {
mItems.set(mLocationIndex, address);
notifyDataSetChanged();
}
+
+ @Override
+ public void onResolutionAvailable(int width, int height) {
+ if (width == 0 || height == 0) return;
+ // Update the resolution with the new width and height
+ Context context = mActivity.getAndroidContext();
+ String widthString = String.format("%s: %d", DetailsHelper.getDetailsName(
+ context, MediaDetails.INDEX_WIDTH), width);
+ String heightString = String.format("%s: %d", DetailsHelper.getDetailsName(
+ context, MediaDetails.INDEX_HEIGHT), height);
+ mItems.set(mWidthIndex, String.valueOf(widthString));
+ mItems.set(mHeightIndex, String.valueOf(heightString));
+ notifyDataSetChanged();
+ }
}
@Override
diff --git a/src/com/android/gallery3d/ui/ExtTexture.java b/src/com/android/gallery3d/ui/ExtTexture.java
index eac504fe5..180a89dce 100644
--- a/src/com/android/gallery3d/ui/ExtTexture.java
+++ b/src/com/android/gallery3d/ui/ExtTexture.java
@@ -16,49 +16,21 @@
package com.android.gallery3d.ui;
-import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
// ExtTexture is a texture whose content comes from a external texture.
// Before drawing, setSize() should be called.
public class ExtTexture extends BasicTexture {
- private static int[] sTextureId = new int[1];
- private static float[] sCropRect = new float[4];
private int mTarget;
public ExtTexture(int target) {
- GLId.glGenTextures(1, sTextureId, 0);
- mId = sTextureId[0];
+ GLId glId = GLCanvas.getGLId();
+ mId = glId.generateTexture();
mTarget = target;
}
private void uploadToCanvas(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
-
- int width = getWidth();
- int height = getHeight();
- // Define a vertically flipped crop rectangle for OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = 0;
- sCropRect[1] = height;
- sCropRect[2] = width;
- sCropRect[3] = -height;
-
- // Set texture parameters.
- gl.glBindTexture(mTarget, mId);
- gl.glTexParameterfv(mTarget,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(mTarget,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(mTarget,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(mTarget,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(mTarget,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
-
+ canvas.setTextureParameters(this);
setAssociatedCanvas(canvas);
mState = STATE_LOADED;
}
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index 6f8baef7e..1dbee5dd9 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -16,8 +16,11 @@
package com.android.gallery3d.ui;
+import android.graphics.Bitmap;
import android.graphics.RectF;
+import com.android.gallery3d.common.ApiHelper;
+
import javax.microedition.khronos.opengles.GL11;
//
@@ -26,107 +29,236 @@ import javax.microedition.khronos.opengles.GL11;
// When a rectangle is specified in this interface, it means the region
// [x, x+width) * [y, y+height)
//
-public interface GLCanvas {
+public abstract class GLCanvas {
+ public enum Blending {
+ Additive, Mix,
+ }
+
+ private static GLCanvas sInstance = instantiateCanvas();
+ private static GLId sGLId = instantiateGLId();
+
+ public static GLId getGLId() {
+ return sGLId;
+ }
+
+ public static GLCanvas getInstance() {
+ return sInstance;
+ }
+
+ private static GLId instantiateGLId() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? (GLES20Canvas) sInstance : new GLIdImpl();
+ }
+
+ private static GLCanvas instantiateCanvas() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? new GLES20Canvas() : new GLCanvasImpl();
+ }
+
+ public static int getEGLContextClientVersion() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? 2 : 1;
+ }
+
+ public abstract void initialize(GL11 gl);
+
// Tells GLCanvas the size of the underlying GL surface. This should be
// called before first drawing and when the size of GL surface is changed.
// This is called by GLRoot and should not be called by the clients
// who only want to draw on the GLCanvas. Both width and height must be
// nonnegative.
- public void setSize(int width, int height);
+ public abstract void setSize(int width, int height);
// Clear the drawing buffers. This should only be used by GLRoot.
- public void clearBuffer();
- public void clearBuffer(float[] argb);
+ public abstract void clearBuffer();
+
+ public abstract void clearBuffer(float[] argb);
// Sets and gets the current alpha, alpha must be in [0, 1].
- public void setAlpha(float alpha);
- public float getAlpha();
+ public abstract void setAlpha(float alpha);
+
+ public abstract float getAlpha();
// (current alpha) = (current alpha) * alpha
- public void multiplyAlpha(float alpha);
+ public abstract void multiplyAlpha(float alpha);
// Change the current transform matrix.
- public void translate(float x, float y, float z);
- public void translate(float x, float y);
- public void scale(float sx, float sy, float sz);
- public void rotate(float angle, float x, float y, float z);
- public void multiplyMatrix(float[] mMatrix, int offset);
+ public abstract void translate(float x, float y, float z);
+
+ public abstract void translate(float x, float y);
+
+ public abstract void scale(float sx, float sy, float sz);
+
+ public abstract void rotate(float angle, float x, float y, float z);
+
+ public abstract void multiplyMatrix(float[] mMatrix, int offset);
// Pushes the configuration state (matrix, and alpha) onto
// a private stack.
- public void save();
+ public abstract void save();
// Same as save(), but only save those specified in saveFlags.
- public void save(int saveFlags);
+ public abstract void save(int saveFlags);
public static final int SAVE_FLAG_ALL = 0xFFFFFFFF;
public static final int SAVE_FLAG_ALPHA = 0x01;
public static final int SAVE_FLAG_MATRIX = 0x02;
+ public static final int SAVE_FLAG_BLEND = 0x04;
// Pops from the top of the stack as current configuration state (matrix,
// alpha, and clip). This call balances a previous call to save(), and is
// used to remove all modifications to the configuration state since the
// last save call.
- public void restore();
+ public abstract void restore();
// Draws a line using the specified paint from (x1, y1) to (x2, y2).
// (Both end points are included).
- public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
+ public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
// Draws a rectangle using the specified paint from (x1, y1) to (x2, y2).
// (Both end points are included).
- public void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
+ public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
// Fills the specified rectangle with the specified color.
- public void fillRect(float x, float y, float width, float height, int color);
+ public abstract void fillRect(float x, float y, float width, float height, int color);
// Draws a texture to the specified rectangle.
- public void drawTexture(
+ public abstract void drawTexture(
BasicTexture texture, int x, int y, int width, int height);
- public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
+
+ public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
int uvBuffer, int indexBuffer, int indexCount);
// Draws the source rectangle part of the texture to the target rectangle.
- public void drawTexture(BasicTexture texture, RectF source, RectF target);
+ public abstract void drawTexture(BasicTexture texture, RectF source, RectF target);
// Draw a texture with a specified texture transform.
- public void drawTexture(BasicTexture texture, float[] mTextureTransform,
+ public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform,
int x, int y, int w, int h);
// Draw two textures to the specified rectangle. The actual texture used is
// from * (1 - ratio) + to * ratio
// The two textures must have the same size.
- public void drawMixed(BasicTexture from, int toColor,
+ public abstract 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,
+ public abstract 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();
-
// Unloads the specified texture from the canvas. The resource allocated
// to draw the texture will be released. The specified texture will return
// to the unloaded state. This function should be called only from
// BasicTexture or its descendant
- public boolean unloadTexture(BasicTexture texture);
+ public abstract boolean unloadTexture(BasicTexture texture);
// Delete the specified buffer object, similar to unloadTexture.
- public void deleteBuffer(int bufferId);
+ public abstract void deleteBuffer(int bufferId);
// Delete the textures and buffers in GL side. This function should only be
// called in the GL thread.
- public void deleteRecycledResources();
+ public abstract void deleteRecycledResources();
// Dump statistics information and clear the counters. For debug only.
- public void dumpStatisticsAndClear();
+ public abstract void dumpStatisticsAndClear();
+
+ public abstract void beginRenderTarget(RawTexture texture);
+
+ public abstract void endRenderTarget();
+
+ /**
+ * Sets texture parameters to use GL_CLAMP_TO_EDGE for both
+ * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be
+ * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER.
+ * bindTexture() must be called prior to this.
+ *
+ * @param texture The texture to set parameters on.
+ */
+ public abstract void setTextureParameters(BasicTexture texture);
+
+ /**
+ * Initializes the texture to a size by calling texImage2D on it.
+ *
+ * @param texture The texture to initialize the size.
+ * @param format The texture format (e.g. GL_RGBA)
+ * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
+ */
+ public abstract void initializeTextureSize(BasicTexture texture, int format, int type);
+
+ /**
+ * Initializes the texture to a size by calling texImage2D on it.
+ *
+ * @param texture The texture to initialize the size.
+ * @param bitmap The bitmap to initialize the bitmap with.
+ */
+ public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap);
+
+ /**
+ * Calls glTexSubImage2D to upload a bitmap to the texture.
+ *
+ * @param texture The target texture to write to.
+ * @param xOffset Specifies a texel offset in the x direction within the
+ * texture array.
+ * @param yOffset Specifies a texel offset in the y direction within the
+ * texture array.
+ * @param format The texture format (e.g. GL_RGBA)
+ * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
+ */
+ public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset,
+ Bitmap bitmap,
+ int format, int type);
+
+ /**
+ * Generates buffers and uploads the buffer data.
+ *
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
+ */
+ public abstract int uploadBuffer(java.nio.FloatBuffer buffer);
+
+ /**
+ * Generates buffers and uploads the element array buffer data.
+ *
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
+ */
+ public abstract int uploadBuffer(java.nio.ByteBuffer buffer);
+
+ /**
+ * Sets the blending algorithm if a texture is not opaque.
+ *
+ * @param blending Either mixing (overlay) or adding a texture.
+ */
+ public abstract void setBlending(Blending blending);
+
+ /**
+ * Enable stencil test
+ */
+ public abstract void enableStencil();
+
+ /**
+ * Disable stencil.
+ */
+ public abstract void disableStencil();
+
+ /**
+ * Clears the stencil so that a new stencil can be generated.
+ */
+ public abstract void clearStencilBuffer();
- public void beginRenderTarget(RawTexture texture);
+ /**
+ * Start/stop updating the stencil buffer.
+ *
+ * @param update True if the stencil should be updated, false otherwise.
+ */
+ public abstract void updateStencil(boolean update);
- public void endRenderTarget();
+ /**
+ * Changes how the stencil buffer is used.
+ *
+ * @param onlyOutside If true, only the area outside the stencil can be
+ * changed. If false, the area inside the stencil can be drawn to
+ * as well.
+ */
+ public abstract void drawOnlyOutsideStencil(boolean onlyOutside);
}
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index 45903b3cd..54c231c3b 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -16,13 +16,16 @@
package com.android.gallery3d.ui;
+import android.graphics.Bitmap;
import android.graphics.RectF;
import android.opengl.GLU;
+import android.opengl.GLUtils;
import android.opengl.Matrix;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.util.IntArray;
+import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
@@ -33,7 +36,7 @@ import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
-public class GLCanvasImpl implements GLCanvas {
+public class GLCanvasImpl extends GLCanvas {
@SuppressWarnings("unused")
private static final String TAG = "GLCanvasImp";
@@ -47,7 +50,7 @@ public class GLCanvasImpl implements GLCanvas {
0, 0, 1, 1, // used for drawing a line
0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
- private final GL11 mGL;
+ private GL11 mGL;
private final float mMatrixValues[] = new float[16];
private final float mTextureMatrixValues[] = new float[16];
@@ -60,7 +63,7 @@ public class GLCanvasImpl implements GLCanvas {
private int mBoxCoords;
- private final GLState mGLState;
+ private GLState mGLState;
private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
private float mAlpha;
@@ -76,8 +79,10 @@ public class GLCanvasImpl implements GLCanvas {
private int mScreenHeight;
private boolean mBlendEnabled = true;
private int mFrameBuffer[] = new int[1];
+ private static float[] sCropRect = new float[4];
private RawTexture mTargetTexture;
+ private Blending mBlending = Blending.Mix;
// Drawing statistics
int mCountDrawLine;
@@ -86,10 +91,7 @@ public class GLCanvasImpl implements GLCanvas {
int mCountTextureRect;
int mCountTextureOES;
- GLCanvasImpl(GL11 gl) {
- mGL = gl;
- mGLState = new GLState(gl);
- initialize();
+ GLCanvasImpl() {
}
@Override
@@ -141,16 +143,18 @@ public class GLCanvasImpl implements GLCanvas {
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
}
- private void initialize() {
- GL11 gl = mGL;
-
+ @Override
+ public void initialize(GL11 gl) {
+ mGL = gl;
+ mGLState = new GLState(gl);
// First create an nio buffer, then create a VBO from it.
int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
int[] name = new int[1];
- GLId.glGenBuffers(1, name, 0);
+ GLId glId = getGLId();
+ glId.glGenBuffers(1, name, 0);
mBoxCoords = name[0];
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
@@ -684,11 +688,6 @@ public class GLCanvasImpl implements GLCanvas {
}
@Override
- public GL11 getGLInstance() {
- return mGL;
- }
-
- @Override
public void clearBuffer(float[] argb) {
if(argb != null && argb.length == 4) {
mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]);
@@ -748,14 +747,15 @@ public class GLCanvasImpl implements GLCanvas {
public void deleteRecycledResources() {
synchronized (mUnboundTextures) {
IntArray ids = mUnboundTextures;
+ GLId glId = getGLId();
if (ids.size() > 0) {
- GLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
+ glId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
ids = mDeleteBuffers;
if (ids.size() > 0) {
- GLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
+ glId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
}
@@ -776,6 +776,11 @@ public class GLCanvasImpl implements GLCanvas {
config.mAlpha = -1;
}
+ if ((saveFlags & SAVE_FLAG_BLEND) != 0) {
+ config.mBlending = mBlending;
+ } else {
+ config.mBlending = null;
+ }
if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
@@ -811,6 +816,7 @@ public class GLCanvasImpl implements GLCanvas {
private static class ConfigState {
float mAlpha;
float mMatrix[] = new float[16];
+ Blending mBlending;
ConfigState mNextFree;
public void restore(GLCanvasImpl canvas) {
@@ -818,6 +824,9 @@ public class GLCanvasImpl implements GLCanvas {
if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
}
+ if (mBlending != null) {
+ canvas.setBlending(mBlending);
+ }
}
}
@@ -847,7 +856,8 @@ public class GLCanvasImpl implements GLCanvas {
GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL;
if (mTargetTexture == null && texture != null) {
- GLId.glGenBuffers(1, mFrameBuffer, 0);
+ GLId glId = getGLId();
+ glId.glGenBuffers(1, mFrameBuffer, 0);
gl11ep.glBindFramebufferOES(
GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]);
}
@@ -917,4 +927,110 @@ public class GLCanvasImpl implements GLCanvas {
throw new RuntimeException(msg + ":" + Integer.toHexString(status));
}
}
+
+ @Override
+ public void setTextureParameters(BasicTexture texture) {
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ // Define a vertically flipped crop rectangle for OES_draw_texture.
+ // The four values in sCropRect are: left, bottom, width, and
+ // height. Negative value of width or height means flip.
+ sCropRect[0] = 0;
+ sCropRect[1] = height;
+ sCropRect[2] = width;
+ sCropRect[3] = -height;
+
+ // Set texture parameters.
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
+ mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ }
+
+ @Override
+ public void initializeTextureSize(BasicTexture texture, int format, int type) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ int width = texture.getTextureWidth();
+ int height = texture.getTextureHeight();
+ mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ }
+
+ @Override
+ public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ }
+
+ @Override
+ public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
+ int format, int type) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ }
+
+ @Override
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
+ }
+
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
+
+ private int uploadBuffer(Buffer buf, int elementSize) {
+ int[] bufferIds = new int[1];
+ GLId glId = getGLId();
+ glId.glGenBuffers(bufferIds.length, bufferIds, 0);
+ int bufferId = bufferIds[0];
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
+ mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
+ GL11.GL_STATIC_DRAW);
+ return bufferId;
+ }
+
+ @Override
+ public void setBlending(Blending blending) {
+ if (mBlending == blending) {
+ return;
+ }
+ Utils.assertTrue(blending == Blending.Additive || blending == Blending.Mix);
+ mBlending = blending;
+ int srcFunc = GL11.GL_ONE;
+ int dstFunc = (blending == Blending.Additive) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA;
+ mGL.glBlendFunc(srcFunc, dstFunc);
+ }
+
+ @Override
+ public void enableStencil() {
+ mGL.glEnable(GL11.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void disableStencil() {
+ mGL.glDisable(GL11.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void clearStencilBuffer() {
+ mGL.glClear(GL11.GL_STENCIL_BUFFER_BIT);
+ }
+
+ @Override
+ public void updateStencil(boolean update) {
+ int passOp = update ? GL11.GL_REPLACE : GL11.GL_KEEP;
+ mGL.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, passOp);
+ }
+
+ @Override
+ public void drawOnlyOutsideStencil(boolean onlyOutside) {
+ int func = onlyOutside ? GL11.GL_NOTEQUAL : GL11.GL_ALWAYS;
+ mGL.glStencilFunc(func, 1, 1);
+ }
}
diff --git a/src/com/android/gallery3d/ui/GLES20Canvas.java b/src/com/android/gallery3d/ui/GLES20Canvas.java
new file mode 100644
index 000000000..b720a773b
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLES20Canvas.java
@@ -0,0 +1,1068 @@
+/*
+ * 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 android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.util.IntArray;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+public class GLES20Canvas extends GLCanvas implements GLId {
+ // ************** Constants **********************
+ private static final String TAG = GLES20Canvas.class.getSimpleName();
+ private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
+ private static final float OPAQUE_ALPHA = 0.95f;
+
+ private static final int COORDS_PER_VERTEX = 2;
+ private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
+
+ private static final int COUNT_FILL_VERTEX = 4;
+ private static final int COUNT_LINE_VERTEX = 2;
+ private static final int COUNT_RECT_VERTEX = 4;
+ private static final int OFFSET_FILL_RECT = 0;
+ private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
+ private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
+
+ private static final float[] BOX_COORDINATES = {
+ 0, 0, // Fill rectangle
+ 1, 0,
+ 0, 1,
+ 1, 1,
+ 0, 0, // Draw line
+ 1, 1,
+ 0, 0, // Draw rectangle outline
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ };
+
+ private static final String POSITION_ATTRIBUTE = "aPosition";
+ private static final String COLOR_UNIFORM = "uColor";
+ private static final String MATRIX_UNIFORM = "uMatrix";
+ private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
+ private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
+ private static final String ALPHA_UNIFORM = "uAlpha";
+ private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
+
+ private static final String DRAW_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + "}\n";
+
+ private static final String DRAW_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "uniform vec4 " + COLOR_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = " + COLOR_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
+ + "}\n";
+
+ private static final String MESH_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
+ + "#extension GL_OES_EGL_image_external : require\n"
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final int INITIAL_RESTORE_STATE_SIZE = 8;
+ private static final int MATRIX_SIZE = 16;
+
+ // Keep track of restore state
+ private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
+ private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
+ private IntArray mSaveFlags = new IntArray();
+ private ArrayList<Blending> mBlendings = new ArrayList<Blending>();
+
+ private int mCurrentAlphaIndex = 0;
+ private int mCurrentMatrixIndex = 0;
+
+ // Viewport size
+ private int mWidth;
+ private int mHeight;
+
+ // Projection matrix
+ private float[] mProjectionMatrix = new float[MATRIX_SIZE];
+
+ // Screen size for when we aren't bound to a texture
+ private int mScreenWidth;
+ private int mScreenHeight;
+
+ // GL programs
+ private int mDrawProgram;
+ private int mTextureProgram;
+ private int mOesTextureProgram;
+ private int mMeshProgram;
+
+ // GL buffer containing BOX_COORDINATES
+ private int mBoxCoordinates;
+
+ // Handle indices -- common
+ private static final int INDEX_POSITION = 0;
+ private static final int INDEX_MATRIX = 1;
+
+ // Handle indices -- draw
+ private static final int INDEX_COLOR = 2;
+
+ // Handle indices -- texture
+ private static final int INDEX_TEXTURE_MATRIX = 2;
+ private static final int INDEX_TEXTURE_SAMPLER = 3;
+ private static final int INDEX_ALPHA = 4;
+
+ // Handle indices -- mesh
+ private static final int INDEX_TEXTURE_COORD = 2;
+
+ private abstract static class ShaderParameter {
+ public int handle;
+ protected final String mName;
+
+ public ShaderParameter(String name) {
+ mName = name;
+ }
+
+ public abstract void loadHandle(int program);
+ }
+
+ private static class UniformShaderParameter extends ShaderParameter {
+ public UniformShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetUniformLocation(program, mName);
+ checkError();
+ }
+ }
+
+ private static class AttributeShaderParameter extends ShaderParameter {
+ public AttributeShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetAttribLocation(program, mName);
+ checkError();
+ }
+ }
+
+ ShaderParameter[] mDrawParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
+ };
+ ShaderParameter[] mTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mOesTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mMeshParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+
+ private final IntArray mUnboundTextures = new IntArray();
+ private final IntArray mDeleteBuffers = new IntArray();
+
+ // Keep track of statistics for debugging
+ private int mCountDrawMesh = 0;
+ private int mCountTextureRect = 0;
+ private int mCountFillRect = 0;
+ private int mCountDrawLine = 0;
+
+ private int mNextTextureId = 1;
+
+ // Buffer for framebuffer IDs -- we keep track so we can switch the attached
+ // texture.
+ private int[] mFrameBuffer = new int[1];
+
+ // Bound textures.
+ private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
+
+ // Temporary variables used within calculations
+ private final float[] mTempMatrix = new float[32];
+ private final float[] mTempColor = new float[4];
+ private final RectF mTempSourceRect = new RectF();
+ private final RectF mTempTargetRect = new RectF();
+ private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
+ private final int[] mTempIntArray = new int[1];
+
+ public GLES20Canvas() {
+ Matrix.setIdentityM(mTempTextureMatrix, 0);
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ mAlphas[mCurrentAlphaIndex] = 1f;
+ mTargetTextures.add(null);
+ }
+
+ @Override
+ public void initialize(GL11 gl) {
+ FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
+ mBoxCoordinates = uploadBuffer(boxBuffer);
+
+ int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
+ int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
+ int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
+ int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
+ int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
+ int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
+ OES_TEXTURE_FRAGMENT_SHADER);
+
+ mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
+ mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
+ mTextureParameters);
+ mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
+ mOesTextureParameters);
+ mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
+
+ mBlendings.clear();
+ mBlendings.add(null);
+ setBlending(Blending.Mix);
+ }
+
+ private static FloatBuffer createBuffer(float[] values) {
+ // First create an nio buffer, then create a VBO from it.
+ int size = values.length * FLOAT_SIZE;
+ FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ buffer.put(values, 0, values.length).position(0);
+ return buffer;
+ }
+
+ private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
+ int program = GLES20.glCreateProgram();
+ checkError();
+ if (program == 0) {
+ throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
+ }
+ GLES20.glAttachShader(program, vertexShader);
+ checkError();
+ GLES20.glAttachShader(program, fragmentShader);
+ checkError();
+ GLES20.glLinkProgram(program);
+ checkError();
+ int[] mLinkStatus = mTempIntArray;
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
+ if (mLinkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ for (int i = 0; i < params.length; i++) {
+ params[i].loadHandle(program);
+ }
+ return program;
+ }
+
+ private static int loadShader(int type, String shaderCode) {
+ // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
+ // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
+ int shader = GLES20.glCreateShader(type);
+
+ // add the source code to the shader and compile it
+ GLES20.glShaderSource(shader, shaderCode);
+ checkError();
+ GLES20.glCompileShader(shader);
+ checkError();
+
+ return shader;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ GLES20.glViewport(0, 0, mWidth, mHeight);
+ checkError();
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
+ if (getTargetTexture() == null) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
+ }
+ }
+
+ @Override
+ public void clearBuffer() {
+ GLES20.glClearColor(0f, 0f, 0f, 1f);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public void clearBuffer(float[] argb) {
+ GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public float getAlpha() {
+ return mAlphas[mCurrentAlphaIndex];
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ mAlphas[mCurrentAlphaIndex] = alpha;
+ }
+
+ @Override
+ public void multiplyAlpha(float alpha) {
+ setAlpha(getAlpha() * alpha);
+ }
+
+ @Override
+ public void translate(float x, float y, float z) {
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
+ }
+
+ // This is a faster version of translate(x, y, z) because
+ // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
+ // (3) we unroll the loop
+ @Override
+ public void translate(float x, float y) {
+ int index = mCurrentMatrixIndex;
+ float[] m = mMatrices;
+ m[index + 12] += m[index + 0] * x + m[index + 4] * y;
+ m[index + 13] += m[index + 1] * x + m[index + 5] * y;
+ m[index + 14] += m[index + 2] * x + m[index + 6] * y;
+ m[index + 15] += m[index + 3] * x + m[index + 7] * y;
+ }
+
+ @Override
+ public void scale(float sx, float sy, float sz) {
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
+ }
+
+ @Override
+ public void rotate(float angle, float x, float y, float z) {
+ if (angle == 0f) {
+ return;
+ }
+ float[] temp = mTempMatrix;
+ Matrix.setRotateM(temp, 0, angle, x, y, z);
+ float[] matrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
+ System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
+ }
+
+ @Override
+ public void multiplyMatrix(float[] matrix, int offset) {
+ float[] temp = mTempMatrix;
+ float[] currentMatrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
+ System.arraycopy(temp, 0, currentMatrix, index, 16);
+ }
+
+ @Override
+ public void save() {
+ save(SAVE_FLAG_ALL);
+ }
+
+ @Override
+ public void save(int saveFlags) {
+ boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (saveAlpha) {
+ float currentAlpha = getAlpha();
+ mCurrentAlphaIndex++;
+ if (mAlphas.length <= mCurrentAlphaIndex) {
+ mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
+ }
+ mAlphas[mCurrentAlphaIndex] = currentAlpha;
+ }
+ boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (saveMatrix) {
+ int currentIndex = mCurrentMatrixIndex;
+ mCurrentMatrixIndex += MATRIX_SIZE;
+ if (mMatrices.length <= mCurrentMatrixIndex) {
+ mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
+ }
+ System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
+ }
+ boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (saveBlending) {
+ mBlendings.add(mBlendings.get(mBlendings.size() - 1));
+ }
+ mSaveFlags.add(saveFlags);
+ }
+
+ @Override
+ public void restore() {
+ int restoreFlags = mSaveFlags.removeLast();
+ boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (restoreAlpha) {
+ mCurrentAlphaIndex--;
+ }
+ boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (restoreMatrix) {
+ mCurrentMatrixIndex -= MATRIX_SIZE;
+ }
+ boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (restoreBlending) {
+ setBlending(mBlendings.get(mBlendings.size() - 2));
+ mBlendings.remove(mBlendings.size() - 1);
+ }
+ }
+
+ @Override
+ public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
+ draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
+ paint);
+ mCountDrawLine++;
+ }
+
+ @Override
+ public void drawRect(float x, float y, float width, float height, GLPaint paint) {
+ draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
+ mCountDrawLine++;
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ GLPaint paint) {
+ draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ int color, float lineWidth) {
+ prepareDraw(offset, color, lineWidth);
+ draw(mDrawParameters, type, count, x, y, width, height);
+ }
+
+ private void prepareDraw(int offset, int color, float lineWidth) {
+ GLES20.glUseProgram(mDrawProgram);
+ checkError();
+ if (lineWidth > 0) {
+ GLES20.glLineWidth(lineWidth);
+ checkError();
+ }
+ float[] colorArray = getColor(color);
+ boolean blendingEnabled = (colorArray[3] < 1f);
+ enableBlending(blendingEnabled);
+ if (blendingEnabled) {
+ GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
+ checkError();
+ }
+
+ GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
+ setPosition(mDrawParameters, offset);
+ checkError();
+ }
+
+ private float[] getColor(int color) {
+ float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
+ float red = ((color >>> 16) & 0xFF) / 255f * alpha;
+ float green = ((color >>> 8) & 0xFF) / 255f * alpha;
+ float blue = (color & 0xFF) / 255f * alpha;
+ mTempColor[0] = red;
+ mTempColor[1] = green;
+ mTempColor[2] = blue;
+ mTempColor[3] = alpha;
+ return mTempColor;
+ }
+
+ private void enableBlending(boolean enableBlending) {
+ if (enableBlending) {
+ GLES20.glEnable(GLES20.GL_BLEND);
+ checkError();
+ } else {
+ GLES20.glDisable(GLES20.GL_BLEND);
+ checkError();
+ }
+ }
+
+ private void setPosition(ShaderParameter[] params, int offset) {
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
+ checkError();
+ GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
+ GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
+ checkError();
+ }
+
+ private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
+ float height) {
+ setMatrix(params, x, y, width, height);
+ int positionHandle = params[INDEX_POSITION].handle;
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDrawArrays(type, 0, count);
+ checkError();
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ }
+
+ private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
+ Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
+ Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
+ Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
+ GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
+ checkError();
+ }
+
+ @Override
+ public void fillRect(float x, float y, float width, float height, int color) {
+ draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
+ color, 0f);
+ mCountFillRect++;
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + width, y + height);
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
+ int left = 0;
+ int top = 0;
+ int right = texture.getWidth();
+ int bottom = texture.getHeight();
+ if (texture.hasBorder()) {
+ left = 1;
+ top = 1;
+ right -= 1;
+ bottom -= 1;
+ }
+ outRect.set(left, top, right, bottom);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ mTempSourceRect.set(source);
+ mTempTargetRect.set(target);
+
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
+ int h) {
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawTextureRect(texture, textureTransform, mTempTargetRect);
+ }
+
+ private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
+ setTextureMatrix(source);
+ drawTextureRect(texture, mTempTextureMatrix, target);
+ }
+
+ private void setTextureMatrix(RectF source) {
+ mTempTextureMatrix[0] = source.width();
+ mTempTextureMatrix[5] = source.height();
+ mTempTextureMatrix[12] = source.left;
+ mTempTextureMatrix[13] = source.top;
+ }
+
+ // 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 static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ int texWidth = texture.getTextureWidth();
+ int texHeight = texture.getTextureHeight();
+ // Convert to texture coordinates
+ source.left /= texWidth;
+ source.right /= texWidth;
+ source.top /= texHeight;
+ source.bottom /= texHeight;
+
+ // Clip if the rendering range is beyond the bound of the texture.
+ float xBound = (float) width / texWidth;
+ if (source.right > xBound) {
+ target.right = target.left + target.width() * (xBound - source.left) / source.width();
+ source.right = xBound;
+ }
+ float yBound = (float) height / texHeight;
+ if (source.bottom > yBound) {
+ target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
+ source.bottom = yBound;
+ }
+ }
+
+ private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
+ ShaderParameter[] params = prepareTexture(texture);
+ setPosition(params, OFFSET_FILL_RECT);
+ GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
+ checkError();
+ draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
+ target.width(), target.height());
+ mCountTextureRect++;
+ }
+
+ private ShaderParameter[] prepareTexture(BasicTexture texture) {
+ ShaderParameter[] params;
+ int program;
+ if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
+ params = mTextureParameters;
+ program = mTextureProgram;
+ } else {
+ params = mOesTextureParameters;
+ program = mOesTextureProgram;
+ }
+ prepareTexture(texture, program, params);
+ return params;
+ }
+
+ private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
+ GLES20.glUseProgram(program);
+ checkError();
+ enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ checkError();
+ texture.onBind(this);
+ GLES20.glBindTexture(texture.getTarget(), texture.getId());
+ checkError();
+ GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
+ checkError();
+ GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
+ checkError();
+ }
+
+ @Override
+ public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
+ int indexBuffer, int indexCount) {
+ prepareTexture(texture, mMeshProgram, mMeshParameters);
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
+ checkError();
+ int positionHandle = mMeshParameters[INDEX_POSITION].handle;
+ GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
+ VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
+ checkError();
+ int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
+ GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
+ false, VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glEnableVertexAttribArray(texCoordHandle);
+ checkError();
+
+ setMatrix(mMeshParameters, x, y, 1, 1);
+ GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
+ checkError();
+
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDisableVertexAttribArray(texCoordHandle);
+ checkError();
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
+ checkError();
+ mCountDrawMesh++;
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ save(SAVE_FLAG_ALPHA);
+
+ float currentAlpha = getAlpha();
+ float cappedRatio = Math.min(1f, Math.max(0f, ratio));
+
+ float textureAlpha = (1f - cappedRatio) * currentAlpha;
+ setAlpha(textureAlpha);
+ drawTexture(texture, source, target);
+
+ float colorAlpha = cappedRatio * currentAlpha;
+ setAlpha(colorAlpha);
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+
+ restore();
+ }
+
+ @Override
+ public boolean unloadTexture(BasicTexture texture) {
+ boolean unload = texture.isLoaded();
+ if (unload) {
+ synchronized (mUnboundTextures) {
+ mUnboundTextures.add(texture.getId());
+ }
+ }
+ return unload;
+ }
+
+ @Override
+ public void deleteBuffer(int bufferId) {
+ synchronized (mUnboundTextures) {
+ mDeleteBuffers.add(bufferId);
+ }
+ }
+
+ @Override
+ public void deleteRecycledResources() {
+ synchronized (mUnboundTextures) {
+ IntArray ids = mUnboundTextures;
+ if (mUnboundTextures.size() > 0) {
+ glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+
+ ids = mDeleteBuffers;
+ if (ids.size() > 0) {
+ glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+ }
+ }
+
+ @Override
+ public void dumpStatisticsAndClear() {
+ String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
+ mCountTextureRect, mCountFillRect, mCountDrawLine);
+ mCountDrawMesh = 0;
+ mCountTextureRect = 0;
+ mCountFillRect = 0;
+ mCountDrawLine = 0;
+ Log.d(TAG, line);
+ }
+
+ @Override
+ public void endRenderTarget() {
+ RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
+ RawTexture texture = getTargetTexture();
+ setRenderTarget(oldTexture, texture);
+ restore(); // restore matrix and alpha
+ }
+
+ @Override
+ public void beginRenderTarget(RawTexture texture) {
+ save(); // save matrix and alpha and blending
+ RawTexture oldTexture = getTargetTexture();
+ mTargetTextures.add(texture);
+ setRenderTarget(oldTexture, texture);
+ }
+
+ private RawTexture getTargetTexture() {
+ return mTargetTextures.get(mTargetTextures.size() - 1);
+ }
+
+ private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
+ if (oldTexture == null && texture != null) {
+ GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
+ checkError();
+ } else if (oldTexture != null && texture == null) {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ checkError();
+ GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ }
+
+ if (texture == null) {
+ setSize(mScreenWidth, mScreenHeight);
+ } else {
+ setSize(texture.getWidth(), texture.getHeight());
+
+ if (!texture.isLoaded()) {
+ texture.prepare(this);
+ }
+
+ GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
+ texture.getTarget(), texture.getId(), 0);
+ checkError();
+
+ checkFramebufferStatus();
+ }
+ }
+
+ private static void checkFramebufferStatus() {
+ int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
+ if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
+ String msg = "";
+ switch (status) {
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
+ msg = "GL_FRAMEBUFFER_UNSUPPORTED";
+ break;
+ }
+ throw new RuntimeException(msg + ":" + Integer.toHexString(status));
+ }
+ }
+
+ @Override
+ public void setTextureParameters(BasicTexture texture) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ }
+
+ @Override
+ public void initializeTextureSize(BasicTexture texture, int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ int width = texture.getTextureWidth();
+ int height = texture.getTextureHeight();
+ GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ }
+
+ @Override
+ public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ }
+
+ @Override
+ public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
+ int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ }
+
+ @Override
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, FLOAT_SIZE);
+ }
+
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
+
+ private int uploadBuffer(Buffer buffer, int elementSize) {
+ glGenBuffers(1, mTempIntArray, 0);
+ checkError();
+ int bufferId = mTempIntArray[0];
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
+ checkError();
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
+ GLES20.GL_STATIC_DRAW);
+ checkError();
+ return bufferId;
+ }
+
+ @Override
+ public void setBlending(Blending blending) {
+ Blending currentBlending = mBlendings.get(mBlendings.size() - 1);
+ if (currentBlending == blending) {
+ return; // nothing to change
+ }
+ mBlendings.set(mBlendings.size() - 1, blending);
+ int srcFunc = GLES20.GL_ONE;
+ int dstFunc;
+ switch (blending) {
+ case Additive:
+ dstFunc = GLES20.GL_ONE;
+ break;
+ case Mix:
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ default:
+ Utils.fail("Unknown blend: " + blending);
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ GLES20.glBlendFunc(srcFunc, dstFunc);
+ checkError();
+ }
+
+ @Override
+ public int generateTexture() {
+ // Can use anything as a lock. No need to create a new object.
+ synchronized (mTempIntArray) {
+ return mNextTextureId++;
+ }
+ }
+
+ @Override
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ GLES20.glGenBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
+ GLES20.glDeleteTextures(n, textures, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
+ GLES20.glDeleteBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
+ GLES20.glDeleteFramebuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void enableStencil() {
+ GLES20.glEnable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void disableStencil() {
+ GLES20.glDisable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void clearStencilBuffer() {
+ GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);
+ }
+
+ @Override
+ public void updateStencil(boolean update) {
+ int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP;
+ GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp);
+ }
+
+ @Override
+ public void drawOnlyOutsideStencil(boolean onlyOutside) {
+ int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS;
+ GLES20.glStencilFunc(func, 1, 1);
+ }
+
+ private static void checkError() {
+ int error = GLES20.glGetError();
+ if (error != 0) {
+ Throwable t = new Throwable();
+ Log.e(TAG, "GL error: " + error, t);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void printMatrix(String message, float[] m, int offset) {
+ StringBuilder b = new StringBuilder(message);
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ b.append(' ');
+ if (i % 4 == 0) {
+ b.append('\n');
+ }
+ b.append(m[offset + i]);
+ }
+ Log.v(TAG, b.toString());
+ }
+
+}
diff --git a/src/com/android/gallery3d/ui/GLId.java b/src/com/android/gallery3d/ui/GLId.java
index 689cf192e..04977c337 100644
--- a/src/com/android/gallery3d/ui/GLId.java
+++ b/src/com/android/gallery3d/ui/GLId.java
@@ -20,31 +20,14 @@ import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
// This mimics corresponding GL functions.
-public class GLId {
- static int sNextId = 1;
+public interface GLId {
+ public int generateTexture();
- public synchronized static void glGenTextures(int n, int[] textures, int offset) {
- while (n-- > 0) {
- textures[offset + n] = sNextId++;
- }
- }
+ public void glGenBuffers(int n, int[] buffers, int offset);
- public synchronized static void glGenBuffers(int n, int[] buffers, int offset) {
- while (n-- > 0) {
- buffers[offset + n] = sNextId++;
- }
- }
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset);
- public synchronized static void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
- gl.glDeleteTextures(n, textures, offset);
- }
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset);
- public synchronized static void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
- gl.glDeleteBuffers(n, buffers, offset);
- }
-
- public synchronized static void glDeleteFramebuffers(
- GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
- gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
- }
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset);
}
diff --git a/src/com/android/gallery3d/ui/GLIdImpl.java b/src/com/android/gallery3d/ui/GLIdImpl.java
new file mode 100644
index 000000000..7a0232bf0
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLIdImpl.java
@@ -0,0 +1,68 @@
+/*
+ * 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 javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+/**
+ * Open GL ES 1.1 implementation for generating and destroying texture IDs and
+ * buffer IDs
+ */
+public class GLIdImpl implements GLId {
+ private static int sNextId = 1;
+ // Mutex for sNextId
+ private static Object sLock = new Object();
+
+ @Override
+ public int generateTexture() {
+ synchronized (sLock) {
+ return sNextId++;
+ }
+ }
+
+ @Override
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ while (n-- > 0) {
+ buffers[offset + n] = sNextId++;
+ }
+ }
+ }
+
+ @Override
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
+ synchronized (sLock) {
+ gl.glDeleteTextures(n, textures, offset);
+ }
+ }
+
+ @Override
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ gl.glDeleteBuffers(n, buffers, offset);
+ }
+ }
+
+ @Override
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
+ }
+ }
+
+
+}
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index b7c48bf2e..6b76999b6 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -117,6 +117,7 @@ public class GLRootView extends GLSurfaceView
super(context, attrs);
mFlags |= FLAG_INITIALIZED;
setBackgroundDrawable(null);
+ setEGLContextClientVersion(GLCanvas.getEGLContextClientVersion());
setEGLConfigChooser(mEglConfigChooser);
setRenderer(this);
if (ApiHelper.USE_888_PIXEL_FORMAT) {
@@ -283,7 +284,8 @@ public class GLRootView extends GLSurfaceView
mRenderLock.lock();
try {
mGL = gl;
- mCanvas = new GLCanvasImpl(gl);
+ mCanvas = GLCanvas.getInstance();
+ mCanvas.initialize(gl);
BasicTexture.invalidateAllTextures();
} finally {
mRenderLock.unlock();
diff --git a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
index deeb3b76d..f57a312cb 100644
--- a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
+++ b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
@@ -49,12 +49,35 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGL10.EGL_NONE
};
+ private final int mConfig2Spec565[] = new int[] {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
+ private final int mConfig2Spec888[] = new int[] {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] numConfig = new int[1];
- int mConfigSpec[] = ApiHelper.USE_888_PIXEL_FORMAT
- ? mConfigSpec888 : mConfigSpec565;
- if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, numConfig)) {
+
+ int configSpec[];
+ if (GLCanvas.getEGLContextClientVersion() == 2) {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfig2Spec888 : mConfig2Spec565;
+ } else {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfigSpec888 : mConfigSpec565;
+ }
+ if (!egl.eglChooseConfig(display, configSpec, null, 0, numConfig)) {
throw new RuntimeException("eglChooseConfig failed");
}
@@ -64,7 +87,7 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGLConfig[] configs = new EGLConfig[numConfig[0]];
if (!egl.eglChooseConfig(display,
- mConfigSpec, configs, configs.length, numConfig)) {
+ configSpec, configs, configs.length, numConfig)) {
throw new RuntimeException();
}
diff --git a/src/com/android/gallery3d/ui/GestureRecognizer.java b/src/com/android/gallery3d/ui/GestureRecognizer.java
index e4e0c49f5..1e5250b9b 100644
--- a/src/com/android/gallery3d/ui/GestureRecognizer.java
+++ b/src/com/android/gallery3d/ui/GestureRecognizer.java
@@ -32,7 +32,7 @@ public class GestureRecognizer {
boolean onSingleTapUp(float x, float y);
boolean onDoubleTap(float x, float y);
boolean onScroll(float dx, float dy, float totalX, float totalY);
- boolean onFling(float velocityX, float velocityY);
+ boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
boolean onScaleBegin(float focusX, float focusY);
boolean onScale(float focusX, float focusY, float scale);
void onScaleEnd();
@@ -94,7 +94,7 @@ public class GestureRecognizer {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
- return mListener.onFling(velocityX, velocityY);
+ return mListener.onFling(e1, e2, velocityX, velocityY);
}
}
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index f432333ce..a9eeaf89c 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -26,12 +26,12 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
-import android.view.Menu;
-import android.view.MenuItem;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
-import com.android.gallery3d.app.CropImage;
+import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
@@ -62,6 +62,7 @@ public class MenuExecutor {
private Future<?> mTask;
// wait the operation to finish when we want to stop it.
private boolean mWaitOnStop;
+ private boolean mPaused;
private final AbstractGalleryActivity mActivity;
private final SelectionManager mSelectionManager;
@@ -113,7 +114,7 @@ public class MenuExecutor {
break;
}
case MSG_TASK_UPDATE: {
- if (mDialog != null) mDialog.setProgress(message.arg1);
+ if (mDialog != null && !mPaused) mDialog.setProgress(message.arg1);
if (message.obj != null) {
ProgressListener listener = (ProgressListener) message.obj;
listener.onProgressUpdate(message.arg1);
@@ -133,13 +134,23 @@ public class MenuExecutor {
if (mTask != null) {
if (!mWaitOnStop) mTask.cancel();
mTask.waitDone();
- mDialog.dismiss();
+ if (mDialog != null && mDialog.isShowing()) mDialog.dismiss();
mDialog = null;
mTask = null;
}
}
+ public void resume() {
+ mPaused = false;
+ if (mDialog != null) mDialog.show();
+ }
+
public void pause() {
+ mPaused = true;
+ if (mDialog != null && mDialog.isShowing()) mDialog.hide();
+ }
+
+ public void destroy() {
stopTaskAndDismissDialog();
}
@@ -161,6 +172,7 @@ public class MenuExecutor {
boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
+ boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0;
boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
@@ -174,6 +186,7 @@ public class MenuExecutor {
setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate);
setMenuItemVisible(menu, R.id.action_crop, supportCrop);
setMenuItemVisible(menu, R.id.action_trim, supportTrim);
+ setMenuItemVisible(menu, R.id.action_mute, supportMute);
// Hide panorama until call to updateMenuForPanorama corrects it
setMenuItemVisible(menu, R.id.action_share_panorama, false);
setMenuItemVisible(menu, R.id.action_share, supportShare);
@@ -332,7 +345,7 @@ public class MenuExecutor {
mDialog.show();
}
MediaOperation operation = new MediaOperation(action, ids, listener);
- mTask = mActivity.getThreadPool().submit(operation, null);
+ mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null);
mWaitOnStop = waitOnStop;
}
diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java
index fa0e9cdc3..f5c614554 100644
--- a/src/com/android/gallery3d/ui/NinePatchTexture.java
+++ b/src/com/android/gallery3d/ui/NinePatchTexture.java
@@ -27,8 +27,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
-import javax.microedition.khronos.opengles.GL11;
-
// NinePatchTexture is a texture backed by a NinePatch resource.
//
// getPaddings() returns paddings specified in the NinePatch.
@@ -199,7 +197,9 @@ class NinePatchInstance {
private ByteBuffer mIndexBuffer;
// Names for buffer names: xy, uv, index.
- private int[] mBufferNames;
+ private int mXyBufferName = -1;
+ private int mUvBufferName;
+ private int mIndexBufferName;
private int mIdxCount;
@@ -396,24 +396,9 @@ class NinePatchInstance {
}
private void prepareBuffers(GLCanvas canvas) {
- mBufferNames = new int[3];
- GL11 gl = canvas.getGLInstance();
- GLId.glGenBuffers(3, mBufferNames, 0);
-
- gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[0]);
- gl.glBufferData(GL11.GL_ARRAY_BUFFER,
- mXyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
- mXyBuffer, GL11.GL_STATIC_DRAW);
-
- gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[1]);
- gl.glBufferData(GL11.GL_ARRAY_BUFFER,
- mUvBuffer.capacity() * (Float.SIZE / Byte.SIZE),
- mUvBuffer, GL11.GL_STATIC_DRAW);
-
- gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mBufferNames[2]);
- gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER,
- mIndexBuffer.capacity(),
- mIndexBuffer, GL11.GL_STATIC_DRAW);
+ mXyBufferName = canvas.uploadBuffer(mXyBuffer);
+ mUvBufferName = canvas.uploadBuffer(mUvBuffer);
+ mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
// These buffers are never used again.
mXyBuffer = null;
@@ -422,19 +407,18 @@ class NinePatchInstance {
}
public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
- if (mBufferNames == null) {
+ if (mXyBufferName == -1) {
prepareBuffers(canvas);
}
- canvas.drawMesh(tex, x, y, mBufferNames[0], mBufferNames[1],
- mBufferNames[2], mIdxCount);
+ canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
}
public void recycle(GLCanvas canvas) {
- if (mBufferNames != null) {
- canvas.deleteBuffer(mBufferNames[0]);
- canvas.deleteBuffer(mBufferNames[1]);
- canvas.deleteBuffer(mBufferNames[2]);
- mBufferNames = null;
+ if (mXyBuffer == null) {
+ canvas.deleteBuffer(mXyBufferName);
+ canvas.deleteBuffer(mUvBufferName);
+ canvas.deleteBuffer(mIndexBufferName);
+ mXyBufferName = -1;
}
}
}
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 6dcae4ca4..58389e4ae 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -174,8 +174,9 @@ public class PhotoView extends GLView {
public static final int SCREEN_NAIL_MAX = 3;
// These are constants for the delete gesture.
- private static final int SWIPE_ESCAPE_VELOCITY = 2500; // dp/sec
- private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
+ private static final int SWIPE_ESCAPE_VELOCITY = 500; // dp/sec
+ private static final int MAX_DISMISS_VELOCITY = 2500; // dp/sec
+ private static final int SWIPE_ESCAPE_DISTANCE = 150; // dp
// The picture entries, the valid index is from -SCREEN_NAIL_MAX to
// SCREEN_NAIL_MAX.
@@ -1070,19 +1071,19 @@ public class PhotoView extends GLView {
}
@Override
- public boolean onFling(float velocityX, float velocityY) {
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIgnoreSwipingGesture) return true;
if (mModeChanged) return true;
if (swipeImages(velocityX, velocityY)) {
mIgnoreUpEvent = true;
} else {
- flingImages(velocityX, velocityY);
+ flingImages(velocityX, velocityY, Math.abs(e2.getY() - e1.getY()));
}
mHadFling = true;
return true;
}
- private boolean flingImages(float velocityX, float velocityY) {
+ private boolean flingImages(float velocityX, float velocityY, float dY) {
int vx = (int) (velocityX + 0.5f);
int vy = (int) (velocityY + 0.5f);
if (!mFilmMode) {
@@ -1099,11 +1100,13 @@ public class PhotoView extends GLView {
}
int maxVelocity = GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY);
int escapeVelocity = GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY);
+ int escapeDistance = GalleryUtils.dpToPixel(SWIPE_ESCAPE_DISTANCE);
int centerY = mPositionController.getPosition(mTouchBoxIndex)
.centerY();
boolean fastEnough = (Math.abs(vy) > escapeVelocity)
&& (Math.abs(vy) > Math.abs(vx))
- && ((vy > 0) == (centerY > getHeight() / 2));
+ && ((vy > 0) == (centerY > getHeight() / 2))
+ && dY >= escapeDistance;
if (fastEnough) {
vy = Math.min(vy, maxVelocity);
int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy);
@@ -1237,7 +1240,10 @@ public class PhotoView extends GLView {
if (mFilmMode) {
int xi = (int) (x + 0.5f);
int yi = (int) (y + 0.5f);
- mTouchBoxIndex = mPositionController.hitTest(xi, yi);
+ // We only care about being within the x bounds, necessary for
+ // handling very wide images which are otherwise very hard to fling
+ mTouchBoxIndex = mPositionController.hitTest(xi, getHeight() / 2);
+
if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) {
mTouchBoxIndex = Integer.MAX_VALUE;
} else {
diff --git a/src/com/android/gallery3d/ui/PopupList.java b/src/com/android/gallery3d/ui/PopupList.java
index 248f50b25..dd6269380 100644
--- a/src/com/android/gallery3d/ui/PopupList.java
+++ b/src/com/android/gallery3d/ui/PopupList.java
@@ -159,7 +159,7 @@ public class PopupList {
R.drawable.menu_dropdown_panel_holo_dark));
mContentList = new ListView(mContext, null,
- android.R.attr.dropDownListViewStyle);
+ com.actionbarsherlock.R.attr.dropDownListViewStyle);
mContentList.setAdapter(new ItemDataAdapter());
mContentList.setOnItemClickListener(mOnItemClickListener);
popup.setContentView(mContentList);
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index 6a4bcea87..9069d5da2 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -18,10 +18,10 @@ package com.android.gallery3d.ui;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Build;
import android.util.Log;
import android.widget.Scroller;
-import com.android.gallery3d.app.PhotoPage;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.ui.PhotoView.Size;
import com.android.gallery3d.util.GalleryUtils;
@@ -211,7 +211,11 @@ class PositionController {
public PositionController(Context context, Listener listener) {
mListener = listener;
mPageScroller = new FlingScroller();
- mFilmScroller = new Scroller(context, null, false);
+ if (Build.VERSION.SDK_INT >= 11) {
+ mFilmScroller = new Scroller(context, null, false);
+ } else {
+ mFilmScroller = new Scroller(context, null);
+ }
// Initialize the areas.
initPlatform();
diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java
index 4c0d9d365..53aef9edc 100644
--- a/src/com/android/gallery3d/ui/RawTexture.java
+++ b/src/com/android/gallery3d/ui/RawTexture.java
@@ -16,15 +16,13 @@
package com.android.gallery3d.ui;
+import android.opengl.GLES20;
+
import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
public class RawTexture extends BasicTexture {
private static final String TAG = "RawTexture";
- private final static int[] sTextureId = new int[1];
- private final static float[] sCropRect = new float[4];
-
private final boolean mOpaque;
public RawTexture(int width, int height, boolean opaque) {
@@ -38,36 +36,10 @@ public class RawTexture extends BasicTexture {
}
protected void prepare(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
-
- // Define a vertically flipped crop rectangle for
- // OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = 0;
- sCropRect[1] = mHeight;
- sCropRect[2] = mWidth;
- sCropRect[3] = -mHeight;
-
- // Upload the bitmap to a new texture.
- GLId.glGenTextures(1, sTextureId, 0);
- gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]);
- gl.glTexParameterfv(GL11.GL_TEXTURE_2D,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
-
- gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
- getTextureWidth(), getTextureHeight(),
- 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, null);
-
- mId = sTextureId[0];
+ GLId glId = GLCanvas.getGLId();
+ mId = glId.generateTexture();
+ canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
+ canvas.setTextureParameters(this);
mState = STATE_LOADED;
setAssociatedCanvas(canvas);
}
diff --git a/src/com/android/gallery3d/ui/SlideshowView.java b/src/com/android/gallery3d/ui/SlideshowView.java
index bb36c47e9..7734eb236 100644
--- a/src/com/android/gallery3d/ui/SlideshowView.java
+++ b/src/com/android/gallery3d/ui/SlideshowView.java
@@ -21,11 +21,10 @@ import android.graphics.PointF;
import com.android.gallery3d.anim.CanvasAnimation;
import com.android.gallery3d.anim.FloatAnimation;
+import com.android.gallery3d.ui.GLCanvas.Blending;
import java.util.Random;
-import javax.microedition.khronos.opengles.GL11;
-
public class SlideshowView extends GLView {
@SuppressWarnings("unused")
private static final String TAG = "SlideshowView";
@@ -93,8 +92,8 @@ public class SlideshowView extends GLView {
protected void render(GLCanvas canvas) {
long animTime = AnimationTime.get();
boolean requestRender = mTransitionAnimation.calculate(animTime);
- GL11 gl = canvas.getGLInstance();
- gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
+ canvas.save(GLCanvas.SAVE_FLAG_BLEND);
+ canvas.setBlending(Blending.Additive);
float alpha = mPrevTexture == null ? 1f : mTransitionAnimation.get();
if (mPrevTexture != null && alpha != 1f) {
@@ -118,7 +117,7 @@ public class SlideshowView extends GLView {
canvas.restore();
}
if (requestRender) invalidate();
- gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ canvas.restore();
}
private class SlideshowAnimation extends CanvasAnimation {
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java
index bb86d05ef..470ee6a98 100644
--- a/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -25,7 +25,6 @@ import com.android.gallery3d.common.Utils;
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
// UploadedTextures use a Bitmap for the content of the texture.
//
@@ -194,9 +193,7 @@ abstract class UploadedTexture extends BasicTexture {
Bitmap bitmap = getBitmap();
int format = GLUtils.getInternalFormat(bitmap);
int type = GLUtils.getType(bitmap);
- canvas.getGLInstance().glBindTexture(GL11.GL_TEXTURE_2D, mId);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0, mBorder, mBorder,
- bitmap, format, type);
+ canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
freeBitmap();
mContentValid = true;
}
@@ -210,11 +207,7 @@ abstract class UploadedTexture extends BasicTexture {
return sUploadedCount > UPLOAD_LIMIT;
}
- static int[] sTextureId = new int[1];
- static float[] sCropRect = new float[4];
-
private void uploadToCanvas(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
Bitmap bitmap = getBitmap();
if (bitmap != null) {
@@ -228,65 +221,40 @@ abstract class UploadedTexture extends BasicTexture {
Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
- // Define a vertically flipped crop rectangle for
- // OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = mBorder;
- sCropRect[1] = mBorder + bHeight;
- sCropRect[2] = bWidth;
- sCropRect[3] = -bHeight;
-
// Upload the bitmap to a new texture.
- GLId.glGenTextures(1, sTextureId, 0);
- gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]);
- gl.glTexParameterfv(GL11.GL_TEXTURE_2D,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ mId = GLCanvas.getGLId().generateTexture();
+ canvas.setTextureParameters(this);
if (bWidth == texWidth && bHeight == texHeight) {
- GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
+ canvas.initializeTexture(this, bitmap);
} else {
int format = GLUtils.getInternalFormat(bitmap);
int type = GLUtils.getType(bitmap);
Config config = bitmap.getConfig();
- gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format,
- texWidth, texHeight, 0, format, type, null);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- mBorder, mBorder, bitmap, format, type);
+ canvas.initializeTextureSize(this, format, type);
+ canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
if (mBorder > 0) {
// Left border
Bitmap line = getBorderLine(true, config, texHeight);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, 0, line, format, type);
+ canvas.texSubImage2D(this, 0, 0, line, format, type);
// Top border
line = getBorderLine(false, config, texWidth);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, 0, line, format, type);
+ canvas.texSubImage2D(this, 0, 0, line, format, type);
}
// Right border
if (mBorder + bWidth < texWidth) {
Bitmap line = getBorderLine(true, config, texHeight);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- mBorder + bWidth, 0, line, format, type);
+ canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
}
// Bottom border
if (mBorder + bHeight < texHeight) {
Bitmap line = getBorderLine(false, config, texWidth);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, mBorder + bHeight, line, format, type);
+ canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
}
}
} finally {
@@ -294,7 +262,6 @@ abstract class UploadedTexture extends BasicTexture {
}
// Update texture state.
setAssociatedCanvas(canvas);
- mId = sTextureId[0];
mState = STATE_LOADED;
mContentValid = true;
} else {