Merge "Make original exif test pass with the exiftool ground truth" into gb-ub-photos-bryce
diff --git a/gallerycommon/src/com/android/gallery3d/common/Utils.java b/gallerycommon/src/com/android/gallery3d/common/Utils.java
index f5a2667..3a68745 100644
--- a/gallerycommon/src/com/android/gallery3d/common/Utils.java
+++ b/gallerycommon/src/com/android/gallery3d/common/Utils.java
@@ -76,7 +76,7 @@
// Throws IllegalArgumentException if the input is <= 0 or
// the answer overflows.
public static int nextPowerOf2(int n) {
- if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException();
+ if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
n -= 1;
n |= n >> 16;
n |= n >> 8;
diff --git a/src/com/android/gallery3d/anim/StateTransitionAnimation.java b/src/com/android/gallery3d/anim/StateTransitionAnimation.java
new file mode 100644
index 0000000..d4d59d3
--- /dev/null
+++ b/src/com/android/gallery3d/anim/StateTransitionAnimation.java
@@ -0,0 +1,155 @@
+/*
+ * 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.anim;
+
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.gallery3d.ui.GLCanvas;
+import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.RawTexture;
+import com.android.gallery3d.ui.TiledScreenNail;
+
+public class StateTransitionAnimation extends Animation {
+
+ public static class Spec {
+ public static final Spec OUTGOING;
+ public static final Spec INCOMING;
+
+ public int duration = 330;
+ public float backgroundAlphaFrom = 0;
+ public float backgroundAlphaTo = 0;
+ public float backgroundScaleFrom = 0;
+ public float backgroundScaleTo = 0;
+ public float contentAlphaFrom = 1;
+ public float contentAlphaTo = 1;
+ public float contentScaleFrom = 1;
+ public float contentScaleTo = 1;
+ public float overlayAlphaFrom = 0;
+ public float overlayAlphaTo = 0;
+ public float overlayScaleFrom = 0;
+ public float overlayScaleTo = 0;
+ public Interpolator interpolator;
+
+ static {
+ OUTGOING = new Spec();
+ OUTGOING.backgroundAlphaFrom = 1f;
+ OUTGOING.backgroundAlphaTo = 0f;
+ OUTGOING.backgroundScaleFrom = 1f;
+ OUTGOING.backgroundScaleTo = 0f;
+ OUTGOING.contentAlphaFrom = 0.9f;
+ OUTGOING.contentAlphaTo = 1f;
+ OUTGOING.contentScaleFrom = 3f;
+ OUTGOING.contentScaleTo = 1f;
+ OUTGOING.interpolator = new DecelerateInterpolator();
+
+ INCOMING = new Spec();
+ INCOMING.overlayAlphaFrom = 1f;
+ INCOMING.overlayAlphaTo = 0f;
+ INCOMING.overlayScaleFrom = 1f;
+ INCOMING.overlayScaleTo = 3f;
+ INCOMING.contentAlphaFrom = 0f;
+ INCOMING.contentAlphaTo = 1f;
+ INCOMING.contentScaleFrom = 0.25f;
+ INCOMING.contentScaleTo = 1f;
+ INCOMING.interpolator = new DecelerateInterpolator();
+ }
+ }
+
+ private final Spec mTransitionSpec;
+ private float mCurrentContentScale;
+ private float mCurrentContentAlpha;
+ private float mCurrentBackgroundScale;
+ private float mCurrentBackgroundAlpha;
+ private float mCurrentOverlayScale;
+ private float mCurrentOverlayAlpha;
+ private RawTexture mOldScreenTexture;
+
+ public StateTransitionAnimation(Spec spec, RawTexture oldScreen) {
+ mTransitionSpec = spec != null ? spec : Spec.OUTGOING;
+ setDuration(mTransitionSpec.duration);
+ setInterpolator(mTransitionSpec.interpolator);
+ mOldScreenTexture = oldScreen;
+ if (mOldScreenTexture != null) {
+ TiledScreenNail.disableDrawPlaceholder();
+ }
+ }
+
+ @Override
+ public boolean calculate(long currentTimeMillis) {
+ boolean retval = super.calculate(currentTimeMillis);
+ if (mOldScreenTexture != null && !isActive()) {
+ mOldScreenTexture.recycle();
+ mOldScreenTexture = null;
+ TiledScreenNail.enableDrawPlaceholder();
+ }
+ return retval;
+ }
+
+ @Override
+ protected void onCalculate(float progress) {
+ mCurrentContentScale = mTransitionSpec.contentScaleFrom
+ + (mTransitionSpec.contentScaleTo - mTransitionSpec.contentScaleFrom) * progress;
+ mCurrentContentAlpha = mTransitionSpec.contentAlphaFrom
+ + (mTransitionSpec.contentAlphaTo - mTransitionSpec.contentAlphaFrom) * progress;
+ mCurrentBackgroundAlpha = mTransitionSpec.backgroundAlphaFrom
+ + (mTransitionSpec.backgroundAlphaTo - mTransitionSpec.backgroundAlphaFrom)
+ * progress;
+ mCurrentBackgroundScale = mTransitionSpec.backgroundScaleFrom
+ + (mTransitionSpec.backgroundScaleTo - mTransitionSpec.backgroundScaleFrom)
+ * progress;
+ mCurrentOverlayScale = mTransitionSpec.overlayScaleFrom
+ + (mTransitionSpec.overlayScaleTo - mTransitionSpec.overlayScaleFrom) * progress;
+ mCurrentOverlayAlpha = mTransitionSpec.overlayAlphaFrom
+ + (mTransitionSpec.overlayAlphaTo - mTransitionSpec.overlayAlphaFrom) * progress;
+ }
+
+ private void applyOldTexture(GLView view, GLCanvas canvas, float alpha, float scale, boolean clear) {
+ if (mOldScreenTexture == null)
+ return;
+ if (clear) canvas.clearBuffer(view.getBackgroundColor());
+ canvas.save();
+ canvas.setAlpha(alpha);
+ int xOffset = view.getWidth() / 2;
+ int yOffset = view.getHeight() / 2;
+ canvas.translate(xOffset, yOffset);
+ canvas.scale(scale, scale, 1);
+ mOldScreenTexture.draw(canvas, -xOffset, -yOffset);
+ canvas.restore();
+ }
+
+ public void applyBackground(GLView view, GLCanvas canvas) {
+ if (mCurrentBackgroundAlpha > 0f) {
+ applyOldTexture(view, canvas, mCurrentBackgroundAlpha, mCurrentBackgroundScale, true);
+ }
+ }
+
+ public void applyContentTransform(GLView view, GLCanvas canvas) {
+ int xOffset = view.getWidth() / 2;
+ int yOffset = view.getHeight() / 2;
+ canvas.translate(xOffset, yOffset);
+ canvas.scale(mCurrentContentScale, mCurrentContentScale, 1);
+ canvas.translate(-xOffset, -yOffset);
+ canvas.setAlpha(mCurrentContentAlpha);
+ }
+
+ public void applyOverlay(GLView view, GLCanvas canvas) {
+ if (mCurrentOverlayAlpha > 0f) {
+ applyOldTexture(view, canvas, mCurrentOverlayAlpha, mCurrentOverlayScale, false);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index cb3aa9d..76649ca 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -41,6 +41,7 @@
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLRootView;
import com.android.gallery3d.util.ThreadPool;
+import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
public class AbstractGalleryActivity extends Activity implements GalleryContext {
@SuppressWarnings("unused")
@@ -51,6 +52,7 @@
private OrientationManager mOrientationManager;
private TransitionStore mTransitionStore = new TransitionStore();
private boolean mDisableToggleStatusBar;
+ private PanoramaViewHelper mPanoramaViewHelper;
private AlertDialog mAlertDialog = null;
private BroadcastReceiver mMountReceiver = new BroadcastReceiver() {
@@ -67,6 +69,8 @@
mOrientationManager = new OrientationManager(this);
toggleStatusBarByOrientation();
getWindow().setBackgroundDrawable(null);
+ mPanoramaViewHelper = new PanoramaViewHelper(this);
+ mPanoramaViewHelper.onCreate();
}
@Override
@@ -168,6 +172,7 @@
mAlertDialog = builder.show();
registerReceiver(mMountReceiver, mMountFilter);
}
+ mPanoramaViewHelper.onStart();
}
@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
@@ -184,6 +189,7 @@
mAlertDialog.dismiss();
mAlertDialog = null;
}
+ mPanoramaViewHelper.onStop();
}
@Override
@@ -293,4 +299,8 @@
public TransitionStore getTransitionStore() {
return mTransitionStore;
}
+
+ public PanoramaViewHelper getPanoramaViewHelper() {
+ return mPanoramaViewHelper;
+ }
}
diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java
index 75327e4..ff0b32c 100644
--- a/src/com/android/gallery3d/app/ActivityState.java
+++ b/src/com/android/gallery3d/app/ActivityState.java
@@ -35,7 +35,10 @@
import android.view.WindowManager;
import com.android.gallery3d.R;
+import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.PreparePageFadeoutTexture;
+import com.android.gallery3d.ui.RawTexture;
import com.android.gallery3d.util.GalleryUtils;
abstract public class ActivityState {
@@ -66,11 +69,24 @@
private boolean mPlugged = false;
boolean mIsFinishing = false;
+ private static final String KEY_TRANSITION_IN = "transition-in";
+
+ public static enum StateTransition { None, Outgoing, Incoming };
+ private StateTransition mNextTransition = StateTransition.None;
+ private StateTransitionAnimation mIntroAnimation;
+ private GLView mContentPane;
+
protected ActivityState() {
}
protected void setContentPane(GLView content) {
- mActivity.getGLRoot().setContentPane(content);
+ mContentPane = content;
+ if (mNextTransition != StateTransition.None) {
+ mContentPane.setIntroAnimation(mIntroAnimation);
+ mIntroAnimation = null;
+ }
+ mContentPane.setBackgroundColor(getBackgroundColor());
+ mActivity.getGLRoot().setContentPane(mContentPane);
}
void initialize(AbstractGalleryActivity activity, Bundle data) {
@@ -157,10 +173,26 @@
win.setAttributes(params);
}
+ protected void transitionOnNextPause(Class<? extends ActivityState> outgoing,
+ Class<? extends ActivityState> incoming, StateTransition hint) {
+ if (outgoing == PhotoPage.class && incoming == AlbumPage.class) {
+ mNextTransition = StateTransition.Outgoing;
+ } else if (outgoing == AlbumPage.class && incoming == PhotoPage.class) {
+ mNextTransition = StateTransition.Incoming;
+ } else {
+ mNextTransition = hint;
+ }
+ }
+
protected void onPause() {
if (0 != (mFlags & FLAG_SCREEN_ON_WHEN_PLUGGED)) {
((Activity) mActivity).unregisterReceiver(mPowerIntentReceiver);
}
+ if (mNextTransition != StateTransition.None) {
+ mActivity.getTransitionStore().put(KEY_TRANSITION_IN, mNextTransition);
+ PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mContentPane);
+ mNextTransition = StateTransition.None;
+ }
}
// should only be called by StateManager
@@ -214,6 +246,16 @@
// a subclass of ActivityState should override the method to resume itself
protected void onResume() {
+ RawTexture fade = mActivity.getTransitionStore().get(
+ PreparePageFadeoutTexture.KEY_FADE_TEXTURE);
+ mNextTransition = mActivity.getTransitionStore().get(
+ KEY_TRANSITION_IN, StateTransition.None);
+ if (mNextTransition != StateTransition.None) {
+ mIntroAnimation = new StateTransitionAnimation(
+ (mNextTransition == StateTransition.Incoming) ?
+ StateTransitionAnimation.Spec.INCOMING :
+ StateTransitionAnimation.Spec.OUTGOING, fade);
+ }
}
protected boolean onCreateActionBar(Menu menu) {
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index c956bc9..4cf73a8 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -47,7 +47,6 @@
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLView;
import com.android.gallery3d.ui.PhotoFallbackEffect;
-import com.android.gallery3d.ui.PreparePageFadeoutTexture;
import com.android.gallery3d.ui.RelativePosition;
import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SlotView;
@@ -139,11 +138,6 @@
private final float mMatrix[] = new float[16];
@Override
- protected void renderBackground(GLCanvas view) {
- view.clearBuffer(getBackgroundColor());
- }
-
- @Override
protected void onLayout(
boolean changed, int left, int top, int right, int bottom) {
@@ -254,8 +248,6 @@
} else {
// Render transition in pressed state
mAlbumView.setPressedIndex(slotIndex);
- PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mRootPane);
- mAlbumView.setPressedIndex(-1);
pickPhoto(slotIndex);
}
@@ -300,8 +292,12 @@
data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP,
startInFilmstrip);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, mMediaSet.isCameraRoll());
- mActivity.getStateManager().startStateForResult(
- PhotoPage.class, REQUEST_PHOTO, data);
+ if (startInFilmstrip) {
+ mActivity.getStateManager().switchState(this, PhotoPage.class, data);
+ } else {
+ mActivity.getStateManager().startStateForResult(
+ PhotoPage.class, REQUEST_PHOTO, data);
+ }
}
}
@@ -320,8 +316,9 @@
activity.startActivity(intent);
activity.finish();
} else {
- activity.setResult(Activity.RESULT_OK,
- new Intent(null, item.getContentUri()));
+ Intent intent = new Intent(null, item.getContentUri())
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ activity.setResult(Activity.RESULT_OK, intent);
activity.finish();
}
}
@@ -373,15 +370,6 @@
mLaunchedFromPhotoPage =
mActivity.getStateManager().hasStateClass(PhotoPage.class);
mInCameraApp = data.getBoolean(PhotoPage.KEY_APP_BRIDGE, false);
-
- // Don't show animation if it is restored or switched from filmstrip
- if (!mLaunchedFromPhotoPage && restoreState == null && data != null) {
- int[] center = data.getIntArray(KEY_SET_CENTER);
- if (center != null) {
- mOpenCenter.setAbsolutePosition(center[0], center[1]);
- mSlotView.startScatteringAnimation(mOpenCenter);
- }
- }
}
@Override
@@ -411,6 +399,7 @@
mAlbumDataAdapter.resume();
mAlbumView.resume();
+ mAlbumView.setPressedIndex(-1);
mActionModeHandler.resume();
if (!mInitialSynced) {
setLoadingBit(BIT_LOADING_SYNC);
@@ -561,7 +550,6 @@
if (mAlbumDataAdapter == null || !mAlbumDataAdapter.isActive(slotIndex)) return;
MediaItem item = mAlbumDataAdapter.get(slotIndex);
if (item == null) return;
- PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mRootPane);
TransitionStore transitions = mActivity.getTransitionStore();
transitions.put(PhotoPage.KEY_INDEX_HINT, slotIndex);
transitions.put(PhotoPage.KEY_OPEN_ANIMATION_RECT,
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index 1719b89..49ab683 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -52,7 +52,6 @@
import com.android.gallery3d.ui.GLCanvas;
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLView;
-import com.android.gallery3d.ui.PreparePageFadeoutTexture;
import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SlotView;
import com.android.gallery3d.ui.SynchronizedHandler;
@@ -130,11 +129,6 @@
private final float mMatrix[] = new float[16];
@Override
- protected void renderBackground(GLCanvas view) {
- view.clearBuffer(getBackgroundColor());
- }
-
- @Override
protected void onLayout(
boolean changed, int left, int top, int right, int bottom) {
mEyePosition.resetPosition();
@@ -273,7 +267,6 @@
& MediaObject.SUPPORT_IMPORT) != 0) {
data.putBoolean(AlbumPage.KEY_AUTO_SELECT_ALL, true);
} else if (!mGetContent && albumShouldOpenInFilmstrip(targetSet)) {
- PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mRootPane);
data.putParcelable(PhotoPage.KEY_OPEN_ANIMATION_RECT,
mSlotView.getSlotRect(slotIndex, mRootPane));
data.putInt(PhotoPage.KEY_INDEX_HINT, 0);
@@ -365,6 +358,7 @@
}
private boolean setupCameraButton() {
+ if (!GalleryUtils.isCameraAvailable(mActivity)) return false;
RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity)
.findViewById(R.id.gallery_root);
if (galleryRoot == null) return false;
diff --git a/src/com/android/gallery3d/app/CropImage.java b/src/com/android/gallery3d/app/CropImage.java
index 2b74504..89ca63d 100644
--- a/src/com/android/gallery3d/app/CropImage.java
+++ b/src/com/android/gallery3d/app/CropImage.java
@@ -61,6 +61,7 @@
import com.android.gallery3d.exif.ExifReader;
import com.android.gallery3d.exif.ExifTag;
import com.android.gallery3d.picasasource.PicasaSource;
+import com.android.gallery3d.ui.BitmapScreenNail;
import com.android.gallery3d.ui.BitmapTileProvider;
import com.android.gallery3d.ui.CropView;
import com.android.gallery3d.ui.GLRoot;
@@ -151,6 +152,7 @@
private BitmapRegionDecoder mRegionDecoder;
private Bitmap mBitmapInIntent;
private boolean mUseRegionDecoder = false;
+ private BitmapScreenNail mBitmapScreenNail;
private ProgressDialog mProgressDialog;
private Future<BitmapRegionDecoder> mLoadTask;
@@ -813,8 +815,14 @@
BitmapUtils.UNCONSTRAINED, BACKUP_PIXEL_COUNT);
mBitmap = regionDecoder.decodeRegion(
new Rect(0, 0, width, height), options);
- mCropView.setDataModel(new TileImageViewAdapter(
- mBitmap, regionDecoder), mMediaItem.getFullImageRotation());
+
+ mBitmapScreenNail = new BitmapScreenNail(mBitmap);
+
+ TileImageViewAdapter adapter = new TileImageViewAdapter();
+ adapter.setScreenNail(mBitmapScreenNail, width, height);
+ adapter.setRegionDecoder(regionDecoder);
+
+ mCropView.setDataModel(adapter, mMediaItem.getFullImageRotation());
if (mDoFaceDetection) {
mCropView.detectFaces(mBitmap);
} else {
@@ -976,6 +984,15 @@
}
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mBitmapScreenNail != null) {
+ mBitmapScreenNail.recycle();
+ mBitmapScreenNail = null;
+ }
+ }
+
private void dismissProgressDialogIfShown() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index 66f2874..20f15be 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -30,11 +30,12 @@
import com.android.gallery3d.data.MediaObject;
import com.android.gallery3d.data.MediaSet;
import com.android.gallery3d.data.Path;
-import com.android.gallery3d.ui.BitmapScreenNail;
import com.android.gallery3d.ui.PhotoView;
import com.android.gallery3d.ui.ScreenNail;
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.ui.TileImageViewAdapter;
+import com.android.gallery3d.ui.TiledScreenNail;
+import com.android.gallery3d.ui.TiledTexture;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
import com.android.gallery3d.util.MediaSetUtils;
@@ -59,8 +60,8 @@
private static final int MSG_RUN_OBJECT = 3;
private static final int MSG_UPDATE_IMAGE_REQUESTS = 4;
- private static final int MIN_LOAD_COUNT = 8;
- private static final int DATA_CACHE_SIZE = 32;
+ private static final int MIN_LOAD_COUNT = 16;
+ private static final int DATA_CACHE_SIZE = 256;
private static final int SCREEN_NAIL_MAX = PhotoView.SCREEN_NAIL_MAX;
private static final int IMAGE_CACHE_SIZE = 2 * SCREEN_NAIL_MAX + 1;
@@ -162,6 +163,7 @@
private DataListener mDataListener;
private final SourceListener mSourceListener = new SourceListener();
+ private final TiledTexture.Uploader mUploader;
// The path of the current viewing item will be stored in mItemPath.
// If mItemPath is not null, mCurrentIndex is only a hint for where we
@@ -183,6 +185,8 @@
Arrays.fill(mChanges, MediaObject.INVALID_DATA_VERSION);
+ mUploader = new TiledTexture.Uploader(activity.getGLRoot());
+
mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
@SuppressWarnings("unchecked")
@Override
@@ -301,8 +305,8 @@
entry.screenNailTask = null;
// Combine the ScreenNails if we already have a BitmapScreenNail
- if (entry.screenNail instanceof BitmapScreenNail) {
- BitmapScreenNail original = (BitmapScreenNail) entry.screenNail;
+ if (entry.screenNail instanceof TiledScreenNail) {
+ TiledScreenNail original = (TiledScreenNail) entry.screenNail;
screenNail = original.combine(screenNail);
}
@@ -321,6 +325,7 @@
}
}
updateImageRequests();
+ updateScreenNailUploadQueue();
}
private void updateFullImage(Path path, Future<BitmapRegionDecoder> future) {
@@ -345,6 +350,8 @@
@Override
public void resume() {
mIsActive = true;
+ TiledTexture.prepareResources();
+
mSource.addContentListener(mSourceListener);
updateImageCache();
updateImageRequests();
@@ -371,6 +378,9 @@
}
mImageCache.clear();
mTileProvider.clear();
+
+ mUploader.clear();
+ TiledTexture.freeResources();
}
private MediaItem getItem(int index) {
@@ -402,6 +412,32 @@
fireDataChange();
}
+ private void uploadScreenNail(int offset) {
+ int index = mCurrentIndex + offset;
+ if (index < mActiveStart || index >= mActiveEnd) return;
+
+ MediaItem item = getItem(index);
+ if (item == null) return;
+
+ ImageEntry e = mImageCache.get(item.getPath());
+ if (e == null) return;
+
+ ScreenNail s = e.screenNail;
+ if (s instanceof TiledScreenNail) {
+ TiledTexture t = ((TiledScreenNail) s).getTexture();
+ if (t != null && !t.isReady()) mUploader.addTexture(t);
+ }
+ }
+
+ private void updateScreenNailUploadQueue() {
+ mUploader.clear();
+ uploadScreenNail(0);
+ for (int i = 1; i < IMAGE_CACHE_SIZE; ++i) {
+ uploadScreenNail(i);
+ uploadScreenNail(-i);
+ }
+ }
+
@Override
public void moveTo(int index) {
updateCurrentIndex(index);
@@ -680,7 +716,7 @@
bitmap = BitmapUtils.rotateBitmap(bitmap,
mItem.getRotation() - mItem.getFullImageRotation(), true);
}
- return bitmap == null ? null : new BitmapScreenNail(bitmap);
+ return bitmap == null ? null : new TiledScreenNail(bitmap);
}
}
@@ -729,7 +765,7 @@
private ScreenNail newPlaceholderScreenNail(MediaItem item) {
int width = item.getWidth();
int height = item.getHeight();
- return new BitmapScreenNail(width, height);
+ return new TiledScreenNail(width, height);
}
// Returns the task if we started the task or the task is already started.
@@ -791,8 +827,8 @@
if (entry.requestedScreenNail != item.getDataVersion()) {
// This ScreenNail is outdated, we want to update it if it's
// still a placeholder.
- if (entry.screenNail instanceof BitmapScreenNail) {
- BitmapScreenNail s = (BitmapScreenNail) entry.screenNail;
+ if (entry.screenNail instanceof TiledScreenNail) {
+ TiledScreenNail s = (TiledScreenNail) entry.screenNail;
s.updatePlaceholderSize(
item.getWidth(), item.getHeight());
}
@@ -810,6 +846,8 @@
if (entry.screenNailTask != null) entry.screenNailTask.cancel();
if (entry.screenNail != null) entry.screenNail.recycle();
}
+
+ updateScreenNailUploadQueue();
}
private class FullImageListener
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index db7f3d9..ecb4832 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -34,12 +34,10 @@
import android.os.SystemClock;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.animation.AccelerateInterpolator;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.android.gallery3d.R;
-import com.android.gallery3d.anim.FloatAnimation;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.ComboAlbum;
@@ -59,9 +57,9 @@
import com.android.gallery3d.data.SnailAlbum;
import com.android.gallery3d.data.SnailItem;
import com.android.gallery3d.data.SnailSource;
+import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.picasasource.PicasaSource;
import com.android.gallery3d.ui.AnimationTime;
-import com.android.gallery3d.ui.BitmapScreenNail;
import com.android.gallery3d.ui.DetailsHelper;
import com.android.gallery3d.ui.DetailsHelper.CloseListener;
import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
@@ -73,12 +71,10 @@
import com.android.gallery3d.ui.MenuExecutor;
import com.android.gallery3d.ui.PhotoFallbackEffect;
import com.android.gallery3d.ui.PhotoView;
-import com.android.gallery3d.ui.PreparePageFadeoutTexture;
-import com.android.gallery3d.ui.RawTexture;
import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.ui.TiledScreenNail;
import com.android.gallery3d.util.GalleryUtils;
-import com.android.gallery3d.util.LightCycleHelper;
public class PhotoPage extends ActivityState implements
PhotoView.Listener, OrientationManager.Listener, AppBridge.Server,
@@ -94,8 +90,9 @@
private static final int MSG_ON_CAMERA_CENTER = 9;
private static final int MSG_ON_PICTURE_CENTER = 10;
private static final int MSG_REFRESH_IMAGE = 11;
- private static final int MSG_UPDATE_DEFERRED = 12;
+ private static final int MSG_UPDATE_PHOTO_UI = 12;
private static final int MSG_UPDATE_PROGRESS = 13;
+ private static final int MSG_UPDATE_DEFERRED = 14;
private static final int HIDE_BARS_TIMEOUT = 3500;
private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
@@ -165,7 +162,6 @@
private boolean mTreatBackAsUp;
private boolean mStartInFilmstrip;
private boolean mInCameraRoll;
- private boolean mStartedFromAlbumPage;
private boolean mRecenterCameraOnResume = true;
private long mCameraSwitchCutoff = 0;
@@ -176,9 +172,7 @@
private boolean mDeferredUpdateWaiting = false;
private long mDeferUpdateUntil = Long.MAX_VALUE;
- private RawTexture mFadeOutTexture;
private Rect mOpenAnimationRect;
- public static final int ANIM_TIME_OPENING = 300;
// The item that is deleted (but it can still be undeleted before commiting)
private Path mDeletePath;
@@ -193,8 +187,15 @@
private SupportedOperationsListener mSupportedOperationsListener =
new SupportedOperationsListener() {
@Override
- public void onChange(int operations) {
- mHandler.sendEmptyMessage(MSG_REFRESH_IMAGE);
+ public void onChange(MediaObject item, int operations) {
+ if (item == mCurrentPhoto) {
+ if (mPhotoView.getFilmMode()
+ && SystemClock.uptimeMillis() < mDeferUpdateUntil) {
+ requestDeferredUpdate();
+ } else {
+ mHandler.sendEmptyMessage(MSG_UPDATE_PHOTO_UI);
+ }
+ }
}
};
@@ -213,13 +214,6 @@
}
}
- private static class BackgroundFadeOut extends FloatAnimation {
- public BackgroundFadeOut() {
- super(1f, 0f, ANIM_TIME_OPENING);
- setInterpolator(new AccelerateInterpolator(2f));
- }
- }
-
private class UpdateProgressListener implements StitchingChangeListener {
@Override
@@ -246,8 +240,6 @@
}
};
- private final FloatAnimation mBackgroundFade = new BackgroundFadeOut();
-
@Override
protected int getBackgroundColorId() {
return R.color.photo_background;
@@ -255,28 +247,6 @@
private final GLView mRootPane = new GLView() {
@Override
- protected void renderBackground(GLCanvas view) {
- if (mFadeOutTexture != null) {
- if (mBackgroundFade.calculate(AnimationTime.get())) invalidate();
- if (!mBackgroundFade.isActive()) {
- mFadeOutTexture = null;
- mOpenAnimationRect = null;
- BitmapScreenNail.enableDrawPlaceholder();
- } else {
- float fadeAlpha = mBackgroundFade.get();
- if (fadeAlpha < 1f) {
- view.clearBuffer(getBackgroundColor());
- view.setAlpha(fadeAlpha);
- }
- mFadeOutTexture.draw(view, 0, 0);
- view.setAlpha(1f - fadeAlpha);
- return;
- }
- }
- view.clearBuffer(getBackgroundColor());
- }
-
- @Override
protected void onLayout(
boolean changed, int left, int top, int right, int bottom) {
mPhotoView.layout(0, 0, right - left, bottom - top);
@@ -366,6 +336,12 @@
break;
}
case MSG_REFRESH_IMAGE: {
+ final MediaItem photo = mCurrentPhoto;
+ mCurrentPhoto = null;
+ updateCurrentPhoto(photo);
+ break;
+ }
+ case MSG_UPDATE_PHOTO_UI: {
updateUIForCurrentPhoto();
break;
}
@@ -388,9 +364,6 @@
mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false);
mStartInFilmstrip = data.getBoolean(KEY_START_IN_FILMSTRIP, false);
mInCameraRoll = data.getBoolean(KEY_IN_CAMERA_ROLL, false);
- mStartedFromAlbumPage =
- data.getInt(KEY_ALBUMPAGE_TRANSITION,
- MSG_ALBUMPAGE_NONE) == MSG_ALBUMPAGE_STARTED;
mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
if (mSetPathString != null) {
mShowSpinner = true;
@@ -548,10 +521,14 @@
mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot);
mProgressListener = new UpdateProgressListener();
progressManager.addChangeListener(mProgressListener);
+ if (mSecureAlbum != null) {
+ progressManager.addChangeListener(mSecureAlbum);
+ }
}
}
}
+ @Override
public void onPictureCenter(boolean isCamera) {
mPhotoView.setWantPictureCenterCallbacks(false);
mHandler.removeMessages(MSG_ON_CAMERA_CENTER);
@@ -559,10 +536,12 @@
mHandler.sendEmptyMessage(isCamera ? MSG_ON_CAMERA_CENTER : MSG_ON_PICTURE_CENTER);
}
+ @Override
public boolean canDisplayBottomControls() {
return mIsActive && !mPhotoView.getFilmMode();
}
+ @Override
public boolean canDisplayBottomControl(int control) {
if (mCurrentPhoto == null) return false;
switch(control) {
@@ -580,6 +559,7 @@
}
}
+ @Override
public void onBottomControlClicked(int control) {
switch(control) {
case R.id.photopage_bottom_control_edit:
@@ -587,7 +567,8 @@
return;
case R.id.photopage_bottom_control_panorama:
mRecenterCameraOnResume = false;
- LightCycleHelper.viewPanorama(mActivity, mCurrentPhoto.getContentUri());
+ mActivity.getPanoramaViewHelper()
+ .showPanorama(mCurrentPhoto.getContentUri());
return;
default:
return;
@@ -617,11 +598,10 @@
private Intent createShareIntent(Path path) {
DataManager manager = mActivity.getDataManager();
int type = manager.getMediaType(path);
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(MenuExecutor.getMimeType(type));
- Uri uri = manager.getContentUri(path);
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- return intent;
+ return new Intent(Intent.ACTION_SEND)
+ .setType(MenuExecutor.getMimeType(type))
+ .putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path))
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
private Intent createSharePanoramaIntent(Path path) {
@@ -630,11 +610,10 @@
if ((supported & MediaObject.SUPPORT_PANORAMA360) == 0) {
return null;
}
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(GalleryUtils.MIME_TYPE_PANORAMA360);
- Uri uri = manager.getContentUri(path);
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- return intent;
+ return new Intent(Intent.ACTION_SEND)
+ .setType(GalleryUtils.MIME_TYPE_PANORAMA360)
+ .putExtra(Intent.EXTRA_STREAM, manager.getContentUri(path))
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
private void launchPhotoEditor() {
@@ -953,11 +932,10 @@
};
private void switchToGrid() {
- if (mStartedFromAlbumPage) {
+ if (mActivity.getStateManager().hasStateClass(AlbumPage.class)) {
onUpPressed();
} else {
if (mOriginalSetPathString == null) return;
- preparePhotoFallbackView();
Bundle data = new Bundle(getData());
data.putString(AlbumPage.KEY_MEDIA_PATH, mOriginalSetPathString);
data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
@@ -976,7 +954,11 @@
mActivity.getTransitionStore().put(KEY_RETURN_INDEX_HINT,
mAppBridge != null ? mCurrentIndex - 1 : mCurrentIndex);
- mActivity.getStateManager().startState(AlbumPage.class, data);
+ if (mInCameraRoll && mAppBridge != null) {
+ mActivity.getStateManager().startState(AlbumPage.class, data);
+ } else {
+ mActivity.getStateManager().switchState(this, AlbumPage.class, data);
+ }
}
}
@@ -1014,9 +996,10 @@
}
case R.id.action_crop: {
Activity activity = mActivity;
- Intent intent = new Intent(CropImage.CROP_ACTION);
- intent.setClass(activity, CropImage.class);
- intent.setData(manager.getContentUri(path));
+ Intent intent = new Intent(FilterShowActivity.CROP_ACTION);
+ intent.setClass(activity, FilterShowActivity.class);
+ intent.setDataAndType(manager.getContentUri(path), current.getMimeType())
+ .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
? REQUEST_CROP_PICASA
: REQUEST_CROP);
@@ -1197,6 +1180,16 @@
Path path = mApplication.getDataManager()
.findPathByUri(intent.getData(), intent.getType());
if (path != null) {
+ Path albumPath = mApplication.getDataManager().getDefaultSetOf(path);
+ if (!albumPath.equalsIgnoreCase(mOriginalSetPathString)) {
+ // If the edited image is stored in a different album, we need
+ // to start a new activity state to show the new image
+ Bundle data = new Bundle(getData());
+ data.putString(KEY_MEDIA_SET_PATH, albumPath.toString());
+ data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString());
+ mActivity.getStateManager().startState(PhotoPage.class, data);
+ return;
+ }
mModel.setCurrentPhoto(path, mCurrentIndex);
}
}
@@ -1287,7 +1280,6 @@
// Hide the detail dialog on exit
if (mShowDetails) hideDetails();
if (mModel != null) {
- if (isFinishing()) preparePhotoFallbackView();
mModel.pause();
}
mPhotoView.pause();
@@ -1351,18 +1343,6 @@
} else if (albumPageTransition == MSG_ALBUMPAGE_PICKED) {
mPhotoView.setFilmMode(false);
}
-
- mFadeOutTexture = transitions.get(PreparePageFadeoutTexture.KEY_FADE_TEXTURE);
- if (mFadeOutTexture != null) {
- mBackgroundFade.start();
- BitmapScreenNail.disableDrawPlaceholder();
- mOpenAnimationRect =
- albumPageTransition == MSG_ALBUMPAGE_NONE ?
- (Rect) mData.getParcelable(KEY_OPEN_ANIMATION_RECT) :
- (Rect) transitions.get(KEY_OPEN_ANIMATION_RECT);
- mPhotoView.setOpenAnimationRect(mOpenAnimationRect);
- mBackgroundFade.start();
- }
}
@Override
diff --git a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
index 2f6f16f..00f2fe7 100644
--- a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
@@ -27,6 +27,7 @@
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.Path;
+import com.android.gallery3d.ui.BitmapScreenNail;
import com.android.gallery3d.ui.PhotoView;
import com.android.gallery3d.ui.ScreenNail;
import com.android.gallery3d.ui.SynchronizedHandler;
@@ -50,6 +51,7 @@
private PhotoView mPhotoView;
private ThreadPool mThreadPool;
private int mLoadingState = LOADING_INIT;
+ private BitmapScreenNail mBitmapScreenNail;
public SinglePhotoDataAdapter(
AbstractGalleryActivity activity, PhotoView view, MediaItem item) {
@@ -113,6 +115,11 @@
return false;
}
+ private void setScreenNail(Bitmap bitmap, int width, int height) {
+ mBitmapScreenNail = new BitmapScreenNail(bitmap);
+ setScreenNail(mBitmapScreenNail, width, height);
+ }
+
private void onDecodeLargeComplete(ImageBundle bundle) {
try {
setScreenNail(bundle.backupImage,
@@ -162,6 +169,10 @@
if (task.get() == null) {
mTask = null;
}
+ if (mBitmapScreenNail != null) {
+ mBitmapScreenNail.recycle();
+ mBitmapScreenNail = null;
+ }
}
@Override
diff --git a/src/com/android/gallery3d/app/StateManager.java b/src/com/android/gallery3d/app/StateManager.java
index c041b0e..fefb2c8 100644
--- a/src/com/android/gallery3d/app/StateManager.java
+++ b/src/com/android/gallery3d/app/StateManager.java
@@ -57,6 +57,8 @@
}
if (!mStack.isEmpty()) {
ActivityState top = getTopState();
+ top.transitionOnNextPause(top.getClass(), klass,
+ ActivityState.StateTransition.Incoming);
if (mIsResumed) top.onPause();
}
state.initialize(mActivity, data);
@@ -81,6 +83,8 @@
if (!mStack.isEmpty()) {
ActivityState as = getTopState();
+ as.transitionOnNextPause(as.getClass(), klass,
+ ActivityState.StateTransition.Incoming);
as.mReceivedResults = state.mResult;
if (mIsResumed) as.onPause();
} else {
@@ -186,15 +190,18 @@
// Remove the top state.
mStack.pop();
state.mIsFinishing = true;
- if (mIsResumed && fireOnPause) state.onPause();
+ ActivityState top = !mStack.isEmpty() ? mStack.peek().activityState : null;
+ if (mIsResumed && fireOnPause) {
+ if (top != null) {
+ state.transitionOnNextPause(state.getClass(), top.getClass(),
+ ActivityState.StateTransition.Outgoing);
+ }
+ state.onPause();
+ }
mActivity.getGLRoot().setContentPane(null);
state.onDestroy();
- if (!mStack.isEmpty()) {
- // Restore the immediately previous state
- ActivityState top = mStack.peek().activityState;
- if (mIsResumed) top.resume();
- }
+ if (top != null && mIsResumed) top.resume();
}
public void switchState(ActivityState oldState,
@@ -207,6 +214,10 @@
}
// Remove the top state.
mStack.pop();
+ if (!data.containsKey(PhotoPage.KEY_APP_BRIDGE)) {
+ // Do not do the fade out stuff when we are switching camera modes
+ oldState.transitionOnNextPause(oldState.getClass(), klass, ActivityState.StateTransition.Incoming);
+ }
if (mIsResumed) oldState.onPause();
oldState.onDestroy();
diff --git a/src/com/android/gallery3d/app/TrimVideo.java b/src/com/android/gallery3d/app/TrimVideo.java
index 09a2abd..01fe462 100644
--- a/src/com/android/gallery3d/app/TrimVideo.java
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -268,9 +268,8 @@
return;
}
if (Math.abs(mVideoView.getDuration() - delta) < 100) {
- Toast.makeText(getApplicationContext(),
- getString(R.string.trim_too_long),
- Toast.LENGTH_SHORT).show();
+ // If no change has been made, go back
+ onBackPressed();
return;
}
// Use the default save directory if the source directory cannot be
diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java
index 3d2c0c2..408a24b 100644
--- a/src/com/android/gallery3d/data/DataManager.java
+++ b/src/com/android/gallery3d/data/DataManager.java
@@ -85,9 +85,6 @@
private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video";
- private static final String ACTION_DELETE_PICTURE =
- "com.android.gallery3d.action.DELETE_PICTURE";
-
public static final Comparator<MediaItem> sDateTakenComparator =
new DateTakenComparator();
@@ -335,15 +332,6 @@
}
}
- // Sends a local broadcast if a local image or video is deleted. This is
- // used to update the thumbnail shown in the camera app.
- public void broadcastLocalDeletion() {
- LocalBroadcastManager manager = LocalBroadcastManager.getInstance(
- mApplication.getAndroidContext());
- Intent intent = new Intent(ACTION_DELETE_PICTURE);
- manager.sendBroadcast(intent);
- }
-
private static class NotifyBroker extends ContentObserver {
private WeakHashMap<ChangeNotifier, Object> mNotifiers =
new WeakHashMap<ChangeNotifier, Object>();
diff --git a/src/com/android/gallery3d/data/EmptyAlbumImage.java b/src/com/android/gallery3d/data/EmptyAlbumImage.java
index dbbc01a..6f8c37c 100644
--- a/src/com/android/gallery3d/data/EmptyAlbumImage.java
+++ b/src/com/android/gallery3d/data/EmptyAlbumImage.java
@@ -24,7 +24,7 @@
private static final String TAG = "EmptyAlbumImage";
public EmptyAlbumImage(Path path, GalleryApp application) {
- super(path, application, R.drawable.ic_menu_revert_holo_dark);
+ super(path, application, R.drawable.placeholder_empty);
}
@Override
diff --git a/src/com/android/gallery3d/data/LocalAlbum.java b/src/com/android/gallery3d/data/LocalAlbum.java
index 4682e77..e05aac0 100644
--- a/src/com/android/gallery3d/data/LocalAlbum.java
+++ b/src/com/android/gallery3d/data/LocalAlbum.java
@@ -267,7 +267,6 @@
GalleryUtils.assertNotInRenderThread();
mResolver.delete(mBaseUri, mWhereClause,
new String[]{String.valueOf(mBucketId)});
- mApplication.getDataManager().broadcastLocalDeletion();
}
@Override
@@ -275,7 +274,7 @@
return true;
}
- private static String getLocalizedName(Resources res, int bucketId,
+ public static String getLocalizedName(Resources res, int bucketId,
String name) {
if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) {
return res.getString(R.string.folder_camera);
@@ -285,6 +284,8 @@
return res.getString(R.string.folder_imported);
} else if (bucketId == MediaSetUtils.SNAPSHOT_BUCKET_ID) {
return res.getString(R.string.folder_screenshot);
+ } else if (bucketId == MediaSetUtils.EDITED_ONLINE_PHOTOS_BUCKET_ID) {
+ return res.getString(R.string.folder_edited_online_photos);
} else {
return name;
}
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index dba6b68..61961d8 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -288,8 +288,6 @@
@Override
public void setSupportedOperationsListener(SupportedOperationsListener l) {
synchronized (mLock) {
- if (mPanoramaMetadataInitialized) return; // no more updates
-
if (l == null) {
if (mGetPanoMetadataTask != null) {
mGetPanoMetadataTask.cancel();
@@ -308,7 +306,7 @@
mPanoramaMetadata = future.get();
mPanoramaMetadataInitialized = true;
if (mListener != null) {
- mListener.onChange(getSupportedOperations());
+ mListener.onChange(LocalImage.this, getSupportedOperations());
}
}
});
@@ -324,7 +322,6 @@
Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI;
mApplication.getContentResolver().delete(baseUri, "_id=?",
new String[]{String.valueOf(id)});
- mApplication.getDataManager().broadcastLocalDeletion();
}
private static String getExifOrientation(int orientation) {
diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java
index 5b6ee4b..c876d81 100644
--- a/src/com/android/gallery3d/data/LocalVideo.java
+++ b/src/com/android/gallery3d/data/LocalVideo.java
@@ -187,7 +187,6 @@
Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI;
mApplication.getContentResolver().delete(baseUri, "_id=?",
new String[]{String.valueOf(id)});
- mApplication.getDataManager().broadcastLocalDeletion();
}
@Override
diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java
index 382a5c7..14cd524 100644
--- a/src/com/android/gallery3d/data/MediaObject.java
+++ b/src/com/android/gallery3d/data/MediaObject.java
@@ -50,7 +50,7 @@
public static final int SUPPORT_ALL = 0xffffffff;
public static interface SupportedOperationsListener {
- public void onChange(int operations);
+ public void onChange(MediaObject item, int operations);
}
// These are the bits returned from getMediaType():
@@ -100,7 +100,7 @@
}
public int getSupportedOperations(boolean getAll) {
- return 0;
+ return getSupportedOperations();
}
public void setSupportedOperationsListener(SupportedOperationsListener l) {
diff --git a/src/com/android/gallery3d/data/Path.java b/src/com/android/gallery3d/data/Path.java
index a4840bd..fcae65e 100644
--- a/src/com/android/gallery3d/data/Path.java
+++ b/src/com/android/gallery3d/data/Path.java
@@ -79,6 +79,7 @@
}
@Override
+ // TODO: toString() should be more efficient, will fix it later
public String toString() {
synchronized (Path.class) {
StringBuilder sb = new StringBuilder();
@@ -91,6 +92,11 @@
}
}
+ public boolean equalsIgnoreCase (String p) {
+ String path = toString();
+ return path.equalsIgnoreCase(p);
+ }
+
public static Path fromString(String s) {
synchronized (Path.class) {
String[] segments = split(s);
diff --git a/src/com/android/gallery3d/data/SecureAlbum.java b/src/com/android/gallery3d/data/SecureAlbum.java
index c666bdc..0a8c5a8 100644
--- a/src/com/android/gallery3d/data/SecureAlbum.java
+++ b/src/com/android/gallery3d/data/SecureAlbum.java
@@ -24,12 +24,13 @@
import android.provider.MediaStore.Video;
import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.app.StitchingChangeListener;
import com.android.gallery3d.util.MediaSetUtils;
import java.util.ArrayList;
// This class lists all media items added by the client.
-public class SecureAlbum extends MediaSet {
+public class SecureAlbum extends MediaSet implements StitchingChangeListener {
@SuppressWarnings("unused")
private static final String TAG = "SecureAlbum";
private static final String[] PROJECTION = {MediaColumns._ID};
@@ -183,4 +184,18 @@
public boolean isLeafAlbum() {
return true;
}
+
+ @Override
+ public void onStitchingQueued(Uri uri) {
+ int id = Integer.parseInt(uri.getLastPathSegment());
+ addMediaItem(false, id);
+ }
+
+ @Override
+ public void onStitchingResult(Uri uri) {
+ }
+
+ @Override
+ public void onStitchingProgress(Uri uri, final int progress) {
+ }
}
diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java
index 5fab667..aaa36a9 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -255,8 +255,6 @@
@Override
public void setSupportedOperationsListener(SupportedOperationsListener l) {
synchronized (mLock) {
- if (mPanoramaMetadataInitialized) return; // no more updates
-
if (l != null) {
if (mGetPanoMetadataTask != null) {
mGetPanoMetadataTask.cancel();
@@ -275,7 +273,7 @@
mPanoramaMetadata = future.get();
mPanoramaMetadataInitialized = true;
if (mListener != null) {
- mListener.onChange(getSupportedOperations());
+ mListener.onChange(UriImage.this, getSupportedOperations());
}
}
});
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index f2b817c..fa9277e 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -6,6 +6,7 @@
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -31,8 +32,10 @@
import android.widget.SeekBar;
import android.widget.ShareActionProvider;
import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
+import android.widget.Toast;
import com.android.gallery3d.R;
+import com.android.gallery3d.data.LocalAlbum;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterBorder;
@@ -60,7 +63,10 @@
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.provider.SharedImageProvider;
import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.ui.ImageButtonTitle;
import com.android.gallery3d.filtershow.ui.ImageCurves;
+import com.android.gallery3d.filtershow.ui.Spline;
+import com.android.gallery3d.util.GalleryUtils;
import java.io.File;
import java.lang.ref.WeakReference;
@@ -70,6 +76,7 @@
public class FilterShowActivity extends Activity implements OnItemClickListener,
OnShareTargetSelectedListener {
+ public static final String CROP_ACTION = "com.android.camera.action.CROP";
private final PanelController mPanelController = new PanelController();
private ImageLoader mImageLoader = null;
private ImageShow mImageShow = null;
@@ -77,7 +84,7 @@
private ImageBorder mImageBorders = null;
private ImageStraighten mImageStraighten = null;
private ImageZoom mImageZoom = null;
- private final ImageCrop mImageCrop = null;
+ private ImageCrop mImageCrop = null;
private ImageRotate mImageRotate = null;
private ImageFlip mImageFlip = null;
@@ -96,6 +103,7 @@
private static final int SELECT_PICTURE = 1;
private static final String LOGTAG = "FilterShowActivity";
protected static final boolean ANIMATE_PANELS = true;
+ private static int mImageBorderSize = 40;
private boolean mShowingHistoryPanel = false;
private boolean mShowingImageStatePanel = false;
@@ -110,6 +118,7 @@
private boolean mSharingImage = false;
private WeakReference<ProgressDialog> mSavingProgressDialog;
+ private static final int SEEK_BAR_MAX = 600;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -117,6 +126,20 @@
ImageFilterRS.setRenderScriptContext(this);
+ ImageShow.setDefaultBackgroundColor(getResources().getColor(R.color.background_screen));
+ // TODO: get those values from XML.
+ ImageShow.setTextSize((int) getPixelsFromDip(12));
+ ImageShow.setTextPadding((int) getPixelsFromDip(10));
+ ImageButtonTitle.setTextSize((int) getPixelsFromDip(12));
+ ImageButtonTitle.setTextPadding((int) getPixelsFromDip(10));
+ ImageSmallFilter.setMargin((int) getPixelsFromDip(3));
+ ImageSmallFilter.setTextMargin((int) getPixelsFromDip(4));
+ mImageBorderSize = (int) getPixelsFromDip(20);
+ Drawable curveHandle = getResources().getDrawable(R.drawable.camera_crop_holo);
+ int curveHandleSize = (int) getResources().getDimension(R.dimen.crop_indicator_size);
+ Spline.setCurveHandle(curveHandle, curveHandleSize);
+ Spline.setCurveWidth((int) getPixelsFromDip(3));
+
setContentView(R.layout.filtershow_activity);
ActionBar actionBar = getActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
@@ -129,7 +152,7 @@
}
});
- mImageLoader = new ImageLoader(getApplicationContext());
+ mImageLoader = new ImageLoader(this, getApplicationContext());
LinearLayout listFilters = (LinearLayout) findViewById(R.id.listFilters);
LinearLayout listBorders = (LinearLayout) findViewById(R.id.listBorders);
@@ -140,8 +163,7 @@
mImageBorders = (ImageBorder) findViewById(R.id.imageBorder);
mImageStraighten = (ImageStraighten) findViewById(R.id.imageStraighten);
mImageZoom = (ImageZoom) findViewById(R.id.imageZoom);
- // TODO: implement crop
- // mImageCrop = (ImageCrop) findViewById(R.id.imageCrop);
+ mImageCrop = (ImageCrop) findViewById(R.id.imageCrop);
mImageRotate = (ImageRotate) findViewById(R.id.imageRotate);
mImageFlip = (ImageFlip) findViewById(R.id.imageFlip);
@@ -150,8 +172,7 @@
mImageViews.add(mImageBorders);
mImageViews.add(mImageStraighten);
mImageViews.add(mImageZoom);
- // TODO: implement crop
- // mImageViews.add(mImageCrop);
+ mImageViews.add(mImageCrop);
mImageViews.add(mImageRotate);
mImageViews.add(mImageFlip);
@@ -180,9 +201,8 @@
mImageStraighten.setMaster(mImageShow);
mImageZoom.setImageLoader(mImageLoader);
mImageZoom.setMaster(mImageShow);
- // TODO: implement crop
- // mImageCrop.setImageLoader(mImageLoader);
- // mImageCrop.setMaster(mImageShow);
+ mImageCrop.setImageLoader(mImageLoader);
+ mImageCrop.setMaster(mImageShow);
mImageRotate.setImageLoader(mImageLoader);
mImageRotate.setMaster(mImageShow);
mImageFlip.setImageLoader(mImageLoader);
@@ -192,8 +212,7 @@
mPanelController.addImageView(findViewById(R.id.imageCurves));
mPanelController.addImageView(findViewById(R.id.imageBorder));
mPanelController.addImageView(findViewById(R.id.imageStraighten));
- // TODO: implement crop
- // mPanelController.addImageView(findViewById(R.id.imageCrop));
+ mPanelController.addImageView(findViewById(R.id.imageCrop));
mPanelController.addImageView(findViewById(R.id.imageRotate));
mPanelController.addImageView(findViewById(R.id.imageFlip));
mPanelController.addImageView(findViewById(R.id.imageZoom));
@@ -203,14 +222,13 @@
mPanelController.addPanel(mGeometryButton, mListGeometry, 2);
mPanelController.addComponent(mGeometryButton, findViewById(R.id.straightenButton));
- // TODO: implement crop
-// mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton));
+ mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.rotateButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton));
mPanelController.addPanel(mColorsButton, mListColors, 3);
- int []recastIDs = {
+ int[] recastIDs = {
R.id.vignetteButton,
R.id.vibranceButton,
R.id.contrastButton,
@@ -220,7 +238,7 @@
R.id.exposureButton,
R.id.shadowRecoveryButton
};
- ImageFilter []filters = {
+ ImageFilter[] filters = {
new ImageFilterVignette(),
new ImageFilterVibrance(),
new ImageFilterContrast(),
@@ -231,7 +249,6 @@
new ImageFilterShadows()
};
-
for (int i = 0; i < filters.length; i++) {
ImageSmallFilter fView = new ImageSmallFilter(this);
@@ -240,51 +257,51 @@
listColors.removeView(v);
filters[i].setParameter(100);
+ if(v instanceof ImageButtonTitle)
+ filters[i].setName(((ImageButtonTitle) v).getText());
fView.setImageFilter(filters[i]);
fView.setController(this);
fView.setImageLoader(mImageLoader);
fView.setId(recastIDs[i]);
-
mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView,pos);
+ listColors.addView(fView, pos);
}
- int []overlayIDs = {
+ int[] overlayIDs = {
R.id.sharpenButton,
R.id.curvesButtonRGB
};
- int []overlayBitmaps = {
+ int[] overlayBitmaps = {
R.drawable.filtershow_button_colors_sharpen,
R.drawable.filtershow_button_colors_curve
};
int []overlayNames = {
- R.string.sharpen,
+ R.string.sharpness,
R.string.curvesRGB
};
- for (int i = 0; i < overlayIDs.length; i++) {
+ for (int i = 0; i < overlayIDs.length; i++) {
ImageWithIcon fView = new ImageWithIcon(this);
View v = listColors.findViewById(overlayIDs[i]);
int pos = listColors.indexOfChild(v);
listColors.removeView(v);
- final int sid =overlayNames[i];
- ImageFilterExposure efilter = new ImageFilterExposure(){
+ final int sid = overlayNames[i];
+ ImageFilterExposure efilter = new ImageFilterExposure() {
{
mName = getString(sid);
}
};
efilter.setParameter(-300);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
- overlayBitmaps[i] );
+ overlayBitmaps[i]);
fView.setIcon(bitmap);
fView.setImageFilter(efilter);
fView.setController(this);
fView.setImageLoader(mImageLoader);
fView.setId(overlayIDs[i]);
-
mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView,pos);
+ listColors.addView(fView, pos);
}
mPanelController.addComponent(mColorsButton, findViewById(R.id.curvesButtonRGB));
@@ -298,7 +315,8 @@
mPanelController.addComponent(mColorsButton, findViewById(R.id.shadowRecoveryButton));
mPanelController.addView(findViewById(R.id.applyEffect));
-
+ mPanelController.addView(findViewById(R.id.pickCurvesChannel));
+ mPanelController.addView(findViewById(R.id.aspect));
findViewById(R.id.resetOperationsButton).setOnClickListener(
createOnClickResetOperationsButton());
@@ -313,35 +331,42 @@
fillListBorders(listBorders);
SeekBar seekBar = (SeekBar) findViewById(R.id.filterSeekBar);
- seekBar.setMax(200);
+ seekBar.setMax(SEEK_BAR_MAX);
+
mImageShow.setSeekBar(seekBar);
mImageZoom.setSeekBar(seekBar);
mPanelController.setRowPanel(findViewById(R.id.secondRowPanel));
mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList),
- findViewById(R.id.applyEffect));
+ findViewById(R.id.applyEffect), findViewById(R.id.aspect));
mPanelController.setMasterImage(mImageShow);
mPanelController.setCurrentPanel(mFxButton);
Intent intent = getIntent();
String data = intent.getDataString();
if (data != null) {
Uri uri = Uri.parse(data);
- mImageLoader.loadBitmap(uri,getScreenImageSize());
+ mImageLoader.loadBitmap(uri, getScreenImageSize());
} else {
pickImage();
}
+
+ String action = intent.getAction();
+ if (action == CROP_ACTION){
+ mPanelController.showComponent(findViewById(R.id.cropButton));
+ }
+
}
- private int getScreenImageSize(){
- DisplayMetrics metrics = new DisplayMetrics();
+ private int getScreenImageSize() {
+ DisplayMetrics metrics = new DisplayMetrics();
Display display = getWindowManager().getDefaultDisplay();
- Point size = new Point();
+ Point size = new Point();
display.getSize(size);
display.getMetrics(metrics);
int msize = Math.min(size.x, size.y);
- return (133*msize)/metrics.densityDpi;
+ return (133 * msize) / metrics.densityDpi;
}
- private void showSavingProgress() {
+ private void showSavingProgress(String albumName) {
ProgressDialog progress;
if (mSavingProgressDialog != null) {
progress = mSavingProgressDialog.get();
@@ -351,7 +376,13 @@
}
}
// TODO: Allow cancellation of the saving process
- progress = ProgressDialog.show(this, "", getString(R.string.saving_image), true, false);
+ String progressText;
+ if (albumName == null) {
+ progressText = getString(R.string.saving_image);
+ } else {
+ progressText = getString(R.string.filtershow_saving_image, albumName);
+ }
+ progress = ProgressDialog.show(this, "", progressText, true, false);
mSavingProgressDialog = new WeakReference<ProgressDialog>(progress);
}
@@ -389,7 +420,7 @@
mSharingImage = true;
// Process and save the image in the background.
- showSavingProgress();
+ showSavingProgress(null);
mImageShow.saveImage(this, mSharedOutputFile);
return true;
}
@@ -493,27 +524,27 @@
int p = 0;
int[] drawid = {
- R.drawable.filtershow_fx_0000_vintage,
- R.drawable.filtershow_fx_0001_instant,
- R.drawable.filtershow_fx_0002_bleach,
- R.drawable.filtershow_fx_0003_blue_crush,
- R.drawable.filtershow_fx_0004_bw_contrast,
R.drawable.filtershow_fx_0005_punch,
- R.drawable.filtershow_fx_0006_x_process,
+ R.drawable.filtershow_fx_0000_vintage,
+ R.drawable.filtershow_fx_0004_bw_contrast,
+ R.drawable.filtershow_fx_0002_bleach,
+ R.drawable.filtershow_fx_0001_instant,
R.drawable.filtershow_fx_0007_washout,
- R.drawable.filtershow_fx_0008_washout_color
+ R.drawable.filtershow_fx_0003_blue_crush,
+ R.drawable.filtershow_fx_0008_washout_color,
+ R.drawable.filtershow_fx_0006_x_process
};
int[] fxNameid = {
- R.string.ffx_vintage,
- R.string.ffx_instant,
- R.string.ffx_bleach,
- R.string.ffx_blue_crush,
- R.string.ffx_bw_contrast,
R.string.ffx_punch,
- R.string.ffx_x_process,
+ R.string.ffx_vintage,
+ R.string.ffx_bw_contrast,
+ R.string.ffx_bleach,
+ R.string.ffx_instant,
R.string.ffx_washout,
+ R.string.ffx_blue_crush,
R.string.ffx_washout_color,
+ R.string.ffx_x_process
};
ImagePreset preset = new ImagePreset(); // empty
@@ -522,14 +553,12 @@
filter.setSelected(true);
mCurrentImageSmallFilter = filter;
- filter.setPreviousImageSmallFilter(null);
- preset.setIsFx(true);
- filter.setImagePreset(preset);
+ filter.setImageFilter(new ImageFilterFx(null,getString(R.string.none)));
filter.setController(this);
filter.setImageLoader(mImageLoader);
listFilters.addView(filter);
- ImageSmallFilter previousFilter = filter;
+ ImageSmallFilter previousFilter = filter;
BitmapFactory.Options o = new BitmapFactory.Options();
o.inScaled = false;
@@ -541,8 +570,6 @@
for (int i = 0; i < p; i++) {
filter = new ImageSmallFilter(this);
-
- filter.setPreviousImageSmallFilter(previousFilter);
filter.setImageFilter(fxArray[i]);
filter.setController(this);
filter.setImageLoader(mImageLoader);
@@ -565,15 +592,16 @@
borders[p++] = new ImageFilterBorder(npd1);
Drawable npd2 = getResources().getDrawable(R.drawable.filtershow_border_brush);
borders[p++] = new ImageFilterBorder(npd2);
- borders[p++] = new ImageFilterParametricBorder(Color.BLACK, 100, 0);
- borders[p++] = new ImageFilterParametricBorder(Color.BLACK, 100, 100);
- borders[p++] = new ImageFilterParametricBorder(Color.WHITE, 100, 0);
- borders[p++] = new ImageFilterParametricBorder(Color.WHITE, 100, 100);
+ borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, 0);
+ borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize,
+ mImageBorderSize);
+ borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, 0);
+ borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize,
+ mImageBorderSize);
ImageSmallFilter previousFilter = null;
for (int i = 0; i < p; i++) {
ImageSmallBorder filter = new ImageSmallBorder(this);
- filter.setPreviousImageSmallFilter(previousFilter);
filter.setImageFilter(borders[i]);
filter.setController(this);
filter.setBorder(true);
@@ -626,7 +654,6 @@
}
}
-
// //////////////////////////////////////////////////////////////////////////////
// imageState panel...
@@ -699,6 +726,7 @@
adapter.reset();
ImagePreset original = new ImagePreset(adapter.getItem(0));
mImageShow.setImagePreset(original);
+ mPanelController.resetParameters();
invalidateViews();
}
@@ -712,6 +740,20 @@
};
}
+ @Override
+ public void onBackPressed() {
+ if (mPanelController.onBackPressed()) {
+ saveImage();
+ }
+ }
+
+ public void cannotLoadImage() {
+ CharSequence text = getString(R.string.cannot_load_image);
+ Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
+ toast.show();
+ finish();
+ }
+
// //////////////////////////////////////////////////////////////////////////////
public float getPixelsFromDip(float value) {
@@ -783,14 +825,22 @@
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
Uri selectedImageUri = data.getData();
- mImageLoader.loadBitmap(selectedImageUri,getScreenImageSize());
+ mImageLoader.loadBitmap(selectedImageUri, getScreenImageSize());
}
}
}
public void saveImage() {
- showSavingProgress();
- mImageShow.saveImage(this, null);
+ if (mImageShow.hasModifications()) {
+ // Get the name of the album, to which the image will be saved
+ File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
+ int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
+ String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
+ showSavingProgress(albumName);
+ mImageShow.saveImage(this, null);
+ } else {
+ finish();
+ }
}
static {
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index f13cdd4..2e8dd23 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -20,9 +20,11 @@
import com.android.gallery3d.filtershow.filters.ImageFilterVibrance;
import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
+import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.ui.ImageCurves;
+import com.android.gallery3d.filtershow.ui.ImageButtonTitle;
import java.util.HashMap;
import java.util.Vector;
@@ -113,17 +115,95 @@
private String mEffectName = null;
private int mParameterValue = 0;
private boolean mShowParameterValue = false;
+ private View mAspectButton = null;
+ private int mCurrentAspectButton = 0;
+ private static final int NUMBER_OF_ASPECT_BUTTONS = 6;
+ private static final int ASPECT_NONE = 0;
+ private static final int ASPECT_1TO1 = 1;
+ private static final int ASPECT_5TO7 = 2;
+ private static final int ASPECT_4TO6 = 3;
+ private static final int ASPECT_16TO9 = 4;
+ private static final int ASPECT_ORIG = 5;
- public UtilityPanel(Context context, View view, View textView) {
+ public UtilityPanel(Context context, View view, View textView, View button) {
mContext = context;
mView = view;
mTextView = (TextView) textView;
+ mAspectButton = button;
}
public boolean selected() {
return mSelected;
}
+ public void nextAspectButton() {
+ if (mAspectButton instanceof ImageButtonTitle
+ && mCurrentImage instanceof ImageCrop) {
+ switch (mCurrentAspectButton) {
+ case ASPECT_NONE:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspect1to1_effect));
+ ((ImageCrop) mCurrentImage).apply(1, 1);
+ break;
+ case ASPECT_1TO1:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspect5to7_effect));
+ ((ImageCrop) mCurrentImage).apply(7, 5);
+ break;
+ case ASPECT_5TO7:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspect4to6_effect));
+ ((ImageCrop) mCurrentImage).apply(6, 4);
+ break;
+ case ASPECT_4TO6:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspect9to16_effect));
+ ((ImageCrop) mCurrentImage).apply(16, 9);
+ break;
+ case ASPECT_16TO9:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspectOriginal_effect));
+ ((ImageCrop) mCurrentImage).applyOriginal();
+ break;
+ case ASPECT_ORIG:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspectNone_effect));
+ ((ImageCrop) mCurrentImage).applyClear();
+ break;
+ default:
+ ((ImageButtonTitle) mAspectButton).setText(mContext
+ .getString(R.string.aspect)
+ + " "
+ + mContext.getString(R.string.aspect1to1_effect));
+ ((ImageCrop) mCurrentImage).applyClear();
+ break;
+ }
+ mCurrentAspectButton = (mCurrentAspectButton + 1) % NUMBER_OF_ASPECT_BUTTONS;
+ }
+ }
+
+ public void showAspectButtons() {
+ if (mAspectButton != null)
+ mAspectButton.setVisibility(View.VISIBLE);
+ }
+
+ public void hideAspectButtons() {
+ if (mAspectButton != null)
+ mAspectButton.setVisibility(View.GONE);
+ }
+
public void onNewValue(int value) {
mParameterValue = value;
updateText();
@@ -131,12 +211,12 @@
public void setEffectName(String effectName) {
mEffectName = effectName;
- showParameter(true);
- updateText();
+ setShowParameter(true);
}
- public void showParameter(boolean s) {
+ public void setShowParameter(boolean s) {
mShowParameterValue = s;
+ updateText();
}
public void updateText() {
@@ -229,12 +309,26 @@
imageShow.setPanelController(this);
}
+ public void resetParameters() {
+ mCurrentImage.resetParameter();
+ showPanel(mCurrentPanel);
+ mCurrentImage.select();
+ }
+
+ public boolean onBackPressed() {
+ if (mUtilityPanel == null || !mUtilityPanel.selected()) {
+ return true;
+ }
+ resetParameters();
+ return false;
+ }
+
public void onNewValue(int value) {
mUtilityPanel.onNewValue(value);
}
public void showParameter(boolean s) {
- mUtilityPanel.showParameter(s);
+ mUtilityPanel.setShowParameter(s);
}
public void setCurrentPanel(View panel) {
@@ -245,8 +339,9 @@
mRowPanel = rowPanel;
}
- public void setUtilityPanel(Context context, View utilityPanel, View textView) {
- mUtilityPanel = new UtilityPanel(context, utilityPanel, textView);
+ public void setUtilityPanel(Context context, View utilityPanel, View textView,
+ View button) {
+ mUtilityPanel = new UtilityPanel(context, utilityPanel, textView, button);
}
public void setMasterImage(ImageShow imageShow) {
@@ -335,34 +430,45 @@
public void ensureFilter(String name) {
ImagePreset preset = getImagePreset();
ImageFilter filter = preset.getFilter(name);
- if (filter == null && name.equalsIgnoreCase("Vignette")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.vignette))) {
filter = setImagePreset(new ImageFilterVignette(), name);
}
- if (filter == null && name.equalsIgnoreCase("Sharpen")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.sharpness))) {
filter = setImagePreset(new ImageFilterSharpen(), name);
}
- if (filter == null && name.equalsIgnoreCase("Contrast")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.contrast))) {
filter = setImagePreset(new ImageFilterContrast(), name);
}
- if (filter == null && name.equalsIgnoreCase("Saturated")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.saturation))) {
filter = setImagePreset(new ImageFilterSaturated(), name);
}
- if (filter == null && name.equalsIgnoreCase("Hue")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.hue))) {
filter = setImagePreset(new ImageFilterHue(), name);
}
- if (filter == null && name.equalsIgnoreCase("Exposure")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.exposure))) {
filter = setImagePreset(new ImageFilterExposure(), name);
}
- if (filter == null && name.equalsIgnoreCase("Vibrance")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.vibrance))) {
filter = setImagePreset(new ImageFilterVibrance(), name);
}
- if (filter == null && name.equalsIgnoreCase("Shadows")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(
+ R.string.shadow_recovery))) {
filter = setImagePreset(new ImageFilterShadows(), name);
}
- if (filter == null && name.equalsIgnoreCase("Redeye")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.redeye))) {
filter = setImagePreset(new ImageFilterRedEye(), name);
}
- if (filter == null && name.equalsIgnoreCase("WBalance")) {
+ if (filter == null
+ && name.equalsIgnoreCase(mCurrentImage.getContext().getString(R.string.wbalance))) {
filter = setImagePreset(new ImageFilterWBalance(), name);
}
mMasterImage.setCurrentFilter(filter);
@@ -379,10 +485,16 @@
}
}
+ if (view.getId() == R.id.pickCurvesChannel) {
+ ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
+ curves.nextChannel();
+ return;
+ }
+
if (mCurrentImage != null) {
mCurrentImage.unselect();
}
-
+ mUtilityPanel.hideAspectButtons();
switch (view.getId()) {
case R.id.straightenButton: {
mCurrentImage = showImageView(R.id.imageStraighten);
@@ -390,16 +502,14 @@
mUtilityPanel.setEffectName(ename);
break;
}
- /*
- // TODO: implement crop
case R.id.cropButton: {
mCurrentImage = showImageView(R.id.imageCrop);
String ename = mCurrentImage.getContext().getString(R.string.crop);
mUtilityPanel.setEffectName(ename);
- mUtilityPanel.showParameter(false);
+ mUtilityPanel.setShowParameter(false);
+ mUtilityPanel.showAspectButtons();
break;
}
- */
case R.id.rotateButton: {
mCurrentImage = showImageView(R.id.imageRotate);
String ename = mCurrentImage.getContext().getString(R.string.rotate);
@@ -408,90 +518,94 @@
}
case R.id.flipButton: {
mCurrentImage = showImageView(R.id.imageFlip);
- String ename = mCurrentImage.getContext().getString(R.string.flip);
+ String ename = mCurrentImage.getContext().getString(R.string.mirror);
mUtilityPanel.setEffectName(ename);
- mUtilityPanel.showParameter(false);
+ mUtilityPanel.setShowParameter(false);
break;
}
case R.id.vignetteButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.vignette);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Vignette");
+ ensureFilter(ename);
break;
}
case R.id.curvesButtonRGB: {
ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
- String ename = mCurrentImage.getContext().getString(R.string.curvesRGB);
+ String ename = curves.getContext().getString(R.string.curvesRGB);
mUtilityPanel.setEffectName(ename);
- curves.setUseRed(true);
- curves.setUseGreen(true);
- curves.setUseBlue(true);
+ mUtilityPanel.setShowParameter(false);
curves.reloadCurve();
mCurrentImage = curves;
break;
}
case R.id.sharpenButton: {
mCurrentImage = showImageView(R.id.imageZoom).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.sharpen);
+ String ename = mCurrentImage.getContext().getString(R.string.sharpness);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Sharpen");
+ ensureFilter(ename);
break;
}
case R.id.contrastButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.contrast);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Contrast");
+ ensureFilter(ename);
break;
}
case R.id.saturationButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.saturation);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Saturated");
+ ensureFilter(ename);
break;
}
case R.id.wbalanceButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(false);
String ename = mCurrentImage.getContext().getString(R.string.wbalance);
mUtilityPanel.setEffectName(ename);
- ensureFilter("WBalance");
+ mUtilityPanel.setShowParameter(false);
+ ensureFilter(ename);
break;
}
case R.id.hueButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.hue);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Hue");
+ ensureFilter(ename);
break;
}
case R.id.exposureButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.exposure);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Exposure");
+ ensureFilter(ename);
break;
}
case R.id.vibranceButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.vibrance);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Vibrance");
+ ensureFilter(ename);
break;
}
case R.id.shadowRecoveryButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.shadow_recovery);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Shadows");
+ ensureFilter(ename);
break;
}
case R.id.redEyeButton: {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.redeye);
mUtilityPanel.setEffectName(ename);
- ensureFilter("Redeye");
+ ensureFilter(ename);
+ break;
+ }
+ case R.id.aspect: {
+ mUtilityPanel.nextAspectButton();
+ mUtilityPanel.showAspectButtons();
break;
}
case R.id.applyEffect: {
diff --git a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
index 67bd49b..25d1db4 100644
--- a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
+++ b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
@@ -62,7 +62,7 @@
private CachedPreset getCachedPreset(ImagePreset preset) {
for (int i = 0; i < mCache.size(); i++) {
CachedPreset cache = mCache.elementAt(i);
- if (cache.mPreset == preset && !cache.mBusy) {
+ if (cache.mPreset == preset) {
return cache;
}
}
@@ -73,7 +73,7 @@
public Bitmap get(ImagePreset preset) {
// Log.v(LOGTAG, "get preset " + preset.name() + " : " + preset);
CachedPreset cache = getCachedPreset(preset);
- if (cache != null) {
+ if (cache != null && !cache.mBusy) {
return cache.mBitmap;
}
// Log.v(LOGTAG, "didn't find preset " + preset.name() + " : " + preset
@@ -138,18 +138,21 @@
public void prepare(ImagePreset preset) {
// Log.v(LOGTAG, "prepare preset " + preset.name() + " : " + preset);
CachedPreset cache = getCachedPreset(preset);
- if (cache == null) {
- if (mCache.size() < mCacheSize) {
- cache = new CachedPreset();
- mCache.add(cache);
- } else {
- cache = getOldestCachedPreset();
+ if (cache == null || (cache.mBitmap == null && !cache.mBusy)) {
+ if (cache == null) {
+ if (mCache.size() < mCacheSize) {
+ cache = new CachedPreset();
+ mCache.add(cache);
+ } else {
+ cache = getOldestCachedPreset();
+ }
}
if (cache != null) {
cache.mPreset = preset;
+ willCompute(cache);
}
}
- willCompute(cache);
+
}
}
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index e00a1b7..6beaed6 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -48,14 +48,25 @@
private int mOrientation = 0;
private HistoryAdapter mAdapter = null;
- private static int PORTRAIT_ORIENTATION = 6;
+
+ private FilterShowActivity mActivity = null;
+
+ private static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
+ private static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
+ private static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
+ private static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
+ private static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
+ private static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
+ private static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
+ private static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
private Context mContext = null;
private Uri mUri = null;
private Rect mOriginalBounds = null;
- public ImageLoader(Context context) {
+ public ImageLoader(FilterShowActivity activity, Context context) {
+ mActivity = activity;
mContext = context;
mCache = new DelayedPresetCache(this, 30);
mHiresCache = new DelayedPresetCache(this, 2);
@@ -64,8 +75,11 @@
public void loadBitmap(Uri uri,int size) {
mUri = uri;
mOrientation = getOrientation(uri);
-
mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
+ if (mOriginalBitmapSmall == null) {
+ // Couldn't read the bitmap, let's exit
+ mActivity.cannotLoadImage();
+ }
mOriginalBitmapLarge = loadScaledBitmap(uri, size);
updateBitmaps();
}
@@ -90,7 +104,20 @@
MediaStore.Images.ImageColumns.ORIENTATION
},
null, null, null);
- return cursor.moveToNext() ? cursor.getInt(0) : -1;
+ if (cursor.moveToNext()){
+ int ori = cursor.getInt(0);
+
+ switch (ori){
+ case 0: return ORI_NORMAL;
+ case 90: return ORI_ROTATE_90;
+ case 270: return ORI_ROTATE_270;
+ case 180: return ORI_ROTATE_180;
+ default:
+ return -1;
+ }
+ } else{
+ return -1;
+ }
} catch (SQLiteException e){
return ExifInterface.ORIENTATION_UNDEFINED;
} finally {
@@ -111,18 +138,56 @@
}
private void updateBitmaps() {
+ if (mOrientation > 1) {
+ mOriginalBitmapSmall = rotateToPortrait(mOriginalBitmapSmall,mOrientation);
+ mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge,mOrientation);
+ }
mCache.setOriginalBitmap(mOriginalBitmapSmall);
mHiresCache.setOriginalBitmap(mOriginalBitmapLarge);
- if (mOrientation == PORTRAIT_ORIENTATION) {
- mOriginalBitmapSmall = rotateToPortrait(mOriginalBitmapSmall);
- mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge);
- }
warnListeners();
}
- private Bitmap rotateToPortrait(Bitmap bitmap) {
- Matrix matrix = new Matrix();
- matrix.postRotate(90);
+ private Bitmap rotateToPortrait(Bitmap bitmap,int ori) {
+ Matrix matrix = new Matrix();
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ if (ori == ORI_ROTATE_90 ||
+ ori == ORI_ROTATE_270 ||
+ ori == ORI_TRANSPOSE||
+ ori == ORI_TRANSVERSE) {
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+ switch(ori){
+ case ORI_ROTATE_90:
+ matrix.setRotate(90,w/2f,h/2f);
+ break;
+ case ORI_ROTATE_180:
+ matrix.setRotate(180,w/2f,h/2f);
+ break;
+ case ORI_ROTATE_270:
+ matrix.setRotate(270,w/2f,h/2f);
+ break;
+ case ORI_FLIP_HOR:
+ matrix.preScale(-1, 1);
+ break;
+ case ORI_FLIP_VERT:
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSPOSE:
+ matrix.setRotate(90,w/2f,h/2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSVERSE:
+ matrix.setRotate(270,w/2f,h/2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_NORMAL:
+ default:
+ return bitmap;
+ }
+
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}
@@ -295,6 +360,7 @@
mFullOriginalBitmap = BitmapFactory.decodeStream(is, null, options);
// TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
// exist)
+ mFullOriginalBitmap = rotateToPortrait(mFullOriginalBitmap,mOrientation);
mSaveCopy = mFullOriginalBitmap;
preset.setIsHighQuality(true);
preset.setScaleFactor(1.0f);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
index 6d0c020..78a8351 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
@@ -5,6 +5,9 @@
public class ImageFilter implements Cloneable {
+ protected int mMaxParameter = 100;
+ protected int mMinParameter = -100;
+ protected int mDefaultParameter = 0;
protected int mParameter = 0;
protected String mName = "Original";
private final String LOGTAG = "ImageFilter";
@@ -29,9 +32,19 @@
filter.setName(getName());
filter.setParameter(getParameter());
filter.setFilterType(filterType);
+ filter.mMaxParameter = mMaxParameter;
+ filter.mMinParameter = mMinParameter;
+ filter.mDefaultParameter = mDefaultParameter;
return filter;
}
+ public boolean isNil() {
+ if (mParameter == mDefaultParameter) {
+ return true;
+ }
+ return false;
+ }
+
public void setName(String name) {
mName = name;
}
@@ -53,6 +66,30 @@
mParameter = value;
}
+ /**
+ * The maximum allowed value (inclusive)
+ * @return maximum value allowed as input to this filter
+ */
+ public int getMaxParameter(){
+ return mMaxParameter;
+ }
+
+ /**
+ * The minimum allowed value (inclusive)
+ * @return minimum value allowed as input to this filter
+ */
+ public int getMinParameter(){
+ return mMinParameter;
+ }
+
+ /**
+ * Returns the default value returned by this filter.
+ * @return default value
+ */
+ public int getDefaultParameter(){
+ return mDefaultParameter;
+ }
+
public boolean same(ImageFilter filter) {
if (!filter.getName().equalsIgnoreCase(getName())) {
return false;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java
index dd7d17c..4291fe4 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java
@@ -23,6 +23,14 @@
}
@Override
+ public boolean isNil() {
+ if (mNinePatch == null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public boolean same(ImageFilter filter) {
boolean isBorderFilter = super.same(filter);
if (!isBorderFilter) {
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
index 01b280b..0f05a1c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
@@ -9,12 +9,7 @@
private static final String LOGTAG = "ImageFilterCurves";
- private final float[] mCurve = new float[256];
-
- private boolean mUseRed = true;
- private boolean mUseGreen = true;
- private boolean mUseBlue = true;
- private Spline mSpline = null;
+ private final Spline[] mSplines = new Spline[4];
public ImageFilterCurves() {
mName = "Curves";
@@ -23,29 +18,22 @@
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterCurves filter = (ImageFilterCurves) super.clone();
- filter.setCurve(mCurve);
- filter.setSpline(new Spline(mSpline));
+ for (int i = 0; i < 4; i++) {
+ if (mSplines[i] != null) {
+ filter.setSpline(new Spline(mSplines[i]), i);
+ }
+ }
return filter;
}
- public void setUseRed(boolean value) {
- mUseRed = value;
- }
-
- public void setUseGreen(boolean value) {
- mUseGreen = value;
- }
-
- public void setUseBlue(boolean value) {
- mUseBlue = value;
- }
-
- public void setCurve(float[] curve) {
- for (int i = 0; i < curve.length; i++) {
- if (i < 256) {
- mCurve[i] = curve[i];
+ @Override
+ public boolean isNil() {
+ for (int i = 0; i < 4; i++) {
+ if (mSplines[i] != null && !mSplines[i].isOriginal()) {
+ return false;
}
}
+ return true;
}
@Override
@@ -55,36 +43,48 @@
return false;
}
ImageFilterCurves curve = (ImageFilterCurves) filter;
- for (int i = 0; i < 256; i++) {
- if (curve.mCurve[i] != mCurve[i]) {
+ for (int i = 0; i < 4; i++) {
+ if (mSplines[i] != curve.mSplines[i]) {
return false;
}
}
return true;
}
- public void populateArray(int[] array) {
+ public void populateArray(int[] array, int curveIndex) {
+ Spline spline = mSplines[curveIndex];
+ if (spline == null) {
+ return;
+ }
+ float[] curve = spline.getAppliedCurve();
for (int i = 0; i < 256; i++) {
- array[i] = (int) (mCurve[i]);
+ array[i] = (int) (curve[i] * 255);
}
}
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
+ if (!mSplines[Spline.RGB].isOriginal()) {
+ int[] rgbGradient = new int[256];
+ populateArray(rgbGradient, Spline.RGB);
+ nativeApplyGradientFilter(bitmap, bitmap.getWidth(), bitmap.getHeight(),
+ rgbGradient, rgbGradient, rgbGradient);
+ }
+
int[] redGradient = null;
- if (mUseRed) {
+ if (!mSplines[Spline.RED].isOriginal()) {
redGradient = new int[256];
- populateArray(redGradient);
+ populateArray(redGradient, Spline.RED);
}
int[] greenGradient = null;
- if (mUseGreen) {
+ if (!mSplines[Spline.GREEN].isOriginal()) {
greenGradient = new int[256];
- populateArray(greenGradient);
+ populateArray(greenGradient, Spline.GREEN);
}
int[] blueGradient = null;
- if (mUseBlue) {
+ if (!mSplines[Spline.BLUE].isOriginal()) {
blueGradient = new int[256];
- populateArray(blueGradient);
+ populateArray(blueGradient, Spline.BLUE);
}
nativeApplyGradientFilter(bitmap, bitmap.getWidth(), bitmap.getHeight(),
@@ -92,11 +92,11 @@
return bitmap;
}
- public void setSpline(Spline spline) {
- mSpline = spline;
+ public void setSpline(Spline spline, int splineIndex) {
+ mSplines[splineIndex] = spline;
}
- public Spline getSpline() {
- return mSpline;
+ public Spline getSpline(int splineIndex) {
+ return mSplines[splineIndex];
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
index 1575b18..3adfbef 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
@@ -2,17 +2,10 @@
package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import com.android.gallery3d.R;
-
-import java.util.Arrays;
public class ImageFilterFx extends ImageFilter {
private static final String TAG = "ImageFilterFx";
Bitmap fxBitmap;
-
public ImageFilterFx(Bitmap fxBitmap,String name) {
setFilterType(TYPE_FX);
mName = name;
@@ -26,9 +19,20 @@
return filter;
}
+ @Override
+ public boolean isNil() {
+ if (fxBitmap != null) {
+ return false;
+ }
+ return true;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h,Bitmap fxBitmap, int fxw, int fxh);
+ @Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
+ if (fxBitmap==null)
+ return bitmap;
int w = bitmap.getWidth();
int h = bitmap.getHeight();
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
index 08c09fb..3d48b7e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
@@ -20,6 +20,8 @@
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
@@ -61,23 +63,19 @@
native protected void nativeApplyFilterStraighten(Bitmap src, int srcWidth, int srcHeight,
Bitmap dst, int dstWidth, int dstHeight, float straightenAngle);
- public Matrix buildMatrix(Bitmap bitmap, boolean rotated) {
- Matrix drawMatrix = new Matrix();
- float dx = bitmap.getWidth() / 2.0f;
- float dy = bitmap.getHeight() / 2.0f;
-
- Matrix flipper = mGeometry.getFlipMatrix(bitmap.getWidth(), bitmap.getHeight());
- drawMatrix.postConcat(flipper);
- drawMatrix.postTranslate(-dx, -dy);
- drawMatrix.postScale(1.0f / mGeometry.getScaleFactor(), 1.0f / mGeometry.getScaleFactor());
- float angle = (mGeometry.getRotation() + mGeometry.getStraightenRotation());
- drawMatrix.postRotate(angle);
- if (rotated) {
- drawMatrix.postTranslate(dy, dx);
- } else {
- drawMatrix.postTranslate(dx, dy);
+ public Matrix buildMatrix(RectF r) {
+ float dx = r.width()/2;
+ float dy = r.height()/2;
+ if(mGeometry.hasSwitchedWidthHeight()){
+ float temp = dx;
+ dx = dy;
+ dy = temp;
}
- return drawMatrix;
+ float w = r.left * 2 + r.width();
+ float h = r.top * 2 + r.height();
+ Matrix m = mGeometry.buildGeometryMatrix(w, h, 1f, dx, dy, false);
+
+ return m;
}
@Override
@@ -85,18 +83,18 @@
// TODO: implement bilinear or bicubic here... for now, just use
// canvas to do a simple implementation...
// TODO: and be more memory efficient! (do it in native?)
-
+ Rect cropBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ RectF crop = mGeometry.getCropBounds(bitmap);
+ if(crop.width() > 0 && crop.height() > 0)
+ crop.roundOut(cropBounds);
Bitmap temp = null;
- float rotation = mGeometry.getRotation();
- boolean rotated = false;
- if (rotation == 0 || rotation % 180 == 0) {
- temp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), mConfig);
+ if (mGeometry.hasSwitchedWidthHeight()) {
+ temp = Bitmap.createBitmap(cropBounds.height(), cropBounds.width(), mConfig);
} else {
- temp = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getWidth(), mConfig);
- rotated = true;
+ temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig);
}
- Matrix drawMatrix = buildMatrix(bitmap, rotated);
+ Matrix drawMatrix = buildMatrix(crop);
Canvas canvas = new Canvas(temp);
canvas.drawBitmap(bitmap, drawMatrix, new Paint());
return temp;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
index 6f6f9e8..e5e52ec 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
@@ -9,6 +9,8 @@
public ImageFilterHue() {
mName = "Hue";
cmatrix = new ColorSpaceMatrix();
+ mMaxParameter = 180;
+ mMinParameter = -180;
}
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java
index 3649d28..66dad78 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java
@@ -31,6 +31,11 @@
}
@Override
+ public boolean isNil() {
+ return false;
+ }
+
+ @Override
public boolean same(ImageFilter filter) {
boolean isBorderFilter = super.same(filter);
if (!isBorderFilter) {
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index 9d9c7e5..5e613eb 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -18,7 +18,6 @@
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
- Log.v(TAG,"White Balance Call");
nativeApplyFilter(bitmap, w, h, -1,-1);
return bitmap;
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
new file mode 100644
index 0000000..95d174f
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
@@ -0,0 +1,54 @@
+package com.android.gallery3d.filtershow.imageshow;
+
+public class GeometryMath {
+ protected static float clamp(float i, float low, float high) {
+ return Math.max(Math.min(i, high), low);
+ }
+
+ protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
+ float x1 = l1[0];
+ float x2 = l2[0];
+ float y1 = l1[1];
+ float y2 = l2[1];
+ float xdelt = x2 - x1;
+ float ydelt = y2 - y1;
+ if (xdelt == 0 && ydelt == 0)
+ return null;
+ float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt)
+ / (xdelt * xdelt + ydelt * ydelt);
+ float[] ret = {
+ (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
+ };
+ return ret;
+ }
+
+ //A . B
+ protected static float dotProduct(float[] a, float[] b){
+ return a[0] * b[0] + a[1] * b[1];
+ }
+
+ protected static float[] normalize(float[] a){
+ float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
+ float[] b = { a[0] / length, a[1] / length };
+ return b;
+ }
+
+ //A onto B
+ protected static float scalarProjection(float[] a, float[] b){
+ float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]);
+ return dotProduct(a, b) / length;
+ }
+
+ protected static float[] getVectorFromPoints(float [] point1, float [] point2){
+ float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ return p;
+ }
+
+ protected static float[] getUnitVectorFromPoints(float [] point1, float [] point2){
+ float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]);
+ p[0] = p[0] / length;
+ p[1] = p[1] / length;
+ return p;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index 0eb2e22..164b6f9 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -22,26 +22,17 @@
import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
-/**
- * This class holds metadata about an image's geometry. Specifically: rotation,
- * scaling, cropping, and image boundaries. It maintains the invariant that the
- * cropping boundaries are within or equal to the image boundaries (before
- * rotation) WHEN mSafe is true.
- */
-
public class GeometryMetadata {
// Applied in order: rotate, crop, scale.
// Do not scale saved image (presumably?).
private static final ImageFilterGeometry mImageFilter = new ImageFilterGeometry();
+ private static final String LOGTAG = "GeometryMetadata";
private float mScaleFactor = 1.0f;
private float mRotation = 0;
private float mStraightenRotation = 0;
private final RectF mCropBounds = new RectF();
private final RectF mPhotoBounds = new RectF();
private FLIP mFlip = FLIP.NONE;
- private boolean mSafe = false;
-
- private Matrix mMatrix = new Matrix();
private RectF mBounds = new RectF();
@@ -56,13 +47,31 @@
set(g);
}
- public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality){
+ public boolean hasModifications() {
+ if (mScaleFactor != 1.0f) {
+ return true;
+ }
+ if (mRotation != 0) {
+ return true;
+ }
+ if (mStraightenRotation != 0) {
+ return true;
+ }
+ if (!mCropBounds.equals(mPhotoBounds)) {
+ return true;
+ }
+ return false;
+ }
+
+ public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality) {
+ if (!hasModifications()) {
+ return original;
+ }
mImageFilter.setGeometryMetadata(this);
Bitmap m = mImageFilter.apply(original, scaleFactor, highQuality);
return m;
}
- // Safe as long as invariant holds.
public void set(GeometryMetadata g) {
mScaleFactor = g.mScaleFactor;
mRotation = g.mRotation;
@@ -70,8 +79,6 @@
mCropBounds.set(g.mCropBounds);
mPhotoBounds.set(g.mPhotoBounds);
mFlip = g.mFlip;
- mSafe = g.mSafe;
- mMatrix = g.mMatrix;
mBounds = g.mBounds;
}
@@ -87,10 +94,19 @@
return mStraightenRotation;
}
- public RectF getCropBounds() {
+ public RectF getPreviewCropBounds() {
return new RectF(mCropBounds);
}
+ public RectF getCropBounds(Bitmap bitmap) {
+ float scale = 1.0f;
+ if (mPhotoBounds.width() > 0) {
+ scale = bitmap.getWidth() / mPhotoBounds.width();
+ }
+ return new RectF(mCropBounds.left * scale, mCropBounds.top * scale,
+ mCropBounds.right * scale, mCropBounds.bottom * scale);
+ }
+
public FLIP getFlipType() {
return mFlip;
}
@@ -99,10 +115,6 @@
return new RectF(mPhotoBounds);
}
- public boolean safe() {
- return mSafe;
- }
-
public void setScaleFactor(float scale) {
mScaleFactor = scale;
}
@@ -119,41 +131,12 @@
mStraightenRotation = straighten;
}
- /**
- * Sets crop bounds to be the intersection of mPhotoBounds and the new crop
- * bounds. If there was no intersection, returns false and does not set crop
- * bounds
- */
- public boolean safeSetCropBounds(RectF newCropBounds) {
- if (mCropBounds.setIntersect(newCropBounds, mPhotoBounds)) {
- mSafe = true;
- return true;
- }
- return false;
- }
-
public void setCropBounds(RectF newCropBounds) {
mCropBounds.set(newCropBounds);
- mSafe = false;
- }
-
- /**
- * Sets mPhotoBounds to be the new photo bounds and sets mCropBounds to be
- * the intersection of the new photo bounds and the old crop bounds. Sets
- * the crop bounds to mPhotoBounds if there is no intersection.
- */
-
- public void safeSetPhotoBounds(RectF newPhotoBounds) {
- mPhotoBounds.set(newPhotoBounds);
- if (!mCropBounds.intersect(mPhotoBounds)) {
- mCropBounds.set(mPhotoBounds);
- }
- mSafe = true;
}
public void setPhotoBounds(RectF newPhotoBounds) {
mPhotoBounds.set(newPhotoBounds);
- mSafe = false;
}
public boolean cropFitsInPhoto(RectF cropBounds) {
@@ -171,7 +154,7 @@
return (mScaleFactor == d.mScaleFactor &&
mRotation == d.mRotation &&
mStraightenRotation == d.mStraightenRotation &&
- mFlip == d.mFlip && mSafe == d.mSafe &&
+ mFlip == d.mFlip &&
mCropBounds.equals(d.mCropBounds) && mPhotoBounds.equals(d.mPhotoBounds));
}
@@ -184,15 +167,13 @@
result = 31 * result + mFlip.hashCode();
result = 31 * result + mCropBounds.hashCode();
result = 31 * result + mPhotoBounds.hashCode();
- result = 31 * result + (mSafe ? 1 : 0);
return result;
}
@Override
public String toString() {
return getClass().getName() + "[" + "scale=" + mScaleFactor
- + ",rotation=" + mRotation + ",flip=" + mFlip + ",safe="
- + (mSafe ? "true" : "false") + ",straighten="
+ + ",rotation=" + mRotation + ",flip=" + mFlip + ",straighten="
+ mStraightenRotation + ",cropRect=" + mCropBounds.toShortString()
+ ",photoRect=" + mPhotoBounds.toShortString() + "]";
}
@@ -228,7 +209,34 @@
}
}
- public Matrix getMatrix() {
- return mMatrix;
+ public boolean hasSwitchedWidthHeight() {
+ return (((int) (mRotation / 90)) % 2) != 0;
+ }
+
+ public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
+ float rotation) {
+ float dx0 = width / 2;
+ float dy0 = height / 2;
+ Matrix m = getFlipMatrix(width, height);
+ m.postTranslate(-dx0, -dy0);
+ m.postRotate(rotation);
+ m.postScale(scaling, scaling);
+ m.postTranslate(dx, dy);
+ return m;
+ }
+
+ public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
+ boolean onlyRotate) {
+ float rot = mRotation;
+ if (!onlyRotate) {
+ rot += mStraightenRotation;
+ }
+ return buildGeometryMatrix(width, height, scaling, dx, dy, rot);
+ }
+
+ public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) {
+ float w = mPhotoBounds.width();
+ float h = mPhotoBounds.height();
+ return buildGeometryMatrix(w, h, scaling, dx, dy, false);
}
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
index 90d36e9..e34e3a2 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -22,12 +22,10 @@
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
-
import com.android.gallery3d.R;
public class ImageCrop extends ImageGeometry {
@@ -38,21 +36,22 @@
private static final int MOVE_BOTTOM = 8;
private static final int MOVE_BLOCK = 16;
+ //Corners
+ private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
+ private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
+ private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
+ private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
+
private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
private static final int TOUCH_TOLERANCE = 30;
- private static final int SHADOW_ALPHA = 160;
- private final float mAspectWidth = 4;
- private final float mAspectHeight = 3;
- private final boolean mFixAspectRatio = false; // not working yet
+ private boolean mFirstDraw = true;
+ private float mAspectWidth = 1;
+ private float mAspectHeight = 1;
+ private boolean mFixAspectRatio = false;
private final Paint borderPaint;
- private float mCropOffsetX = 0;
- private float mCropOffsetY = 0;
- private float mPrevOffsetX = 0;
- private float mPrevOffsetY = 0;
-
private int movingEdges;
private final Drawable cropIndicator;
private final int indicatorSize;
@@ -85,23 +84,133 @@
borderPaint.setStrokeWidth(2f);
}
+ private boolean switchCropBounds(int moving_corner, RectF dst) {
+ RectF crop = getCropBoundsDisplayed();
+ float dx1 = 0;
+ float dy1 = 0;
+ float dx2 = 0;
+ float dy2 = 0;
+ if ((moving_corner & MOVE_RIGHT) != 0) {
+ dx1 = mCurrentX - crop.right;
+ } else if ((moving_corner & MOVE_LEFT) != 0) {
+ dx1 = mCurrentX - crop.left;
+ }
+ if ((moving_corner & MOVE_BOTTOM) != 0) {
+ dy1 = mCurrentY - crop.bottom;
+ } else if ((moving_corner & MOVE_TOP) != 0) {
+ dy1 = mCurrentY - crop.top;
+ }
+ RectF newCrop = null;
+ //Fix opposite corner in place and move sides
+ if (moving_corner == BOTTOM_RIGHT) {
+ newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top
+ + crop.width());
+ } else if (moving_corner == BOTTOM_LEFT) {
+ newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top
+ + crop.width());
+ } else if (moving_corner == TOP_LEFT) {
+ newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(),
+ crop.right, crop.bottom);
+ } else if (moving_corner == TOP_RIGHT) {
+ newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left
+ + crop.height(), crop.bottom);
+ }
+ if ((moving_corner & MOVE_RIGHT) != 0) {
+ dx2 = mCurrentX - newCrop.right;
+ } else if ((moving_corner & MOVE_LEFT) != 0) {
+ dx2 = mCurrentX - newCrop.left;
+ }
+ if ((moving_corner & MOVE_BOTTOM) != 0) {
+ dy2 = mCurrentY - newCrop.bottom;
+ } else if ((moving_corner & MOVE_TOP) != 0) {
+ dy2 = mCurrentY - newCrop.top;
+ }
+ if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){
+ Matrix m = getCropBoundDisplayMatrix();
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0)){
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
+ return false;
+ }
+ if (!m0.mapRect(newCrop)){
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
+ return false;
+ }
+ float temp = mAspectWidth;
+ mAspectWidth = mAspectHeight;
+ mAspectHeight = temp;
+ dst.set(newCrop);
+ return true;
+ }
+ return false;
+ }
+
+ public void apply(float w, float h){
+ mFixAspectRatio = true;
+ mAspectWidth = w;
+ mAspectHeight = h;
+ setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten()));
+ cropSetup();
+ saveAndSetPreset();
+ invalidate();
+ }
+
+ public void applyOriginal() {
+ mFixAspectRatio = true;
+ RectF photobounds = getLocalPhotoBounds();
+ float w = photobounds.width();
+ float h = photobounds.height();
+ float scale = Math.min(w, h);
+ mAspectWidth = w / scale;
+ mAspectHeight = h / scale;
+ setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds,
+ getLocalStraighten()));
+ cropSetup();
+ saveAndSetPreset();
+ invalidate();
+ }
+
+ public void applyClear() {
+ mFixAspectRatio = false;
+ setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten()));
+ cropSetup();
+ saveAndSetPreset();
+ invalidate();
+ }
+
private float getScaledMinWidthHeight() {
- RectF disp = getLocalDisplayBounds();
+ RectF disp = new RectF(0, 0, getWidth(), getHeight());
float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
- / getLocalScale();
+ / computeScale(getWidth(), getHeight());
return scaled;
}
- protected static Matrix getCropRotationMatrix(float rotation, RectF localImage) {
- Matrix m = new Matrix();
- m.setRotate(rotation, localImage.centerX(), localImage.centerY());
+ protected Matrix getCropRotationMatrix(float rotation, RectF localImage) {
+ Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height());
+ m.postRotate(rotation, localImage.centerX(), localImage.centerY());
if (!m.rectStaysRect()) {
return null;
}
return m;
}
- @Override
+ protected Matrix getCropBoundDisplayMatrix(){
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+ if (m == null) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
+ m = new Matrix();
+ }
+ float zoom = computeScale(getWidth(), getHeight());
+ m.postTranslate(mXOffset, mYOffset);
+ m.postScale(zoom, zoom, mCenterX, mCenterY);
+ return m;
+ }
+
protected RectF getCropBoundsDisplayed() {
RectF bounds = getLocalCropBounds();
RectF crop = new RectF(bounds);
@@ -115,7 +224,7 @@
m.mapRect(crop);
}
m = new Matrix();
- float zoom = getLocalScale();
+ float zoom = computeScale(getWidth(), getHeight());
m.setScale(zoom, zoom, mCenterX, mCenterY);
m.preTranslate(mXOffset, mYOffset);
m.mapRect(crop);
@@ -137,6 +246,44 @@
return crop;
}
+ private RectF getUnrotatedCropBounds(RectF cropBounds) {
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+
+ if (m == null) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX");
+ return null;
+ }
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0)) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
+ return null;
+ }
+ RectF crop = new RectF(cropBounds);
+ if (!m0.mapRect(crop)) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
+ return null;
+ }
+ return crop;
+ }
+
+ private RectF getRotatedStraightenBounds() {
+ RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten());
+ Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
+
+ if (m == null) {
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE");
+ return null;
+ } else {
+ m.mapRect(straightenBounds);
+ }
+ return straightenBounds;
+ }
+
/**
* Sets cropped bounds; modifies the bounds if it's smaller than the allowed
* dimensions.
@@ -145,16 +292,33 @@
// Avoid cropping smaller than minimum width or height.
RectF cbounds = new RectF(bounds);
float minWidthHeight = getScaledMinWidthHeight();
+ float aw = mAspectWidth;
+ float ah = mAspectHeight;
+ if (mFixAspectRatio) {
+ minWidthHeight /= aw * ah;
+ int r = (int) (getLocalRotation() / 90);
+ if (r % 2 != 0) {
+ float temp = aw;
+ aw = ah;
+ ah = temp;
+ }
+ }
float newWidth = cbounds.width();
float newHeight = cbounds.height();
- if (newWidth < minWidthHeight) {
- newWidth = minWidthHeight;
+ if (mFixAspectRatio) {
+ if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) {
+ newWidth = minWidthHeight * aw;
+ newHeight = minWidthHeight * ah;
+ }
+ } else {
+ if (newWidth < minWidthHeight) {
+ newWidth = minWidthHeight;
+ }
+ if (newHeight < minWidthHeight) {
+ newHeight = minWidthHeight;
+ }
}
- if (newHeight < minWidthHeight) {
- newHeight = minWidthHeight;
- }
-
RectF pbounds = getLocalPhotoBounds();
if (pbounds.width() < minWidthHeight) {
newWidth = pbounds.width();
@@ -164,18 +328,14 @@
}
cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
- RectF snappedCrop = findCropBoundForRotatedImg(cbounds, pbounds, getLocalStraighten(),
- mCenterX - mXOffset, mCenterY - mYOffset);
-
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), getLocalStraighten());
- snappedCrop.intersect(straightenBounds);
-
+ RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten());
+ cbounds.intersect(straightenBounds);
if (mFixAspectRatio) {
- // TODO: add aspect ratio stuff
- fixAspectRatio(snappedCrop, mAspectWidth, mAspectHeight);
+ fixAspectRatio(cbounds, aw, ah);
}
- setLocalCropBounds(snappedCrop);
+ setLocalCropBounds(cbounds);
invalidate();
}
@@ -202,35 +362,150 @@
else if (bottom <= TOUCH_TOLERANCE) {
movingEdges |= MOVE_BOTTOM;
}
+ // Check inside block.
+ if (cropped.contains(x, y) && (movingEdges == 0)) {
+ movingEdges = MOVE_BLOCK;
+ }
+ if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) {
+ movingEdges = fixEdgeToCorner(movingEdges);
+ }
invalidate();
}
+ private int fixEdgeToCorner(int moving_edges){
+ if (moving_edges == MOVE_LEFT) {
+ moving_edges |= MOVE_TOP;
+ }
+ if (moving_edges == MOVE_TOP) {
+ moving_edges |= MOVE_LEFT;
+ }
+ if (moving_edges == MOVE_RIGHT) {
+ moving_edges |= MOVE_BOTTOM;
+ }
+ if (moving_edges == MOVE_BOTTOM) {
+ moving_edges |= MOVE_RIGHT;
+ }
+ return moving_edges;
+ }
+
+ private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){
+ RectF newCrop = null;
+ //Fix opposite corner in place and move sides
+ if (moving_corner == BOTTOM_RIGHT) {
+ newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
+ + dy);
+ } else if (moving_corner == BOTTOM_LEFT) {
+ newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height()
+ + dy);
+ } else if (moving_corner == TOP_LEFT) {
+ newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy,
+ r.right, r.bottom);
+ } else if (moving_corner == TOP_RIGHT) {
+ newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left
+ + r.width() + dx, r.bottom);
+ }
+ return newCrop;
+ }
+
private void moveEdges(float dX, float dY) {
RectF cropped = getRotatedCropBounds();
float minWidthHeight = getScaledMinWidthHeight();
- float scale = getLocalScale();
+ float scale = computeScale(getWidth(), getHeight());
float deltaX = dX / scale;
float deltaY = dY / scale;
- if (movingEdges == MOVE_BLOCK) {
- // TODO
- } else {
- if ((movingEdges & MOVE_LEFT) != 0) {
- cropped.left = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight);
- fixRectAspectW(cropped);
+ int select = movingEdges;
+ if (mFixAspectRatio && (select != MOVE_BLOCK)) {
+ if (select == MOVE_LEFT) {
+ select |= MOVE_TOP;
}
- if ((movingEdges & MOVE_TOP) != 0) {
- cropped.top = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight);
- fixRectAspectH(cropped);
+ if (select == MOVE_TOP) {
+ select |= MOVE_LEFT;
}
- if ((movingEdges & MOVE_RIGHT) != 0) {
- cropped.right = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight);
- fixRectAspectW(cropped);
+ if (select == MOVE_RIGHT) {
+ select |= MOVE_BOTTOM;
}
- if ((movingEdges & MOVE_BOTTOM) != 0) {
- cropped.bottom = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight);
- fixRectAspectH(cropped);
+ if (select == MOVE_BOTTOM) {
+ select |= MOVE_RIGHT;
+ }
+ RectF blank = new RectF();
+ if(switchCropBounds(select, blank)){
+ setCropBounds(blank);
+ return;
}
}
+
+ if (select == MOVE_BLOCK) {
+ RectF straight = getRotatedStraightenBounds();
+ // Move the whole cropped bounds within the photo display bounds.
+ deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX)
+ : Math.max(straight.left - cropped.left, deltaX);
+ deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY)
+ : Math.max(straight.top - cropped.top, deltaY);
+ cropped.offset(deltaX, deltaY);
+ } else {
+ float dx = 0;
+ float dy = 0;
+
+ if ((select & MOVE_LEFT) != 0) {
+ dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left;
+ }
+ if ((select & MOVE_TOP) != 0) {
+ dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top;
+ }
+ if ((select & MOVE_RIGHT) != 0) {
+ dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight)
+ - cropped.right;
+ }
+ if ((select & MOVE_BOTTOM) != 0) {
+ dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
+ - cropped.bottom;
+ }
+
+ if (mFixAspectRatio) {
+ RectF crop = getCropBoundsDisplayed();
+ float [] l1 = {crop.left, crop.bottom};
+ float [] l2 = {crop.right, crop.top};
+ if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){
+ l1[1] = crop.top;
+ l2[1] = crop.bottom;
+ }
+ float[] b = { l1[0] - l2[0], l1[1] - l2[1] };
+ float[] disp = {dx, dy};
+ float[] bUnit = GeometryMath.normalize(b);
+ float sp = GeometryMath.scalarProjection(disp, bUnit);
+ dx = sp * bUnit[0];
+ dy = sp * bUnit[1];
+ RectF newCrop = fixedCornerResize(crop, select, dx * scale, dy * scale);
+ Matrix m = getCropBoundDisplayMatrix();
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0)){
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
+ return;
+ }
+ if (!m0.mapRect(newCrop)){
+ if (LOGV)
+ Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
+ return;
+ }
+ setCropBounds(newCrop);
+ return;
+ } else {
+ if ((select & MOVE_LEFT) != 0) {
+ cropped.left += dx;
+ }
+ if ((select & MOVE_TOP) != 0) {
+ cropped.top += dy;
+ }
+ if ((select & MOVE_RIGHT) != 0) {
+ cropped.right += dx;
+ }
+ if ((select & MOVE_BOTTOM) != 0) {
+ cropped.bottom += dy;
+ }
+ }
+ }
+ movingEdges = select;
Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
Matrix m0 = new Matrix();
if (!m.invert(m0)) {
@@ -244,24 +519,6 @@
setCropBounds(cropped);
}
- private void fixRectAspectH(RectF cropped) {
- if (mFixAspectRatio) {
- float half = getNewWidthForHeightAspect(cropped.height(), mAspectWidth, mAspectHeight) / 2;
- float mid = (cropped.right - cropped.left) / 2;
- cropped.left = mid - half;
- cropped.right = mid + half;
- }
- }
-
- private void fixRectAspectW(RectF cropped) {
- if (mFixAspectRatio) {
- float half = getNewHeightForWidthAspect(cropped.width(), mAspectWidth, mAspectHeight) / 2;
- float mid = (cropped.bottom - cropped.top) / 2;
- cropped.top = mid - half;
- cropped.bottom = mid + half;
- }
- }
-
private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
int left = (int) centerX - indicatorSize / 2;
int top = (int) centerY - indicatorSize / 2;
@@ -273,81 +530,111 @@
protected void setActionDown(float x, float y) {
super.setActionDown(x, y);
detectMovingEdges(x, y);
- if (movingEdges == 0) {
- mPrevOffsetX = mCropOffsetX;
- mPrevOffsetY = mCropOffsetY;
- }
+ }
+
+ @Override
+ protected void setActionUp() {
+ super.setActionUp();
+ movingEdges = 0;
}
@Override
protected void setActionMove(float x, float y) {
- if (movingEdges != 0) {
+ if (movingEdges != 0){
moveEdges(x - mCurrentX, y - mCurrentY);
- } else {
- float dx = x - mTouchCenterX;
- float dy = y - mTouchCenterY;
- mCropOffsetX = dx + mPrevOffsetX;
- mCropOffsetY = dy + mPrevOffsetY;
}
super.setActionMove(x, y);
}
- @Override
- protected void gainedVisibility() {
- setCropBounds(getLocalCropBounds());
- super.gainedVisibility();
+ private void cropSetup() {
+ if (mFixAspectRatio) {
+ RectF cb = getRotatedCropBounds();
+ fixAspectRatio(cb, mAspectWidth, mAspectHeight);
+ RectF cb0 = getUnrotatedCropBounds(cb);
+ setCropBounds(cb0);
+ } else {
+ setCropBounds(getLocalCropBounds());
+ }
}
- protected RectF drawCrop(Canvas canvas, Paint p, RectF cropBounds, float scale,
- float rotation, float centerX, float centerY, float offsetX, float offsetY) {
- RectF crop = new RectF(cropBounds);
- Matrix m = new Matrix();
- m.preTranslate(offsetX, offsetY);
- m.mapRect(crop);
+ @Override
+ protected void gainedVisibility() {
+ cropSetup();
+ mFirstDraw = true;
+ }
- m.setRotate(rotation, centerX, centerY);
- if (!m.rectStaysRect()) {
- float[] corners = getCornersFromRect(crop);
- m.mapPoints(corners);
- drawClosedPath(canvas, p, corners);
- } else {
- RectF crop2 = new RectF(crop);
- m.mapRect(crop2);
- Path path = new Path();
- path.addRect(crop2, Path.Direction.CCW);
- canvas.drawPath(path, p);
- }
- return crop;
+ @Override
+ public void resetParameter() {
+ super.resetParameter();
+ cropSetup();
+ }
+
+ @Override
+ protected void lostVisibility() {
}
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
+ // TODO: move style to xml
gPaint.setAntiAlias(true);
gPaint.setFilterBitmap(true);
gPaint.setDither(true);
gPaint.setARGB(255, 255, 255, 255);
- drawTransformedBitmap(canvas, image, gPaint, false);
- float scale = getLocalScale();
+ if (mFirstDraw) {
+ cropSetup();
+ mFirstDraw = false;
+ }
float rotation = getLocalRotation();
+ drawTransformedBitmap(canvas, image, gPaint, true);
- RectF scaledCrop = drawCrop(canvas, gPaint, getLocalCropBounds(), scale,
- rotation, mCenterX, mCenterY, mXOffset,
- mYOffset);
-
- boolean notMoving = movingEdges == 0;
- if (((movingEdges & MOVE_TOP) != 0) || notMoving) {
+ gPaint.setARGB(255, 125, 255, 128);
+ gPaint.setStrokeWidth(3);
+ gPaint.setStyle(Paint.Style.STROKE);
+ drawStraighten(canvas, gPaint);
+ RectF scaledCrop = unrotatedCropBounds();
+ int decoded_moving = decoder(movingEdges, rotation);
+ canvas.save();
+ canvas.rotate(rotation, mCenterX, mCenterY);
+ boolean notMoving = decoded_moving == 0;
+ if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
}
- if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) {
+ if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) {
drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
}
- if (((movingEdges & MOVE_LEFT) != 0) || notMoving) {
+ if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) {
drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
}
- if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) {
+ if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) {
drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
}
+ canvas.restore();
}
+ private int bitCycleLeft(int x, int times, int d) {
+ int mask = (1 << d) - 1;
+ int mout = x & mask;
+ times %= d;
+ int hi = mout >> (d - times);
+ int low = (mout << times) & mask;
+ int ret = x & ~mask;
+ ret |= low;
+ ret |= hi;
+ return ret;
+ }
+
+ protected int decoder(int movingEdges, float rotation) {
+ int rot = constrainedRotation(rotation);
+ switch (rot) {
+ case 90:
+ return bitCycleLeft(movingEdges, 3, 4);
+ case 180:
+ return bitCycleLeft(movingEdges, 2, 4);
+ case 270:
+ return bitCycleLeft(movingEdges, 1, 4);
+ default:
+ return movingEdges;
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java
index 3408405..00b9aed 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java
@@ -45,6 +45,11 @@
super.setActionDown(x, y);
}
+ boolean hasRotated90(){
+ int rot = constrainedRotation(getLocalRotation());
+ return ((int) (rot / 90)) % 2 != 0;
+ }
+
@Override
protected void setActionMove(float x, float y) {
super.setActionMove(x, y);
@@ -52,6 +57,11 @@
float diffx = mTouchCenterX - x;
float diffy = mTouchCenterY - y;
float flick = getScaledMinFlick();
+ if(hasRotated90()){
+ float temp = diffx;
+ diffx = diffy;
+ diffy = temp;
+ }
if (Math.abs(diffx) >= flick) {
// flick moving left/right
FLIP flip = getLocalFlip();
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
index 98f892e..d5a7ada 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -26,12 +26,12 @@
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.imageshow.GeometryMath;
public abstract class ImageGeometry extends ImageSlave {
private boolean mVisibilityGained = false;
@@ -48,10 +48,8 @@
protected float mTouchCenterX;
protected float mTouchCenterY;
- private Matrix mLocalMatrix = null;
-
// Local geometry data
- private GeometryMetadata mLocalGeoMetadata = null;
+ private GeometryMetadata mLocalGeometry = null;
private RectF mLocalDisplayBounds = null;
protected float mXOffset = 0;
protected float mYOffset = 0;
@@ -77,9 +75,38 @@
calculateLocalScalingFactorAndOffset();
}
- private float computeScale(float width, float height) {
- float imageWidth = mLocalGeoMetadata.getPhotoBounds().width();
- float imageHeight = mLocalGeoMetadata.getPhotoBounds().height();
+ protected static float angleFor(float dx, float dy) {
+ return (float) (Math.atan2(dx, dy) * 180 / Math.PI);
+ }
+
+ protected static int snappedAngle(float angle) {
+ float remainder = angle % 90;
+ int current = (int) (angle / 90); // truncates
+ if (remainder < -45) {
+ --current;
+ } else if (remainder > 45) {
+ ++current;
+ }
+ return current * 90;
+ }
+
+ protected float getCurrentTouchAngle(){
+ if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
+ return 0;
+ }
+ float dX1 = mTouchCenterX - mCenterX;
+ float dY1 = mTouchCenterY - mCenterY;
+ float dX2 = mCurrentX - mCenterX;
+ float dY2 = mCurrentY - mCenterY;
+
+ float angleA = angleFor(dX1, dY1);
+ float angleB = angleFor(dX2, dY2);
+ return (angleB - angleA) % 360;
+ }
+
+ protected float computeScale(float width, float height) {
+ float imageWidth = mLocalGeometry.getPhotoBounds().width();
+ float imageHeight = mLocalGeometry.getPhotoBounds().height();
float zoom = width / imageWidth;
if (imageHeight > imageWidth) {
zoom = height / imageHeight;
@@ -88,9 +115,9 @@
}
private void calculateLocalScalingFactorAndOffset() {
- if (mLocalGeoMetadata == null || mLocalDisplayBounds == null)
+ if (mLocalGeometry == null || mLocalDisplayBounds == null)
return;
- RectF imageBounds = mLocalGeoMetadata.getPhotoBounds();
+ RectF imageBounds = mLocalGeometry.getPhotoBounds();
float imageWidth = imageBounds.width();
float imageHeight = imageBounds.height();
float displayWidth = mLocalDisplayBounds.width();
@@ -100,9 +127,7 @@
mCenterY = displayHeight / 2;
mYOffset = (displayHeight - imageHeight) / 2.0f;
mXOffset = (displayWidth - imageWidth) / 2.0f;
-
- float zoom = computeScale(mLocalDisplayBounds.width(), mLocalDisplayBounds.height());
- mLocalGeoMetadata.setScaleFactor(zoom);
+ updateScale();
}
@Override
@@ -118,21 +143,16 @@
// Overwrites local with master
protected void syncLocalToMasterGeometry() {
- mLocalGeoMetadata = getMaster().getGeometry();
+ mLocalGeometry = getMaster().getGeometry();
calculateLocalScalingFactorAndOffset();
- mLocalMatrix = mLocalGeoMetadata.getMatrix();
- }
-
- public Matrix getLocalMatrix() {
- return mLocalMatrix;
}
protected RectF getLocalPhotoBounds() {
- return mLocalGeoMetadata.getPhotoBounds();
+ return mLocalGeometry.getPhotoBounds();
}
protected RectF getLocalCropBounds() {
- return mLocalGeoMetadata.getCropBounds();
+ return mLocalGeometry.getPreviewCropBounds();
}
protected RectF getLocalDisplayBounds() {
@@ -140,59 +160,62 @@
}
protected float getLocalScale() {
- return mLocalGeoMetadata.getScaleFactor();
+ return mLocalGeometry.getScaleFactor();
}
protected float getLocalRotation() {
- return mLocalGeoMetadata.getRotation();
+ return mLocalGeometry.getRotation();
}
protected float getLocalStraighten() {
- return mLocalGeoMetadata.getStraightenRotation();
+ return mLocalGeometry.getStraightenRotation();
}
protected void setLocalScale(float s) {
- mLocalGeoMetadata.setScaleFactor(s);
+ mLocalGeometry.setScaleFactor(s);
}
- protected void updateMatrix() {
- RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeoMetadata.getPhotoBounds(),
+ protected void updateScale() {
+ RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeometry.getPhotoBounds(),
getLocalStraighten());
float zoom = computeScale(bounds.width(), bounds.height());
setLocalScale(zoom);
- float w = mLocalGeoMetadata.getPhotoBounds().width();
- float h = mLocalGeoMetadata.getPhotoBounds().height();
- float ratio = h / w;
- float rcenterx = 0.5f;
- float rcentery = 0.5f * ratio;
- Matrix flipper = mLocalGeoMetadata.getFlipMatrix(1.0f, ratio);
- mLocalMatrix.reset();
- mLocalMatrix.postConcat(flipper);
- mLocalMatrix.postRotate(getTotalLocalRotation(), rcenterx, rcentery);
- invalidate();
}
protected void setLocalRotation(float r) {
- mLocalGeoMetadata.setRotation(r);
- updateMatrix();
+ mLocalGeometry.setRotation(r);
+ updateScale();
+ }
+
+ /**
+ * Constrains rotation to be in [0, 90, 180, 270].
+ */
+ protected int constrainedRotation(float rotation) {
+ int r = (int) ((rotation % 360) / 90);
+ r = (r < 0) ? (r + 4) : r;
+ return r * 90;
+ }
+
+ protected Matrix getLocalGeoFlipMatrix(float width, float height) {
+ return mLocalGeometry.getFlipMatrix(width, height);
}
protected void setLocalStraighten(float r) {
- mLocalGeoMetadata.setStraightenRotation(r);
- updateMatrix();
+ mLocalGeometry.setStraightenRotation(r);
+ updateScale();
}
protected void setLocalCropBounds(RectF c) {
- mLocalGeoMetadata.setCropBounds(c);
+ mLocalGeometry.setCropBounds(c);
+ updateScale();
}
protected FLIP getLocalFlip() {
- return mLocalGeoMetadata.getFlipType();
+ return mLocalGeometry.getFlipType();
}
protected void setLocalFlip(FLIP flip) {
- mLocalGeoMetadata.setFlipType(flip);
- updateMatrix();
+ mLocalGeometry.setFlipType(flip);
}
protected float getTotalLocalRotation() {
@@ -208,64 +231,26 @@
protected static float[] getCornersFromRect(RectF r) {
// Order is:
// 0------->1
- // ^ |
- // | v
+ // ^ |
+ // | v
// 3<-------2
float[] corners = {
r.left, r.top, // 0
r.right, r.top, // 1
r.right, r.bottom,// 2
- r.left, r.bottom
- // 3
+ r.left, r.bottom // 3
};
return corners;
}
- // Returns maximal rectangular crop bound that still fits within
- // the image bound after the image has been rotated.
- protected static RectF findCropBoundForRotatedImg(RectF cropBound,
- RectF imageBound,
- float rotation,
- float centerX,
- float centerY) {
- Matrix m = new Matrix();
- float[] cropEdges = getCornersFromRect(cropBound);
- m.setRotate(rotation, centerX, centerY);
- Matrix m0 = new Matrix();
- if (!m.invert(m0))
- return null;
- m0.mapPoints(cropEdges);
- getEdgePoints(imageBound, cropEdges);
- m.mapPoints(cropEdges);
- return trapToRect(cropEdges);
- }
-
// If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
// image bound rectangle, clamps it to the edge of the rectangle.
protected static void getEdgePoints(RectF imageBound, float[] array) {
if (array.length < 2)
return;
for (int x = 0; x < array.length; x += 2) {
- array[x] = clamp(array[x], imageBound.left, imageBound.right);
- array[x + 1] = clamp(array[x + 1], imageBound.top, imageBound.bottom);
- }
- }
-
- protected static RectF trapToRect(float[] array) {
- float dx0 = array[4] - array[0];
- float dy0 = array[5] - array[1];
- float dx1 = array[6] - array[2];
- float dy1 = array[7] - array[3];
- float l0 = dx0 * dx0 + dy0 * dy0;
- float l1 = dx1 * dx1 + dy1 * dy1;
- if (l0 > l1) {
- RectF n = new RectF(array[2], array[3], array[6], array[7]);
- n.sort();
- return n;
- } else {
- RectF n = new RectF(array[0], array[1], array[4], array[5]);
- n.sort();
- return n;
+ array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
+ array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
}
}
@@ -280,26 +265,14 @@
return crop;
}
- protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
- float x1 = l1[0];
- float x2 = l2[0];
- float y1 = l1[1];
- float y2 = l2[1];
- float xdelt = x2 - x1;
- float ydelt = y2 - y1;
- if (xdelt == 0 && ydelt == 0)
- return null;
- float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt)
- / (xdelt * xdelt + ydelt * ydelt);
- float[] ret = {
- (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
- };
- return ret;
- }
-
protected static void fixAspectRatio(RectF r, float w, float h) {
float scale = Math.min(r.width() / w, r.height() / h);
- r.set(r.left, r.top, scale * w, scale * h);
+ float centX = r.centerX();
+ float centY = r.centerY();
+ float hw = scale * w / 2;
+ float hh = scale * h / 2;
+ r.set(centX - hw, centY - hh, centX + hw, centY + hh);
+
}
protected static float getNewHeightForWidthAspect(float width, float w, float h) {
@@ -310,22 +283,17 @@
return height * w / h;
}
- protected void logMasterGeo() {
- Log.v(LOGTAG, getMaster().getGeometry().toString());
- }
-
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == View.VISIBLE) {
mVisibilityGained = true;
syncLocalToMasterGeometry();
+ updateScale();
gainedVisibility();
- logMasterGeo();
} else {
if (mVisibilityGained == true && mHasDrawn == true) {
lostVisibility();
- logMasterGeo();
}
mVisibilityGained = false;
mHasDrawn = false;
@@ -334,7 +302,6 @@
protected void gainedVisibility() {
// TODO: Override this stub.
- updateMatrix();
}
protected void lostVisibility() {
@@ -356,8 +323,6 @@
case (MotionEvent.ACTION_UP):
setActionUp();
saveAndSetPreset();
- Log.v(LOGTAG, "up action");
- logMasterGeo();
break;
case (MotionEvent.ACTION_MOVE):
setActionMove(event.getX(), event.getY());
@@ -405,17 +370,12 @@
protected void saveAndSetPreset() {
ImagePreset copy = new ImagePreset(getImagePreset());
- copy.setGeometry(mLocalGeoMetadata);
+ copy.setGeometry(mLocalGeometry);
copy.setHistoryName("Geometry");
copy.setIsFx(false);
setImagePreset(copy);
}
- //
- protected static float clamp(float i, float low, float high) {
- return Math.max(Math.min(i, high), low);
- }
-
public static RectF getUntranslatedStraightenCropBounds(RectF imageRect, float straightenAngle) {
float deg = straightenAngle;
if (deg < 0) {
@@ -440,102 +400,125 @@
return new RectF(left, top, right, bottom);
}
+ protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) {
+ float scale = computeScale(getWidth(), getHeight());
+ float yoff = getHeight() / 2;
+ float xoff = getWidth() / 2;
+ float w = r.left * 2 + r.width();
+ float h = r.top * 2 + r.height();
+ return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate);
+ }
+
+ protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) {
+ canvas.save();
+ canvas.drawBitmap(bitmap, m, paint);
+ canvas.restore();
+ }
+
+ protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) {
+ float scale = computeScale(getWidth(), getHeight());
+ float yoff = getHeight() / 2;
+ float xoff = getWidth() / 2;
+ Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff);
+ drawImageBitmap(canvas, bitmap, paint, m);
+ }
+
+ protected RectF straightenBounds() {
+ RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten());
+ Matrix m = getGeoMatrix(bounds, true);
+ m.mapRect(bounds);
+ return bounds;
+ }
+
+ protected void drawStraighten(Canvas canvas, Paint paint) {
+ RectF bounds = straightenBounds();
+ canvas.save();
+ canvas.drawRect(bounds, paint);
+ canvas.restore();
+ }
+
+ protected RectF unrotatedCropBounds() {
+ RectF bounds = getLocalCropBounds();
+ RectF pbounds = getLocalPhotoBounds();
+ float scale = computeScale(getWidth(), getHeight());
+ float yoff = getHeight() / 2;
+ float xoff = getWidth() / 2;
+ Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale, xoff, yoff, 0);
+ m.mapRect(bounds);
+ return bounds;
+ }
+
+ protected RectF cropBounds() {
+ RectF bounds = getLocalCropBounds();
+ Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
+ m.mapRect(bounds);
+ return bounds;
+ }
+
+ // Fails for non-90 degree
+ protected void drawCrop(Canvas canvas, Paint paint) {
+ RectF bounds = cropBounds();
+ canvas.save();
+ canvas.drawRect(bounds, paint);
+ canvas.restore();
+ }
+
+ protected void drawCropSafe(Canvas canvas, Paint paint) {
+ Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
+ RectF crop = getLocalCropBounds();
+ if (!m.rectStaysRect()) {
+ float[] corners = getCornersFromRect(crop);
+ m.mapPoints(corners);
+ drawClosedPath(canvas, paint, corners);
+ } else {
+ m.mapRect(crop);
+ Path path = new Path();
+ path.addRect(crop, Path.Direction.CCW);
+ canvas.drawPath(path, paint);
+ }
+ }
+
+ protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
+ paint.setARGB(255, 0, 0, 0);
+ drawImageBitmap(canvas, bitmap, paint);
+ paint.setColor(Color.WHITE);
+ paint.setStyle(Style.STROKE);
+ paint.setStrokeWidth(2);
+ drawCropSafe(canvas, paint);
+ paint.setARGB(128, 0, 0, 0);
+ paint.setStyle(Paint.Style.FILL);
+ drawShadows(canvas, paint, unrotatedCropBounds());
+ }
+
+ protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
+ RectF display = new RectF(0, 0, getWidth(), getHeight());
+ drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
+ getHeight() / 2);
+ }
+
protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
float rotation, float centerX, float centerY) {
canvas.save();
canvas.rotate(rotation, centerX, centerY);
- float dWidth = outerBounds.width();
- float dHeight = outerBounds.height();
- canvas.drawRect(0, 0, dWidth, innerBounds.top, p);
- canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p);
- canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom,
+
+ float x = (outerBounds.left - outerBounds.right);
+ float y = (outerBounds.top - outerBounds.bottom);
+ float longest = (float) Math.sqrt(x * x + y * y) / 2;
+ float minX = centerX - longest;
+ float maxX = centerX + longest;
+ float minY = centerY - longest;
+ float maxY = centerY + longest;
+ canvas.drawRect(minX, minY, innerBounds.right, innerBounds.top, p);
+ canvas.drawRect(minX, innerBounds.top, innerBounds.left, maxY, p);
+ canvas.drawRect(innerBounds.left, innerBounds.bottom, maxX, maxY,
p);
- canvas.drawRect(innerBounds.right, innerBounds.top, dWidth,
+ canvas.drawRect(innerBounds.right, minY, maxX,
innerBounds.bottom, p);
canvas.rotate(-rotation, centerX, centerY);
canvas.restore();
}
- public Matrix computeBoundsMatrix(Bitmap bitmap) {
- Matrix boundsMatrix = new Matrix();
- boundsMatrix.setTranslate((getWidth() - bitmap.getWidth()) / 2.0f,
- (getHeight() - bitmap.getHeight()) / 2.0f);
- boundsMatrix.postRotate(getLocalRotation(), getWidth() / 2.0f, getHeight() / 2.0f);
- return boundsMatrix;
- }
-
- public RectF cropBounds(Bitmap bitmap) {
- Matrix boundsMatrix = computeBoundsMatrix(bitmap);
- RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- RectF transformedBounds = new RectF(bounds);
- boundsMatrix.mapRect(transformedBounds);
- return transformedBounds;
- }
-
- protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
- Matrix boundsMatrix = computeBoundsMatrix(bitmap);
- RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- RectF transformedBounds = new RectF(bounds);
- boundsMatrix.mapRect(transformedBounds);
-
- canvas.save();
- Matrix matrix = getLocalMatrix();
- canvas.translate((getWidth() - bitmap.getWidth()) / 2.0f,
- (getHeight() - bitmap.getHeight()) / 2.0f);
- paint.setARGB(255, 0, 0, 0);
- Matrix drawMatrix = new Matrix();
- float w = bitmap.getWidth();
- drawMatrix.preScale(1.0f/w, 1.0f/w);
- drawMatrix.postConcat(matrix);
- drawMatrix.postScale(w, w);
- canvas.drawBitmap(bitmap, drawMatrix, paint);
- canvas.restore();
-
- canvas.save();
- canvas.setMatrix(boundsMatrix);
- paint.setColor(Color.WHITE);
- paint.setStyle(Style.STROKE);
- paint.setStrokeWidth(2);
- canvas.drawRect(bounds, paint);
- canvas.restore();
-
- if (!clip) { // we display the rest of the bitmap grayed-out
- drawShadows(canvas, transformedBounds, new RectF(0, 0, getWidth(), getHeight()), paint);
- }
- }
-
- protected RectF getCropBoundsDisplayed() {
- return getCropBoundsDisplayed(getLocalCropBounds());
- }
-
- protected RectF getCropBoundsDisplayed(RectF bounds) {
- RectF crop = new RectF(bounds);
- Matrix m = new Matrix();
- float zoom = getLocalScale();
- m.setScale(zoom, zoom, mCenterX, mCenterY);
- m.preTranslate(mXOffset, mYOffset);
- m.mapRect(crop);
- return crop;
- }
-
- protected void drawShadows(Canvas canvas, RectF innerBounds, RectF outerBounds, Paint p) {
- float dWidth = outerBounds.width();
- float dHeight = outerBounds.height();
-
- // TODO: move style to xml
- p.setARGB(128, 0, 0, 0);
- p.setStyle(Paint.Style.FILL);
-
- canvas.drawRect(0, 0, dWidth, innerBounds.top, p);
- canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p);
- canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom,
- p);
- canvas.drawRect(innerBounds.right, innerBounds.top, dWidth,
- innerBounds.bottom, p);
- }
-
@Override
public void onDraw(Canvas canvas) {
if (getDirtyGeometryFlag()) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java
index 089e117..775aa71 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java
@@ -23,7 +23,6 @@
import android.util.AttributeSet;
public class ImageRotate extends ImageGeometry {
- private static final float MATH_PI = (float) Math.PI;
private float mBaseAngle = 0;
private float mAngle = 0;
@@ -39,35 +38,10 @@
super(context);
}
- private float angleFor(float dx, float dy) {
- return (float) (Math.atan2(dx, dy) * 180 / MATH_PI);
- }
-
- private int snappedAngle(float angle) {
- float remainder = angle % 90;
- int current = (int) (angle / 90); // truncates
- if (remainder < -45) {
- --current;
- } else if (remainder > 45) {
- ++current;
- }
- return current * 90;
- }
-
private static final Paint gPaint = new Paint();
private void computeValue() {
- if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
- return;
- }
- float dX1 = mTouchCenterX - mCenterX;
- float dY1 = mTouchCenterY - mCenterY;
- float dX2 = mCurrentX - mCenterX;
- float dY2 = mCurrentY - mCenterY;
-
- float angleA = angleFor(dX1, dY1);
- float angleB = angleFor(dX2, dY2);
- float angle = (angleB - angleA) % 360;
+ float angle = getCurrentTouchAngle();
mAngle = (mBaseAngle - angle) % 360;
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index f4a2184..5c476eb 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -57,7 +58,10 @@
private boolean mDirtyGeometry = true;
private Bitmap mBackgroundImage = null;
- // TODO: remove protected here, it should be private
+ private final boolean USE_BACKGROUND_IMAGE = false;
+ private static int mBackgroundColor = Color.RED;
+
+ // TODO: remove protected here, it should be private
protected Bitmap mForegroundImage = null;
protected Bitmap mFilteredImage = null;
@@ -88,23 +92,45 @@
private SeekBar mSeekBar = null;
private PanelController mController = null;
+ public static void setDefaultBackgroundColor(int value) {
+ mBackgroundColor = value;
+ }
+
+ public static void setTextSize(int value) {
+ mTextSize = value;
+ }
+
+ public static void setTextPadding(int value) {
+ mTextPadding = value;
+ }
+
private final Handler mHandler = new Handler();
public void select() {
if (getCurrentFilter() != null) {
int parameter = getCurrentFilter().getParameter();
- updateSeekBar(parameter);
+ int maxp = getCurrentFilter().getMaxParameter();
+ int minp = getCurrentFilter().getMinParameter();
+ updateSeekBar(parameter,minp,maxp);
}
if (mSeekBar != null) {
mSeekBar.setOnSeekBarChangeListener(this);
}
}
- public void updateSeekBar(int parameter) {
+ private int parameterToUI(int parameter,int minp,int maxp,int uimax){
+ return (uimax*(parameter-minp))/(maxp-minp);
+ }
+
+ private int uiToParameter(int ui,int minp,int maxp,int uimax){
+ return ((maxp-minp)*ui)/uimax+minp;
+ }
+ public void updateSeekBar(int parameter,int minp,int maxp) {
if (mSeekBar == null) {
return;
}
- int progress = parameter + 100;
+ int seekMax = mSeekBar.getMax();
+ int progress = parameterToUI(parameter,minp,maxp,seekMax);
mSeekBar.setProgress(progress);
if (getPanelController() != null) {
getPanelController().onNewValue(parameter);
@@ -115,8 +141,18 @@
}
+ public boolean hasModifications() {
+ if (getImagePreset() == null) {
+ return false;
+ }
+ return getImagePreset().hasModifications();
+ }
+
public void resetParameter() {
- onNewValue(0);
+ ImageFilter currentFilter = getCurrentFilter();
+ if (currentFilter!=null) {
+ onNewValue(currentFilter.getDefaultParameter());
+ }
if (USE_SLIDER_GESTURE) {
mSliderController.reset();
}
@@ -131,18 +167,22 @@
}
@Override
- public void onNewValue(int value) {
+ public void onNewValue(int parameter) {
+ int maxp = 100;
+ int minp = -100;
if (getCurrentFilter() != null) {
- getCurrentFilter().setParameter(value);
+ getCurrentFilter().setParameter(parameter);
+ maxp = getCurrentFilter().getMaxParameter();
+ minp = getCurrentFilter().getMinParameter();
}
if (getImagePreset() != null) {
mImageLoader.resetImageForPreset(getImagePreset(), this);
getImagePreset().fillImageStateAdapter(mImageStateAdapter);
}
if (getPanelController() != null) {
- getPanelController().onNewValue(value);
+ getPanelController().onNewValue(parameter);
}
- updateSeekBar(value);
+ updateSeekBar(parameter,minp,maxp);
invalidate();
}
@@ -278,7 +318,7 @@
canvas.drawRect(textRect, mPaint);
mPaint.setARGB(255, 200, 200, 200);
canvas.drawText(getImagePreset().name(), mTextPadding,
- 10 + mTextPadding, mPaint);
+ 1.5f * mTextPadding, mPaint);
}
if (showControls()) {
@@ -333,14 +373,18 @@
}
public void drawBackground(Canvas canvas) {
- if (mBackgroundImage == null) {
- mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources());
- }
- if (mBackgroundImage != null) {
- Rect s = new Rect(0, 0, mBackgroundImage.getWidth(),
- mBackgroundImage.getHeight());
- Rect d = new Rect(0, 0, getWidth(), getHeight());
- canvas.drawBitmap(mBackgroundImage, s, d, mPaint);
+ if (USE_BACKGROUND_IMAGE) {
+ if (mBackgroundImage == null) {
+ mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources());
+ }
+ if (mBackgroundImage != null) {
+ Rect s = new Rect(0, 0, mBackgroundImage.getWidth(),
+ mBackgroundImage.getHeight());
+ Rect d = new Rect(0, 0, getWidth(), getHeight());
+ canvas.drawBitmap(mBackgroundImage, s, d, mPaint);
+ }
+ } else {
+ canvas.drawColor(mBackgroundColor);
}
}
@@ -495,7 +539,14 @@
@Override
public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {
- onNewValue(progress - 100);
+ int parameter = progress;
+ if (getCurrentFilter()!=null){
+ int maxp = getCurrentFilter().getMaxParameter();
+ int minp = getCurrentFilter().getMinParameter();
+ parameter = uiToParameter(progress,minp,maxp,arg0.getMax());
+ }
+
+ onNewValue(parameter);
}
@Override
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
index d0c67f7..0cf6389 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
@@ -18,7 +18,7 @@
protected final int mSelectedBackgroundColor = Color.WHITE;
protected final int mInnerBorderColor = Color.BLACK;
protected final int mInnerBorderWidth = 2;
- protected final float mImageScaleFactor = 2.5f;
+ protected final float mImageScaleFactor = 3.5f;
public ImageSmallBorder(Context context) {
super(context);
@@ -31,15 +31,16 @@
@Override
public void onDraw(Canvas canvas) {
getFilteredImage();
- if (mIsSelected) {
- canvas.drawColor(mSelectedBackgroundColor);
- } else {
- canvas.drawColor(mBackgroundColor);
- }
+ canvas.drawColor(mBackgroundColor);
// TODO: simplify & make faster...
+ RectF border = new RectF(mMargin, 2*mMargin, getWidth() - mMargin - 1, getWidth());
+
+ if (mIsSelected) {
+ mPaint.setColor(mSelectedBackgroundColor);
+ canvas.drawRect(0, mMargin, getWidth(), getWidth() + mMargin, mPaint);
+ }
+
mPaint.setColor(mInnerBorderColor);
- RectF border = new RectF(mMargin, mMargin, getWidth() - mMargin - 1, getHeight() - mMargin);
- canvas.drawLine(0, 0, getWidth(), 0, mPaint);
mPaint.setStrokeWidth(mInnerBorderWidth);
Path path = new Path();
path.addRect(border, Path.Direction.CCW);
@@ -47,9 +48,9 @@
canvas.drawPath(path, mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.save();
- canvas.clipRect(mMargin + 1, mMargin, getWidth() - mMargin - 2, getHeight() - mMargin - 1,
+ canvas.clipRect(mMargin + 1, 2*mMargin, getWidth() - mMargin - 2, getWidth() - 1,
Region.Op.INTERSECT);
- canvas.translate(mMargin, mMargin + 1);
+ canvas.translate(mMargin + 1, 2*mMargin + 1);
canvas.scale(mImageScaleFactor, mImageScaleFactor);
Rect d = new Rect(0, 0, getWidth(), getWidth());
drawImage(canvas, mFilteredImage, d);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
index a358e0c..f4bd41c 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
@@ -23,16 +23,22 @@
private boolean mSetBorder = false;
protected final Paint mPaint = new Paint();
protected boolean mIsSelected = false;
- protected boolean mNextIsSelected = false;
- private ImageSmallFilter mPreviousImageSmallFilter = null;
// TODO: move this to xml.
- protected final int mMargin = 12;
- protected final int mTextMargin = 8;
+ protected static int mMargin = 12;
+ protected static int mTextMargin = 8;
protected final int mBackgroundColor = Color.argb(255, 30, 32, 40);
protected final int mSelectedBackgroundColor = Color.WHITE;
protected final int mTextColor = Color.WHITE;
+ public static void setMargin(int value) {
+ mMargin = value;
+ }
+
+ public static void setTextMargin(int value) {
+ mTextMargin = value;
+ }
+
public ImageSmallFilter(Context context, AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
@@ -50,28 +56,14 @@
mImagePreset.add(mImageFilter);
}
- public void setPreviousImageSmallFilter(ImageSmallFilter previous) {
- mPreviousImageSmallFilter = previous;
- }
-
@Override
public void setSelected(boolean value) {
if (mIsSelected != value) {
invalidate();
- if (mPreviousImageSmallFilter != null) {
- mPreviousImageSmallFilter.setNextSelected(value);
- }
}
mIsSelected = value;
}
- public void setNextSelected(boolean value) {
- if (mNextIsSelected != value) {
- invalidate();
- }
- mNextIsSelected = value;
- }
-
public void setBorder(boolean value) {
mSetBorder = value;
}
@@ -138,37 +130,40 @@
public void onDraw(Canvas canvas) {
getFilteredImage();
canvas.drawColor(mBackgroundColor);
- Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
- float textWidth = mPaint.measureText(getImagePreset().name());
+ float textWidth = mPaint.measureText(mImageFilter.getName());
int h = mTextSize + 2 * mTextPadding;
int x = (int) ((getWidth() - textWidth) / 2);
int y = getHeight();
if (mIsSelected) {
mPaint.setColor(mSelectedBackgroundColor);
- canvas.drawRect(0, 0, getWidth(), getWidth() + mMargin, mPaint);
+ canvas.drawRect(0, mMargin, getWidth(), getWidth() + mMargin, mPaint);
}
- if (mNextIsSelected) {
- mPaint.setColor(mSelectedBackgroundColor);
- canvas.drawRect(getWidth() - mMargin, 0, getWidth(), getWidth() + mMargin, mPaint);
- }
- drawImage(canvas, mFilteredImage, d);
+ Rect destination = new Rect(mMargin, 2*mMargin, getWidth() - mMargin, getWidth());
+ drawImage(canvas, mFilteredImage, destination);
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
- canvas.drawText(getImagePreset().name(), x, y - mTextMargin, mPaint);
+ canvas.drawText(mImageFilter.getName(), x, y - mTextMargin, mPaint);
}
- public void drawImage(Canvas canvas, Bitmap image, Rect d) {
+ public void drawImage(Canvas canvas, Bitmap image, Rect destination) {
if (image != null) {
int iw = image.getWidth();
int ih = image.getHeight();
- int iy = (int) ((ih - iw) / 2.0f);
- int ix = 0;
+ int x = 0;
+ int y = 0;
+ int size = 0;
+ Rect source = null;
if (iw > ih) {
- iy = 0;
- ix = (int) ((iw - ih) / 2.0f);
+ size = ih;
+ x = (int) ((iw - size) / 2.0f);
+ y = 0;
+ } else {
+ size = iw;
+ x = 0;
+ y = (int) ((ih - size) / 2.0f);
}
- Rect s = new Rect(ix, iy, ix + iw, iy + iw);
- canvas.drawBitmap(image, s, d, mPaint);
+ source = new Rect(x, y, x + size, y + size);
+ canvas.drawBitmap(image, source, destination, mPaint);
}
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index a94d629..26c9671 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -46,29 +46,21 @@
mBaseAngle = mAngle = getLocalStraighten();
}
+ private void setCropToStraighten(){
+ setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
+ getLocalStraighten()));
+ }
+
@Override
protected void setActionMove(float x, float y) {
super.setActionMove(x, y);
computeValue();
setLocalStraighten(mAngle);
- }
-
- private float angleFor(float dx, float dy) {
- return (float) (Math.atan2(dx, dy) * 180 / Math.PI);
+ setCropToStraighten();
}
private void computeValue() {
- if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) {
- return;
- }
- float dX1 = mTouchCenterX - mCenterX;
- float dY1 = mTouchCenterY - mCenterY;
- float dX2 = mCurrentX - mCenterX;
- float dY2 = mCurrentY - mCenterY;
-
- float angleA = angleFor(dX1, dY1);
- float angleB = angleFor(dX2, dY2);
- float angle = (angleB - angleA) % 360;
+ float angle = getCurrentTouchAngle();
mAngle = (mBaseAngle - angle) % 360;
mAngle = Math.max(MIN_STRAIGHTEN_ANGLE, mAngle);
mAngle = Math.min(MAX_STRAIGHTEN_ANGLE, mAngle);
@@ -80,8 +72,19 @@
}
@Override
+ protected void gainedVisibility(){
+ setCropToStraighten();
+ }
+
+ @Override
+ protected void setActionUp() {
+ super.setActionUp();
+ setCropToStraighten();
+ }
+
+ @Override
public void onNewValue(int value) {
- setLocalStraighten(clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE));
+ setLocalStraighten(GeometryMath.clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE));
if (getPanelController() != null) {
getPanelController().onNewValue((int) getLocalStraighten());
}
@@ -98,7 +101,7 @@
drawTransformedBitmap(canvas, image, gPaint, false);
// Draw the grid
- RectF bounds = cropBounds(image);
+ RectF bounds = straightenBounds();
Path path = new Path();
path.addRect(bounds, Path.Direction.CCW);
gPaint.setARGB(255, 255, 255, 255);
diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
index 42dbcb9..f303d4c 100644
--- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
@@ -37,7 +37,9 @@
public ImagePreset(ImagePreset source, String historyName) {
this(source);
- if (historyName!=null) setHistoryName(historyName);
+ if (historyName != null) {
+ setHistoryName(historyName);
+ }
}
public ImagePreset(ImagePreset source) {
@@ -58,6 +60,22 @@
mGeoData.set(source.mGeoData);
}
+ public boolean hasModifications() {
+ if (mImageBorder != null && !mImageBorder.isNil()) {
+ return true;
+ }
+ if (mGeoData.hasModifications()) {
+ return true;
+ }
+ for (int i = 0; i < mFilters.size(); i++) {
+ ImageFilter filter = mFilters.elementAt(i);
+ if (!filter.isNil()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setGeometry(GeometryMetadata m) {
mGeoData.set(m);
}
@@ -123,27 +141,25 @@
public void add(ImageFilter filter) {
- if (filter.getFilterType() == ImageFilter.TYPE_BORDER){
+ if (filter.getFilterType() == ImageFilter.TYPE_BORDER) {
setHistoryName("Border");
setBorder(filter);
- } else if (filter.getFilterType() == ImageFilter.TYPE_FX){
-
+ } else if (filter.getFilterType() == ImageFilter.TYPE_FX) {
boolean found = false;
for (int i = 0; i < mFilters.size(); i++) {
byte type = mFilters.get(i).getFilterType();
if (found) {
- if (type != ImageFilter.TYPE_VIGNETTE){
+ if (type != ImageFilter.TYPE_VIGNETTE) {
mFilters.remove(i);
continue;
}
}
- if (type==ImageFilter.TYPE_FX){
+ if (type == ImageFilter.TYPE_FX) {
mFilters.remove(i);
mFilters.add(i, filter);
setHistoryName(filter.getName());
found = true;
}
-
}
if (!found) {
mFilters.add(filter);
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
index 49cc33a..23f4972 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
@@ -30,6 +30,7 @@
import android.view.Gravity;
import android.widget.Toast;
+import com.android.camera.R;
import com.android.gallery3d.filtershow.presets.ImagePreset;
//import com.android.gallery3d.R;
@@ -49,9 +50,8 @@
*/
public class SaveCopyTask extends AsyncTask<ProcessedBitmap, Void, Uri> {
- public static final String DOWNLOAD = "download";
- public static final String DEFAULT_SAVE_DIRECTORY = "Download";
private static final int DEFAULT_COMPRESS_QUALITY = 95;
+ private static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
/**
* Saves the bitmap in the final destination
@@ -114,13 +114,17 @@
System.currentTimeMillis()));
}
- public static File getNewFile(Context context, Uri sourceUri) {
+ public static File getFinalSaveDirectory(Context context, Uri sourceUri) {
File saveDirectory = getSaveDirectory(context, sourceUri);
if ((saveDirectory == null) || !saveDirectory.canWrite()) {
saveDirectory = new File(Environment.getExternalStorageDirectory(),
- DOWNLOAD);
+ DEFAULT_SAVE_DIRECTORY);
}
+ return saveDirectory;
+ }
+ public static File getNewFile(Context context, Uri sourceUri) {
+ File saveDirectory = getFinalSaveDirectory(context, sourceUri);
String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(
System.currentTimeMillis()));
return new File(saveDirectory, filename + ".JPG");
diff --git a/src/com/android/gallery3d/filtershow/ui/ControlPoint.java b/src/com/android/gallery3d/filtershow/ui/ControlPoint.java
index 68b799a..0c08e76 100644
--- a/src/com/android/gallery3d/filtershow/ui/ControlPoint.java
+++ b/src/com/android/gallery3d/filtershow/ui/ControlPoint.java
@@ -2,26 +2,19 @@
package com.android.gallery3d.filtershow.ui;
public class ControlPoint implements Comparable {
+ public float x;
+ public float y;
+
public ControlPoint(float px, float py) {
x = px;
y = py;
}
- public ControlPoint multiply(float m) {
- return new ControlPoint(x * m, y * m);
+ public ControlPoint(ControlPoint point) {
+ x = point.x;
+ y = point.y;
}
- public ControlPoint add(ControlPoint v) {
- return new ControlPoint(x + v.x, y + v.y);
- }
-
- public ControlPoint sub(ControlPoint v) {
- return new ControlPoint(x - v.x, y - v.y);
- }
-
- public float x;
- public float y;
-
public ControlPoint copy() {
return new ControlPoint(x, y);
}
diff --git a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java
index 7f0b043..3d43dc7 100644
--- a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java
+++ b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java
@@ -1,16 +1,15 @@
package com.android.gallery3d.filtershow.ui;
-import com.android.gallery3d.R;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
-import android.util.Log;
import android.widget.ImageButton;
+import com.android.gallery3d.R;
+
public class ImageButtonTitle extends ImageButton {
private static final String LOGTAG = "ImageButtonTitle";
private String mText = null;
@@ -18,6 +17,18 @@
private static int mTextPadding = 20;
private static Paint gPaint = new Paint();
+ public static void setTextSize(int value) {
+ mTextSize = value;
+ }
+
+ public static void setTextPadding(int value) {
+ mTextPadding = value;
+ }
+
+ public void setText(String text) {
+ mText = text;
+ }
+
public ImageButtonTitle(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = getContext().obtainStyledAttributes(
@@ -26,6 +37,11 @@
mText = a.getString(R.styleable.ImageButtonTitle_android_text);
}
+ public String getText(){
+ return mText;
+ }
+
+ @Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mText != null) {
diff --git a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
index 660a4fa..a8445b8 100644
--- a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
+++ b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
@@ -2,17 +2,17 @@
package com.android.gallery3d.filtershow.ui;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.AsyncTask;
import android.util.AttributeSet;
-import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.View;
-import android.widget.PopupMenu;
-import android.widget.Toast;
-import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
import com.android.gallery3d.filtershow.imageshow.ImageSlave;
import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -21,15 +21,18 @@
private static final String LOGTAG = "ImageCurves";
Paint gPaint = new Paint();
- Spline mSpline = null;
+ Spline[] mSplines = new Spline[4];
Path gPathSpline = new Path();
- float[] mAppliedCurve = new float[256];
+
+ private int mCurrentCurveIndex = 0;
private boolean mDidAddPoint = false;
private boolean mDidDelete = false;
private ControlPoint mCurrentControlPoint = null;
- private boolean mUseRed = true;
- private boolean mUseGreen = true;
- private boolean mUseBlue = true;
+ private ImagePreset mLastPreset = null;
+ int[] redHistogram = new int[256];
+ int[] greenHistogram = new int[256];
+ int[] blueHistogram = new int[256];
+ Path gHistoPath = new Path();
public ImageCurves(Context context) {
super(context);
@@ -41,23 +44,16 @@
resetCurve();
}
+ public void nextChannel() {
+ mCurrentCurveIndex = ((mCurrentCurveIndex + 1) % 4);
+ invalidate();
+ }
+
@Override
public boolean showTitle() {
return false;
}
- public void setUseRed(boolean value) {
- mUseRed = value;
- }
-
- public void setUseGreen(boolean value) {
- mUseGreen = value;
- }
-
- public void setUseBlue(boolean value) {
- mUseBlue = value;
- }
-
public void reloadCurve() {
if (getMaster() != null) {
String filterName = getFilterName();
@@ -67,7 +63,12 @@
resetCurve();
return;
}
- mSpline = new Spline(filter.getSpline());
+ for (int i = 0; i < 4; i++) {
+ Spline spline = filter.getSpline(i);
+ if (spline != null) {
+ mSplines[i] = new Spline(spline);
+ }
+ }
applyNewCurve();
}
}
@@ -76,13 +77,19 @@
public void resetParameter() {
super.resetParameter();
resetCurve();
+ mLastPreset = null;
+ invalidate();
}
public void resetCurve() {
- mSpline = new Spline();
+ Spline spline = new Spline();
- mSpline.addPoint(0.0f, 1.0f);
- mSpline.addPoint(1.0f, 0.0f);
+ spline.addPoint(0.0f, 1.0f);
+ spline.addPoint(1.0f, 0.0f);
+
+ for (int i = 0; i < 4; i++) {
+ mSplines[i] = new Spline(spline);
+ }
if (getMaster() != null) {
applyNewCurve();
}
@@ -93,86 +100,48 @@
super.onDraw(canvas);
gPaint.setAntiAlias(true);
- gPaint.setFilterBitmap(true);
- gPaint.setDither(true);
- drawGrid(canvas);
- drawSpline(canvas);
-
- drawToast(canvas);
- }
-
- private void drawGrid(Canvas canvas) {
- float w = getWidth();
- float h = getHeight();
-
- // Grid
- gPaint.setARGB(128, 150, 150, 150);
- gPaint.setStrokeWidth(1);
-
- float stepH = h / 9;
- float stepW = w / 9;
-
- // central diagonal
- gPaint.setARGB(255, 100, 100, 100);
- gPaint.setStrokeWidth(2);
- canvas.drawLine(0, h, w, 0, gPaint);
-
- gPaint.setARGB(128, 200, 200, 200);
- gPaint.setStrokeWidth(4);
- stepH = h / 3;
- stepW = w / 3;
- for (int j = 1; j < 3; j++) {
- canvas.drawLine(0, j * stepH, w, j * stepH, gPaint);
- canvas.drawLine(j * stepW, 0, j * stepW, h, gPaint);
+ if (getImagePreset() != mLastPreset) {
+ new ComputeHistogramTask().execute(mFilteredImage);
+ mLastPreset = getImagePreset();
}
- }
- private void drawSpline(Canvas canvas) {
- float w = getWidth();
- float h = getHeight();
-
- gPathSpline.reset();
- for (int x = 0; x < w; x += 11) {
- float fx = x / w;
- ControlPoint drawPoint = mSpline.getPoint(fx);
- float newX = drawPoint.x * w;
- float newY = drawPoint.y * h;
- if (x == 0) {
- gPathSpline.moveTo(newX, newY);
- } else {
- gPathSpline.lineTo(newX, newY);
+ if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.RED) {
+ drawHistogram(canvas, redHistogram, Color.RED, PorterDuff.Mode.SCREEN);
+ }
+ if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.GREEN) {
+ drawHistogram(canvas, greenHistogram, Color.GREEN, PorterDuff.Mode.SCREEN);
+ }
+ if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.BLUE) {
+ drawHistogram(canvas, blueHistogram, Color.BLUE, PorterDuff.Mode.SCREEN);
+ }
+ // We only display the other channels curves when showing the RGB curve
+ if (mCurrentCurveIndex == Spline.RGB) {
+ for (int i = 0; i < 4; i++) {
+ Spline spline = mSplines[i];
+ if (i != mCurrentCurveIndex && !spline.isOriginal()) {
+ // And we only display a curve if it has more than two
+ // points
+ spline.draw(canvas, Spline.colorForCurve(i), getWidth(), getHeight(), false);
+ }
}
}
+ // ...but we always display the current curve.
+ mSplines[mCurrentCurveIndex]
+ .draw(canvas, Spline.colorForCurve(mCurrentCurveIndex), getWidth(), getHeight(),
+ true);
+ drawToast(canvas);
- gPaint.setStrokeWidth(10);
- gPaint.setStyle(Paint.Style.STROKE);
- gPaint.setARGB(255, 50, 50, 50);
- canvas.drawPath(gPathSpline, gPaint);
- gPaint.setStrokeWidth(5);
- gPaint.setARGB(255, 150, 150, 150);
- canvas.drawPath(gPathSpline, gPaint);
-
- gPaint.setARGB(255, 150, 150, 150);
- for (int j = 1; j < mSpline.getNbPoints() - 1; j++) {
- ControlPoint point = mSpline.getPoint(j);
- gPaint.setStrokeWidth(10);
- gPaint.setARGB(255, 50, 50, 100);
- canvas.drawCircle(point.x * w, point.y * h, 30, gPaint);
- gPaint.setStrokeWidth(5);
- gPaint.setARGB(255, 150, 150, 200);
- canvas.drawCircle(point.x * w, point.y * h, 30, gPaint);
- }
}
private int pickControlPoint(float x, float y) {
int pick = 0;
- float px = mSpline.getPoint(0).x;
- float py = mSpline.getPoint(0).y;
+ float px = mSplines[mCurrentCurveIndex].getPoint(0).x;
+ float py = mSplines[mCurrentCurveIndex].getPoint(0).y;
double delta = Math.sqrt((px - x) * (px - x) + (py - y) * (py - y));
- for (int i = 1; i < mSpline.getNbPoints(); i++) {
- px = mSpline.getPoint(i).x;
- py = mSpline.getPoint(i).y;
+ for (int i = 1; i < mSplines[mCurrentCurveIndex].getNbPoints(); i++) {
+ px = mSplines[mCurrentCurveIndex].getPoint(i).x;
+ py = mSplines[mCurrentCurveIndex].getPoint(i).y;
double currentDelta = Math.sqrt((px - x) * (px - x) + (py - y)
* (py - y));
if (currentDelta < delta) {
@@ -182,77 +151,35 @@
}
if (!mDidAddPoint && (delta * getWidth() > 100)
- && (mSpline.getNbPoints() < 10)) {
+ && (mSplines[mCurrentCurveIndex].getNbPoints() < 10)) {
return -1;
}
- return pick;// mSpline.getPoint(pick);
- }
-
- public void showPopupMenu(View v) {
- // TODO: sort out the popup menu UI for curves
- final Context context = v.getContext();
- PopupMenu popupMenu = new PopupMenu(v.getContext(), v);
- popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_curves,
- popupMenu.getMenu());
-
- popupMenu
- .setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
-
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- Toast.makeText(context, item.toString(),
- Toast.LENGTH_LONG).show();
- return true;
- }
- });
-
- popupMenu.show();
+ return pick;
}
private String getFilterName() {
- String filterName = "Curves";
- if (mUseRed && !mUseGreen && !mUseBlue) {
- filterName = "CurvesRed";
- } else if (!mUseRed && mUseGreen && !mUseBlue) {
- filterName = "CurvesGreen";
- } else if (!mUseRed && !mUseGreen && mUseBlue) {
- filterName = "CurvesBlue";
- }
- return filterName;
+ return "Curves";
}
@Override
public synchronized boolean onTouchEvent(MotionEvent e) {
float posX = e.getX() / getWidth();
- float posY = e.getY() / getHeight();
-
- /*
- * if (true) { showPopupMenu(this); return true; }
- */
-
- // ControlPoint point = null;
-
- // Log.v(LOGTAG, "onTouchEvent - " + e + " action masked : " +
- // e.getActionMasked());
+ float posY = e.getY();
+ float margin = Spline.curveHandleSize() / 2;
+ if (posY < margin) {
+ posY = margin;
+ }
+ if (posY > getHeight() - margin) {
+ posY = getHeight() - margin;
+ }
+ posY = (posY - margin) / (getHeight() - 2 * margin);
if (e.getActionMasked() == MotionEvent.ACTION_UP) {
applyNewCurve();
- // Log.v(LOGTAG, "ACTION UP, mCurrentControlPoint set to null!");
mCurrentControlPoint = null;
- String name = null;
- if (mUseRed && mUseGreen && mUseBlue) {
- name = "Curves (RGB)";
- } else if (mUseRed) {
- name = "Curves (Red)";
- } else if (mUseGreen) {
- name = "Curves (Green)";
- } else if (mUseBlue) {
- name = "Curves (Blue)";
- }
-
-
- ImagePreset copy = new ImagePreset(getImagePreset(),name);
+ String name = "Curves";
+ ImagePreset copy = new ImagePreset(getImagePreset(), name);
copy.setIsFx(false);
mImageLoader.getHistory().insert(copy, 0);
@@ -268,103 +195,31 @@
if (mDidDelete) {
return true;
}
- // Log.v(LOGTAG, "ACTION DOWN, mCurrentControlPoint is " +
- // mCurrentControlPoint);
int pick = pickControlPoint(posX, posY);
- // Log.v(LOGTAG, "ACTION DOWN, pick is " + pick);
if (mCurrentControlPoint == null) {
if (pick == -1) {
mCurrentControlPoint = new ControlPoint(posX, posY);
- mSpline.addPoint(mCurrentControlPoint);
+ mSplines[mCurrentCurveIndex].addPoint(mCurrentControlPoint);
mDidAddPoint = true;
- // Log.v(LOGTAG, "ACTION DOWN - 2, added a new control point! "
- // + mCurrentControlPoint);
-
} else {
- mCurrentControlPoint = mSpline.getPoint(pick);
- // Log.v(LOGTAG, "ACTION DOWN - 2, picking up control point " +
- // mCurrentControlPoint + " at pick " + pick);
+ mCurrentControlPoint = mSplines[mCurrentCurveIndex].getPoint(pick);
}
}
- // Log.v(LOGTAG, "ACTION DOWN - 3, pick is " + pick);
- if (!((mCurrentControlPoint.x == 0 && mCurrentControlPoint.y == 1) || (mCurrentControlPoint.x == 1 && mCurrentControlPoint.y == 0))) {
- if (mSpline.isPointContained(posX, pick)) {
- mCurrentControlPoint.x = posX;
- mCurrentControlPoint.y = posY;
- // Log.v(LOGTAG, "ACTION DOWN - 4, move control point " +
- // mCurrentControlPoint);
- } else if (pick != -1) {
- // Log.v(LOGTAG, "ACTION DOWN - 4, delete pick " + pick);
- mSpline.deletePoint(pick);
- mDidDelete = true;
- }
+ if (mSplines[mCurrentCurveIndex].isPointContained(posX, pick)) {
+ mCurrentControlPoint.x = posX;
+ mCurrentControlPoint.y = posY;
+ } else if (pick != -1) {
+ mSplines[mCurrentCurveIndex].deletePoint(pick);
+ mDidDelete = true;
}
- // Log.v(LOGTAG, "ACTION DOWN - 5, DONE");
applyNewCurve();
invalidate();
return true;
}
public synchronized void applyNewCurve() {
- ControlPoint[] points = new ControlPoint[256];
- for (int i = 0; i < 256; i++) {
- float v = i / 255.0f;
- ControlPoint p = mSpline.getPoint(v);
- points[i] = p;
- }
- for (int i = 0; i < 256; i++) {
- mAppliedCurve[i] = -1;
- }
- for (int i = 0; i < 256; i++) {
- int index = (int) (points[i].x * 255);
- if (index >= 0 && index <= 255) {
- float v = 1.0f - points[i].y;
- if (v < 0) {
- v = 0;
- }
- if (v > 1.0f) {
- v = 1.0f;
- }
- mAppliedCurve[index] = v;
- }
- }
- float prev = 0;
- for (int i = 0; i < 256; i++) {
- if (mAppliedCurve[i] == -1) {
- // need to interpolate...
- int j = i + 1;
- if (j > 255) {
- j = 255;
- }
- for (; j < 256; j++) {
- if (mAppliedCurve[j] != -1) {
- break;
- }
- }
- if (j > 255) {
- j = 255;
- }
- // interpolate linearly between i and j - 1
- float start = prev;
- float end = mAppliedCurve[j];
- float delta = (end - start) / (j - i + 1);
- for (int k = i; k < j; k++) {
- start = start + delta;
- mAppliedCurve[k] = start;
- }
- i = j;
- }
- prev = mAppliedCurve[i];
- }
- for (int i = 0; i < 256; i++) {
- mAppliedCurve[i] = mAppliedCurve[i] * 255;
- }
- float[] appliedCurve = new float[256];
- for (int i = 0; i < 256; i++) {
- appliedCurve[i] = mAppliedCurve[i];
- }
// update image
if (getImagePreset() != null) {
String filterName = getFilterName();
@@ -379,15 +234,92 @@
}
if (filter != null) {
- filter.setSpline(new Spline(mSpline));
- filter.setCurve(appliedCurve);
- filter.setUseRed(mUseRed);
- filter.setUseGreen(mUseGreen);
- filter.setUseBlue(mUseBlue);
+ for (int i = 0; i < 4; i++) {
+ filter.setSpline(new Spline(mSplines[i]), i);
+ }
}
mImageLoader.resetImageForPreset(getImagePreset(), this);
invalidate();
}
}
+ class ComputeHistogramTask extends AsyncTask<Bitmap, Void, int[]> {
+ @Override
+ protected int[] doInBackground(Bitmap... params) {
+ int[] histo = new int[256 * 3];
+ Bitmap bitmap = params[0];
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ int[] pixels = new int[w * h];
+ bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ int index = j * w + i;
+ int r = Color.red(pixels[index]);
+ int g = Color.green(pixels[index]);
+ int b = Color.blue(pixels[index]);
+ histo[r]++;
+ histo[256 + g]++;
+ histo[512 + b]++;
+ }
+ }
+ return histo;
+ }
+
+ @Override
+ protected void onPostExecute(int[] result) {
+ System.arraycopy(result, 0, redHistogram, 0, 256);
+ System.arraycopy(result, 256, greenHistogram, 0, 256);
+ System.arraycopy(result, 512, blueHistogram, 0, 256);
+ invalidate();
+ }
+ }
+
+ private void drawHistogram(Canvas canvas, int[] histogram, int color, PorterDuff.Mode mode) {
+ int max = 0;
+ for (int i = 0; i < histogram.length; i++) {
+ if (histogram[i] > max) {
+ max = histogram[i];
+ }
+ }
+ float w = getWidth();
+ float h = getHeight();
+ float wl = w / histogram.length;
+ float wh = (0.3f * h) / max;
+ Paint paint = new Paint();
+ paint.setARGB(100, 255, 255, 255);
+ paint.setStrokeWidth((int) Math.ceil(wl));
+
+ Paint paint2 = new Paint();
+ paint2.setColor(color);
+ paint2.setStrokeWidth(6);
+ paint2.setXfermode(new PorterDuffXfermode(mode));
+ gHistoPath.reset();
+ gHistoPath.moveTo(0, h);
+ boolean firstPointEncountered = false;
+ float prev = 0;
+ float last = 0;
+ for (int i = 0; i < histogram.length; i++) {
+ float x = i * wl;
+ float l = histogram[i] * wh;
+ if (l != 0) {
+ float v = h - (l + prev) / 2.0f;
+ if (!firstPointEncountered) {
+ gHistoPath.lineTo(x, h);
+ firstPointEncountered = true;
+ }
+ gHistoPath.lineTo(x, v);
+ prev = l;
+ last = x;
+ }
+ }
+ gHistoPath.lineTo(last, h);
+ gHistoPath.lineTo(w, h);
+ gHistoPath.close();
+ canvas.drawPath(gHistoPath, paint2);
+ paint2.setStrokeWidth(2);
+ paint2.setStyle(Paint.Style.STROKE);
+ paint2.setARGB(255, 200, 200, 200);
+ canvas.drawPath(gHistoPath, paint2);
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/ui/Spline.java b/src/com/android/gallery3d/filtershow/ui/Spline.java
index a272d28..b5c7974 100644
--- a/src/com/android/gallery3d/filtershow/ui/Spline.java
+++ b/src/com/android/gallery3d/filtershow/ui/Spline.java
@@ -1,10 +1,28 @@
package com.android.gallery3d.filtershow.ui;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+
import java.util.Collections;
import java.util.Vector;
public class Spline {
+ private final Vector<ControlPoint> mPoints;
+ private static Drawable mCurveHandle;
+ private static int mCurveHandleSize;
+ private static int mCurveWidth;
+
+ public static final int RGB = 0;
+ public static final int RED = 1;
+ public static final int GREEN = 2;
+ public static final int BLUE = 3;
+
+ private final Paint gPaint = new Paint();
+
public Spline() {
mPoints = new Vector<ControlPoint>();
}
@@ -13,28 +31,276 @@
mPoints = new Vector<ControlPoint>();
for (int i = 0; i < spline.mPoints.size(); i++) {
ControlPoint p = spline.mPoints.elementAt(i);
- mPoints.add(p);
+ mPoints.add(new ControlPoint(p));
}
Collections.sort(mPoints);
- delta_t = 1.0f / mPoints.size();
}
- public ControlPoint interpolate(float t, ControlPoint p1,
- ControlPoint p2, ControlPoint p3, ControlPoint p4) {
+ public static void setCurveHandle(Drawable drawable, int size) {
+ mCurveHandle = drawable;
+ mCurveHandleSize = size;
+ }
- float t3 = t * t * t;
- float t2 = t * t;
- float b1 = 0.5f * (-t3 + 2 * t2 - t);
- float b2 = 0.5f * (3 * t3 - 5 * t2 + 2);
- float b3 = 0.5f * (-3 * t3 + 4 * t2 + t);
- float b4 = 0.5f * (t3 - t2);
+ public static void setCurveWidth(int width) {
+ mCurveWidth = width;
+ }
- ControlPoint b1p1 = p1.multiply(b1);
- ControlPoint b2p2 = p2.multiply(b2);
- ControlPoint b3p3 = p3.multiply(b3);
- ControlPoint b4p4 = p4.multiply(b4);
+ public static int curveHandleSize() {
+ return mCurveHandleSize;
+ }
- return b1p1.add(b2p2.add(b3p3.add(b4p4)));
+ public static int colorForCurve(int curveIndex) {
+ switch (curveIndex) {
+ case Spline.RED:
+ return Color.RED;
+ case GREEN:
+ return Color.GREEN;
+ case BLUE:
+ return Color.BLUE;
+ }
+ return Color.WHITE;
+ }
+
+ public boolean isOriginal() {
+ if (this.getNbPoints() > 2) {
+ return false;
+ }
+ if (mPoints.elementAt(0).x != 0 || mPoints.elementAt(0).y != 1) {
+ return false;
+ }
+ if (mPoints.elementAt(1).x != 1 || mPoints.elementAt(1).y != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private void drawHandles(Canvas canvas, Drawable indicator, float centerX, float centerY) {
+ int left = (int) centerX - mCurveHandleSize / 2;
+ int top = (int) centerY - mCurveHandleSize / 2;
+ indicator.setBounds(left, top, left + mCurveHandleSize, top + mCurveHandleSize);
+ indicator.draw(canvas);
+ }
+
+ public float[] getAppliedCurve() {
+ float[] curve = new float[256];
+ ControlPoint[] points = new ControlPoint[mPoints.size()];
+ for (int i = 0; i < mPoints.size(); i++) {
+ ControlPoint p = mPoints.get(i);
+ points[i] = new ControlPoint(p.x, p.y);
+ }
+ double[] derivatives = solveSystem(points);
+ int start = 0;
+ if (points[0].x != 0) {
+ start = (int) (points[0].x * 256);
+ }
+ for (int i = 0; i < start; i++) {
+ curve[i] = 1.0f - points[0].y;
+ }
+ for (int i = start; i < 256; i++) {
+ ControlPoint cur = null;
+ ControlPoint next = null;
+ double x = i / 256.0;
+ int pivot = 0;
+ for (int j = 0; j < points.length - 1; j++) {
+ if (x >= points[j].x && x <= points[j + 1].x) {
+ pivot = j;
+ }
+ }
+ cur = points[pivot];
+ next = points[pivot + 1];
+ if (x <= next.x) {
+ double x1 = cur.x;
+ double x2 = next.x;
+ double y1 = cur.y;
+ double y2 = next.y;
+
+ // Use the second derivatives to apply the cubic spline
+ // equation:
+ double delta = (x2 - x1);
+ double delta2 = delta * delta;
+ double b = (x - x1) / delta;
+ double a = 1 - b;
+ double ta = a * y1;
+ double tb = b * y2;
+ double tc = (a * a * a - a) * derivatives[pivot];
+ double td = (b * b * b - b) * derivatives[pivot + 1];
+ double y = ta + tb + (delta2 / 6) * (tc + td);
+ if (y > 1.0f) {
+ y = 1.0f;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ curve[i] = (float) (1.0f - y);
+ } else {
+ curve[i] = 1.0f - next.y;
+ }
+ }
+ return curve;
+ }
+
+ private void drawGrid(Canvas canvas, float w, float h) {
+ // Grid
+ gPaint.setARGB(128, 150, 150, 150);
+ gPaint.setStrokeWidth(1);
+
+ float stepH = h / 9;
+ float stepW = w / 9;
+
+ // central diagonal
+ gPaint.setARGB(255, 100, 100, 100);
+ gPaint.setStrokeWidth(2);
+ canvas.drawLine(0, h, w, 0, gPaint);
+
+ gPaint.setARGB(128, 200, 200, 200);
+ gPaint.setStrokeWidth(4);
+ stepH = h / 3;
+ stepW = w / 3;
+ for (int j = 1; j < 3; j++) {
+ canvas.drawLine(0, j * stepH, w, j * stepH, gPaint);
+ canvas.drawLine(j * stepW, 0, j * stepW, h, gPaint);
+ }
+ canvas.drawLine(0, 0, 0, h, gPaint);
+ canvas.drawLine(w, 0, w, h, gPaint);
+ canvas.drawLine(0, 0, w, 0, gPaint);
+ canvas.drawLine(0, h, w, h, gPaint);
+ }
+
+ public void draw(Canvas canvas, int color, int canvasWidth, int canvasHeight,
+ boolean showHandles) {
+ float w = canvasWidth;
+ float h = canvasHeight - mCurveHandleSize;
+ float dx = 0;
+ float dy = mCurveHandleSize / 2;
+
+ // The cubic spline equation is (from numerical recipes in C):
+ // y = a(y_i) + b(y_i+1) + c(y"_i) + d(y"_i+1)
+ //
+ // with c(y"_i) and d(y"_i+1):
+ // c(y"_i) = 1/6 (a^3 - a) delta^2 (y"_i)
+ // d(y"_i_+1) = 1/6 (b^3 - b) delta^2 (y"_i+1)
+ //
+ // and delta:
+ // delta = x_i+1 - x_i
+ //
+ // To find the second derivatives y", we can rearrange the equation as:
+ // A(y"_i-1) + B(y"_i) + C(y"_i+1) = D
+ //
+ // With the coefficients A, B, C, D:
+ // A = 1/6 (x_i - x_i-1)
+ // B = 1/3 (x_i+1 - x_i-1)
+ // C = 1/6 (x_i+1 - x_i)
+ // D = (y_i+1 - y_i)/(x_i+1 - x_i) - (y_i - y_i-1)/(x_i - x_i-1)
+ //
+ // We can now easily solve the equation to find the second derivatives:
+ ControlPoint[] points = new ControlPoint[mPoints.size()];
+ for (int i = 0; i < mPoints.size(); i++) {
+ ControlPoint p = mPoints.get(i);
+ points[i] = new ControlPoint(p.x * w, p.y * h);
+ }
+ double[] derivatives = solveSystem(points);
+
+ Path path = new Path();
+ path.moveTo(0, points[0].y);
+ for (int i = 0; i < points.length - 1; i++) {
+ double x1 = points[i].x;
+ double x2 = points[i + 1].x;
+ double y1 = points[i].y;
+ double y2 = points[i + 1].y;
+
+ for (double x = x1; x < x2; x += 20) {
+ // Use the second derivatives to apply the cubic spline
+ // equation:
+ double delta = (x2 - x1);
+ double delta2 = delta * delta;
+ double b = (x - x1) / delta;
+ double a = 1 - b;
+ double ta = a * y1;
+ double tb = b * y2;
+ double tc = (a * a * a - a) * derivatives[i];
+ double td = (b * b * b - b) * derivatives[i + 1];
+ double y = ta + tb + (delta2 / 6) * (tc + td);
+ if (y > h) {
+ y = h;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ path.lineTo((float) x, (float) y);
+ }
+ }
+ canvas.save();
+ canvas.translate(dx, dy);
+ drawGrid(canvas, w, h);
+ ControlPoint lastPoint = points[points.length - 1];
+ path.lineTo(lastPoint.x, lastPoint.y);
+ path.lineTo(w, lastPoint.y);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ paint.setDither(true);
+ paint.setStyle(Paint.Style.STROKE);
+ int curveWidth = mCurveWidth;
+ if (showHandles) {
+ curveWidth *= 1.5;
+ }
+ paint.setStrokeWidth(curveWidth + 2);
+ paint.setColor(Color.BLACK);
+ canvas.drawPath(path, paint);
+ paint.setStrokeWidth(curveWidth);
+ paint.setColor(color);
+ canvas.drawPath(path, paint);
+ if (showHandles) {
+ for (int i = 0; i < points.length; i++) {
+ float x = points[i].x;
+ float y = points[i].y;
+ drawHandles(canvas, mCurveHandle, x, y);
+ }
+ }
+ canvas.restore();
+ }
+
+ double[] solveSystem(ControlPoint[] points) {
+ int n = points.length;
+ double[][] system = new double[n][3];
+ double[] result = new double[n]; // d
+ double[] solution = new double[n]; // returned coefficients
+ system[0][1] = 1;
+ system[n - 1][1] = 1;
+ double d6 = 1.0 / 6.0;
+ double d3 = 1.0 / 3.0;
+
+ // let's create a tridiagonal matrix representing the
+ // system, and apply the TDMA algorithm to solve it
+ // (see http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm)
+ for (int i = 1; i < n - 1; i++) {
+ double deltaPrevX = points[i].x - points[i - 1].x;
+ double deltaX = points[i + 1].x - points[i - 1].x;
+ double deltaNextX = points[i + 1].x - points[i].x;
+ double deltaNextY = points[i + 1].y - points[i].y;
+ double deltaPrevY = points[i].y - points[i - 1].y;
+ system[i][0] = d6 * deltaPrevX; // a_i
+ system[i][1] = d3 * deltaX; // b_i
+ system[i][2] = d6 * deltaNextX; // c_i
+ result[i] = (deltaNextY / deltaNextX) - (deltaPrevY / deltaPrevX); // d_i
+ }
+
+ // Forward sweep
+ for (int i = 1; i < n; i++) {
+ // m = a_i/b_i-1
+ double m = system[i][0] / system[i - 1][1];
+ // b_i = b_i - m(c_i-1)
+ system[i][1] = system[i][1] - m * system[i - 1][2];
+ // d_i = d_i - m(d_i-1)
+ result[i] = result[i] - m * result[i - 1];
+ }
+
+ // Back substitution
+ solution[n - 1] = result[n - 1] / system[n - 1][1];
+ for (int i = n - 2; i >= 0; --i) {
+ solution[i] = (result[i] - system[i][2] * solution[i + 1]) / system[i][1];
+ }
+ return solution;
}
public void addPoint(float x, float y) {
@@ -44,42 +310,11 @@
public void addPoint(ControlPoint v) {
mPoints.add(v);
Collections.sort(mPoints);
- delta_t = 1.0f / mPoints.size();
}
- public ControlPoint getPoint(float t) {
- int p = (int) (t / delta_t);
- int p0 = p - 1;
- int max = mPoints.size() - 1;
-
- if (p0 < 0) {
- p0 = 0;
- } else if (p0 >= max) {
- p0 = max;
- }
- int p1 = p;
- if (p1 < 0) {
- p1 = 0;
- } else if (p1 >= max) {
- p1 = max;
- }
- int p2 = p + 1;
- if (p2 < 0) {
- p2 = 0;
- } else if (p2 >= max) {
- p2 = max;
- }
- int p3 = p + 2;
- if (p3 < 0) {
- p3 = 0;
- } else if (p3 >= max) {
- p3 = max;
- }
- float lt = (t - delta_t * (float) p) / delta_t;
- return interpolate(lt, mPoints.elementAt(p0),
- mPoints.elementAt(p1), mPoints.elementAt(p2),
- mPoints.elementAt(p3));
-
+ public void deletePoint(int n) {
+ mPoints.remove(n);
+ Collections.sort(mPoints);
}
public int getNbPoints() {
@@ -106,15 +341,6 @@
return true;
}
- public void deletePoint(int n) {
- mPoints.remove(n);
- Collections.sort(mPoints);
- delta_t = 1.0f / (mPoints.size() - 1f);
- }
-
- private Vector<ControlPoint> mPoints;
- private float delta_t;
-
public Spline copy() {
Spline spline = new Spline();
for (int i = 0; i < mPoints.size(); i++) {
@@ -123,4 +349,5 @@
}
return spline;
}
+
}
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index 8cebddd..3384b88 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -280,6 +280,7 @@
intent.setType(GalleryUtils.MIME_TYPE_PANORAMA360);
intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
}
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return intent;
@@ -315,6 +316,7 @@
intent.setAction(Intent.ACTION_SEND).setType(mimeType);
intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
}
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
setNfcBeamPushUris(uris.toArray(new Uri[uris.size()]));
} else {
setNfcBeamPushUris(null);
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
index d5337f0..9b3f29f 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
@@ -71,7 +71,7 @@
mWaitLoadingTexture = new ColorTexture(mPlaceholderColor);
mWaitLoadingTexture.setSize(1, 1);
mCameraOverlay = new ResourceTexture(activity,
- R.drawable.frame_overlay_gallery_camera);
+ R.drawable.ic_cameraalbum_overlay);
}
public void setPressedIndex(int index) {
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 7b8e30d..99cf057 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -42,8 +42,8 @@
protected int mWidth = UNSPECIFIED;
protected int mHeight = UNSPECIFIED;
- private int mTextureWidth;
- private int mTextureHeight;
+ protected int mTextureWidth;
+ protected int mTextureHeight;
private boolean mHasBorder;
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index bf31bcb..741eefb 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -19,209 +19,40 @@
import android.graphics.Bitmap;
import android.graphics.RectF;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-import com.android.gallery3d.data.MediaItem;
-
-// This is a ScreenNail wraps a Bitmap. There are some extra functions:
-//
-// - If we need to draw before the bitmap is available, we draw a rectange of
-// placeholder color (gray).
-//
-// - When the the bitmap is available, and we have drawn the placeholder color
-// before, we will do a fade-in animation.
public class BitmapScreenNail implements ScreenNail {
- @SuppressWarnings("unused")
- private static final String TAG = "BitmapScreenNail";
-
- // The duration of the fading animation in milliseconds
- private static final int DURATION = 180;
-
- private static int sMaxSide = 640;
-
- // These are special values for mAnimationStartTime
- private static final long ANIMATION_NOT_NEEDED = -1;
- private static final long ANIMATION_NEEDED = -2;
- private static final long ANIMATION_DONE = -3;
-
- private int mWidth;
- private int mHeight;
- private Bitmap mBitmap;
- private BitmapTexture mTexture;
- private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+ private final BitmapTexture mBitmapTexture;
public BitmapScreenNail(Bitmap bitmap) {
- mWidth = bitmap.getWidth();
- mHeight = bitmap.getHeight();
- mBitmap = bitmap;
- // We create mTexture lazily, so we don't incur the cost if we don't
- // actually need it.
- }
-
- public BitmapScreenNail(int width, int height) {
- setSize(width, height);
- }
-
- // This gets overridden by bitmap_screennail_placeholder
- // in GalleryUtils.initialize
- private static int mPlaceholderColor = 0xFF222222;
- private static boolean mDrawPlaceholder = true;
-
- public static void setPlaceholderColor(int color) {
- mPlaceholderColor = color;
- }
-
- private void setSize(int width, int height) {
- if (width == 0 || height == 0) {
- width = sMaxSide;
- height = sMaxSide * 3 / 4;
- }
- float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
- mWidth = Math.round(scale * width);
- mHeight = Math.round(scale * height);
- }
-
- private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
- if (pool == null || bitmap == null) return;
- pool.recycle(bitmap);
- }
-
- // Combines the two ScreenNails.
- // Returns the used one and recycle the unused one.
- public ScreenNail combine(ScreenNail other) {
- if (other == null) {
- return this;
- }
-
- if (!(other instanceof BitmapScreenNail)) {
- recycle();
- return other;
- }
-
- // Now both are BitmapScreenNail. Move over the information about width,
- // height, and Bitmap, then recycle the other.
- BitmapScreenNail newer = (BitmapScreenNail) other;
- mWidth = newer.mWidth;
- mHeight = newer.mHeight;
- if (newer.mBitmap != null) {
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = newer.mBitmap;
- newer.mBitmap = null;
-
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- }
-
- newer.recycle();
- return this;
- }
-
- public void updatePlaceholderSize(int width, int height) {
- if (mBitmap != null) return;
- if (width == 0 || height == 0) return;
- setSize(width, height);
+ mBitmapTexture = new BitmapTexture(bitmap);
}
@Override
public int getWidth() {
- return mWidth;
+ return mBitmapTexture.getWidth();
}
@Override
public int getHeight() {
- return mHeight;
- }
-
- @Override
- public void noDraw() {
- }
-
- @Override
- public void recycle() {
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = null;
- }
-
- public static void disableDrawPlaceholder() {
- mDrawPlaceholder = false;
- }
-
- public static void enableDrawPlaceholder() {
- mDrawPlaceholder = true;
+ return mBitmapTexture.getHeight();
}
@Override
public void draw(GLCanvas canvas, int x, int y, int width, int height) {
- if (mBitmap == null) {
- if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
- mAnimationStartTime = ANIMATION_NEEDED;
- }
- if(mDrawPlaceholder) {
- canvas.fillRect(x, y, width, height, mPlaceholderColor);
- }
- return;
- }
+ mBitmapTexture.draw(canvas, x, y, width, height);
+ }
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
+ @Override
+ public void noDraw() {
+ // do nothing
+ }
- if (mAnimationStartTime == ANIMATION_NEEDED) {
- mAnimationStartTime = now();
- }
-
- if (isAnimating()) {
- canvas.drawMixed(mTexture, mPlaceholderColor, getRatio(), x, y,
- width, height);
- } else {
- mTexture.draw(canvas, x, y, width, height);
- }
+ @Override
+ public void recycle() {
+ mBitmapTexture.recycle();
}
@Override
public void draw(GLCanvas canvas, RectF source, RectF dest) {
- if (mBitmap == null) {
- canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
- mPlaceholderColor);
- return;
- }
-
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- canvas.drawTexture(mTexture, source, dest);
- }
-
- public boolean isAnimating() {
- if (mAnimationStartTime < 0) return false;
- if (now() - mAnimationStartTime >= DURATION) {
- mAnimationStartTime = ANIMATION_DONE;
- return false;
- }
- return true;
- }
-
- private static long now() {
- return AnimationTime.get();
- }
-
- private float getRatio() {
- float r = (float)(now() - mAnimationStartTime) / DURATION;
- return Utils.clamp(1.0f - r, 0.0f, 1.0f);
- }
-
- public boolean isShowingPlaceholder() {
- return (mBitmap == null) || isAnimating();
- }
-
- public static void setMaxSide(int size) {
- sMaxSide = size;
+ canvas.drawTexture(mBitmapTexture, source, dest);
}
}
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index e3a32ef..6f8baef 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -99,6 +99,13 @@
public void drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int w, int h);
+ // Draw a region of a texture and a specified color to the specified
+ // rectangle. The actual color used is from * (1 - ratio) + to * ratio.
+ // The region of the texture is defined by parameter "src". The target
+ // rectangle is specified by parameter "target".
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target);
+
// Gets the underlying GL instance. This is used only when direct access to
// GL is needed.
public GL11 getGLInstance();
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index d83daf3..45903b3 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -415,7 +415,7 @@
// This function changes the source coordinate to the texture coordinates.
// It also clips the source and target coordinates if it is beyond the
// bound of the texture.
- private void convertCoordinate(RectF source, RectF target,
+ private static void convertCoordinate(RectF source, RectF target,
BasicTexture texture) {
int width = texture.getWidth();
@@ -465,6 +465,82 @@
color[3] = alpha;
}
+ private void setMixedColor(int toColor, float ratio, float alpha) {
+ //
+ // The formula we want:
+ // alpha * ((1 - ratio) * from + ratio * to)
+ //
+ // The formula that GL supports is in the form of:
+ // combo * from + (1 - combo) * to * scale
+ //
+ // So, we have combo = alpha * (1 - ratio)
+ // and scale = alpha * ratio / (1 - combo)
+ //
+ float combo = alpha * (1 - ratio);
+ float scale = alpha * ratio / (1 - combo);
+
+ // Specify the interpolation factor via the alpha component of
+ // GL_TEXTURE_ENV_COLORs.
+ // RGB component are get from toColor and will used as SRC1
+ float colorScale = scale * (toColor >>> 24) / (0xff * 0xff);
+ setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
+ ((toColor >>> 8) & 0xff) * colorScale,
+ (toColor & 0xff) * colorScale, combo);
+ GL11 gl = mGL;
+ gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
+
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
+
+ // Wire up the interpolation factor for RGB.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
+
+ // Wire up the interpolation factor for alpha.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+
+ }
+
+ @Override
+ public void drawMixed(BasicTexture from, int toColor, float ratio,
+ RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) return;
+
+ if (ratio <= 0.01f) {
+ drawTexture(from, source, target);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+ return;
+ }
+
+ float alpha = mAlpha;
+
+ // Copy the input to avoid changing it.
+ mDrawTextureSourceRect.set(source);
+ mDrawTextureTargetRect.set(target);
+ source = mDrawTextureSourceRect;
+ target = mDrawTextureTargetRect;
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ if (!bindTexture(from)) return;
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ setMixedColor(toColor, ratio, alpha);
+ convertCoordinate(source, target, from);
+ setTextureCoords(source);
+ textureRect(target.left, target.top, target.width(), target.height());
+ mGLState.setTexEnvMode(GL11.GL_REPLACE);
+ }
+
private void drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int width, int height, float alpha) {
// change from 0 to 0.01f to prevent getting divided by zero below
@@ -482,45 +558,9 @@
final GL11 gl = mGL;
if (!bindTexture(from)) return;
- //
- // The formula we want:
- // alpha * ((1 - ratio) * from + ratio * to)
- //
- // The formula that GL supports is in the form of:
- // combo * from + (1 - combo) * to * scale
- //
- // So, we have combo = alpha * (1 - ratio)
- // and scale = alpha * ratio / (1 - combo)
- //
- float combo = alpha * (1 - ratio);
- float scale = alpha * ratio / (1 - combo);
-
// Interpolate the RGB and alpha values between both textures.
mGLState.setTexEnvMode(GL11.GL_COMBINE);
-
- // Specify the interpolation factor via the alpha component of
- // GL_TEXTURE_ENV_COLORs.
- // RGB component are get from toColor and will used as SRC1
- float colorScale = scale * (toColor >>> 24) / (0xff * 0xff);
- setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
- ((toColor >>> 8) & 0xff) * colorScale,
- (toColor & 0xff) * colorScale, combo);
- gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
-
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
-
- // Wire up the interpolation factor for RGB.
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
-
- // Wire up the interpolation factor for alpha.
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
- gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+ setMixedColor(toColor, ratio, alpha);
drawBoundTexture(from, x, y, width, height);
mGLState.setTexEnvMode(GL11.GL_REPLACE);
diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java
index 1651b43..13b610b 100644
--- a/src/com/android/gallery3d/ui/GLRoot.java
+++ b/src/com/android/gallery3d/ui/GLRoot.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Matrix;
import com.android.gallery3d.anim.CanvasAnimation;
@@ -45,4 +46,6 @@
public void freeze();
public void unfreeze();
public void setLightsOutMode(boolean enabled);
+
+ public Context getContext();
}
diff --git a/src/com/android/gallery3d/ui/GLView.java b/src/com/android/gallery3d/ui/GLView.java
index 3924c6e..664012c 100644
--- a/src/com/android/gallery3d/ui/GLView.java
+++ b/src/com/android/gallery3d/ui/GLView.java
@@ -21,6 +21,7 @@
import android.view.MotionEvent;
import com.android.gallery3d.anim.CanvasAnimation;
+import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.common.Utils;
import java.util.ArrayList;
@@ -77,6 +78,9 @@
protected int mScrollHeight = 0;
protected int mScrollWidth = 0;
+ private float [] mBackgroundColor;
+ private StateTransitionAnimation mTransition;
+
public void startAnimation(CanvasAnimation animation) {
GLRoot root = getGLRoot();
if (root == null) throw new IllegalStateException();
@@ -217,13 +221,46 @@
}
protected void render(GLCanvas canvas) {
+ boolean transitionActive = false;
+ if (mTransition != null && mTransition.calculate(AnimationTime.get())) {
+ invalidate();
+ transitionActive = mTransition.isActive();
+ }
renderBackground(canvas);
+ canvas.save();
+ if (transitionActive) {
+ mTransition.applyContentTransform(this, canvas);
+ }
for (int i = 0, n = getComponentCount(); i < n; ++i) {
renderChild(canvas, getComponent(i));
}
+ canvas.restore();
+ if (transitionActive) {
+ mTransition.applyOverlay(this, canvas);
+ }
+ }
+
+ public void setIntroAnimation(StateTransitionAnimation intro) {
+ mTransition = intro;
+ if (mTransition != null) mTransition.start();
+ }
+
+ public float [] getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ public void setBackgroundColor(float [] color) {
+ mBackgroundColor = color;
}
protected void renderBackground(GLCanvas view) {
+ if (mBackgroundColor != null) {
+ view.clearBuffer(mBackgroundColor);
+ }
+ if (mTransition != null && mTransition.isActive()) {
+ mTransition.applyBackground(this, view);
+ return;
+ }
}
protected void renderChild(GLCanvas canvas, GLView component) {
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 5978159..747a34c 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -17,6 +17,7 @@
package com.android.gallery3d.ui;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -546,10 +547,20 @@
}
private int getPanoramaRotation() {
- // Panorama only support rotations of 0 and 90, so if it is greater
- // than that flip the output surface texture to compensate
- if (mDisplayRotation > 180)
+ // This function is magic
+ // The issue here is that Pano makes bad assumptions about rotation and
+ // orientation. The first is it assumes only two rotations are possible,
+ // 0 and 90. Thus, if display rotation is >= 180, we invert the output.
+ // The second is that it assumes landscape is a 90 rotation from portrait,
+ // however on landscape devices this is not true. Thus, if we are in portrait
+ // on a landscape device, we need to invert the output
+ int orientation = getGLRoot().getContext().getResources().getConfiguration().orientation;
+ boolean invertPortrait = (orientation == Configuration.ORIENTATION_PORTRAIT
+ && (mDisplayRotation == 90 || mDisplayRotation == 270));
+ boolean invert = (mDisplayRotation >= 180);
+ if (invert != invertPortrait) {
return (mCompensation + 180) % 360;
+ }
return mCompensation;
}
@@ -839,8 +850,8 @@
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
@Override
@@ -1002,8 +1013,8 @@
// onDoubleTap happened on the second ACTION_DOWN.
// We need to ignore the next UP event.
mIgnoreUpEvent = true;
- if (scale <= 1.0f || controller.isAtMinimalScale()) {
- controller.zoomIn(x, y, Math.max(1.5f, scale * 1.5f));
+ if (scale <= .75f || controller.isAtMinimalScale()) {
+ controller.zoomIn(x, y, Math.max(1.0f, scale * 1.5f));
} else {
controller.resetToFullView();
}
@@ -1781,8 +1792,8 @@
MediaItem item = mModel.getMediaItem(i);
if (item == null) continue;
ScreenNail sc = mModel.getScreenNail(i);
- if (!(sc instanceof BitmapScreenNail)
- || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
+ if (!(sc instanceof TiledScreenNail)
+ || ((TiledScreenNail) sc).isShowingPlaceholder()) continue;
// Now, sc is BitmapScreenNail and is not showing placeholder
Rect rect = new Rect(getPhotoRect(i));
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index fffa7e0..6a4bcea 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -67,7 +67,7 @@
SNAPBACK_ANIMATION_TIME, // ANIM_KIND_SNAPBACK
400, // ANIM_KIND_SLIDE
300, // ANIM_KIND_ZOOM
- PhotoPage.ANIM_TIME_OPENING, // ANIM_KIND_OPENING
+ 300, // ANIM_KIND_OPENING
0, // ANIM_KIND_FLING (the duration is calculated dynamically)
0, // ANIM_KIND_FLING_X (see the comment above)
0, // ANIM_KIND_DELETE (the duration is calculated dynamically)
diff --git a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
index 812e831..36e7f4b 100644
--- a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
+++ b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
@@ -6,7 +6,7 @@
import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
public class PreparePageFadeoutTexture implements OnGLIdleListener {
- private static final long TIMEOUT = FadeTexture.DURATION;
+ private static final long TIMEOUT = 200;
public static final String KEY_FADE_TEXTURE = "fade_texture";
private RawTexture mTexture;
@@ -15,10 +15,20 @@
private GLView mRootPane;
public PreparePageFadeoutTexture(GLView rootPane) {
- mTexture = new RawTexture(rootPane.getWidth(), rootPane.getHeight(), true);
+ int w = rootPane.getWidth();
+ int h = rootPane.getHeight();
+ if (w == 0 || h == 0) {
+ mCancelled = true;
+ return;
+ }
+ mTexture = new RawTexture(w, h, true);
mRootPane = rootPane;
}
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
public synchronized RawTexture get() {
if (mCancelled) {
return null;
@@ -32,21 +42,26 @@
@Override
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
- if(!mCancelled) {
+ if (!mCancelled) {
+ try {
canvas.beginRenderTarget(mTexture);
mRootPane.render(canvas);
canvas.endRenderTarget();
- } else {
+ } catch (RuntimeException e) {
mTexture = null;
}
- mResultReady.open();
- return false;
+ } else {
+ mTexture = null;
+ }
+ mResultReady.open();
+ return false;
}
public static void prepareFadeOutTexture(AbstractGalleryActivity activity,
GLView rootPane) {
- GLRoot root = activity.getGLRoot();
PreparePageFadeoutTexture task = new PreparePageFadeoutTexture(rootPane);
+ if (task.isCancelled()) return;
+ GLRoot root = activity.getGLRoot();
RawTexture texture = null;
root.unlockRenderThread();
try {
@@ -56,8 +71,9 @@
root.lockRenderThread();
}
- if (texture != null) {
- activity.getTransitionStore().put(KEY_FADE_TEXTURE, texture);
+ if (texture == null) {
+ return;
}
+ activity.getTransitionStore().put(KEY_FADE_TEXTURE, texture);
}
}
diff --git a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
index 1930e38..7cb8948 100644
--- a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
+++ b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
@@ -109,6 +109,7 @@
canvas.translate(cx, cy);
canvas.scale(1, -1, 1);
canvas.translate(-cx, -cy);
+ updateTransformMatrix(mTransform);
canvas.drawTexture(mExtTexture, mTransform, x, y, width, height);
canvas.restore();
}
@@ -119,6 +120,8 @@
throw new UnsupportedOperationException();
}
+ protected void updateTransformMatrix(float[] matrix) {}
+
@Override
abstract public void noDraw();
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index 5ce06be..8f26981 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -462,8 +462,8 @@
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
private void uploadBackgroundTiles(GLCanvas canvas) {
@@ -575,8 +575,10 @@
}
if (tile == null) break;
if (!tile.isContentValid()) {
+ boolean hasBeenLoaded = tile.isLoaded();
Utils.assertTrue(tile.mTileState == STATE_DECODED);
tile.updateContent(canvas);
+ if (!hasBeenLoaded) tile.draw(canvas, 0, 0);
--quota;
}
}
@@ -621,7 +623,6 @@
}
}
- // TODO: avoid drawing the unused part of the textures.
static boolean drawTile(
Tile tile, GLCanvas canvas, RectF source, RectF target) {
while (true) {
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 08d3379..45e2ce2 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -40,51 +40,25 @@
public TileImageViewAdapter() {
}
- public TileImageViewAdapter(
- Bitmap bitmap, BitmapRegionDecoder regionDecoder) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mRegionDecoder = regionDecoder;
- mImageWidth = regionDecoder.getWidth();
- mImageHeight = regionDecoder.getHeight();
- mLevelCount = calculateLevelCount();
- }
-
public synchronized void clear() {
- updateScreenNail(null, false);
+ mScreenNail = null;
mImageWidth = 0;
mImageHeight = 0;
mLevelCount = 0;
mRegionDecoder = null;
}
- public synchronized void setScreenNail(Bitmap bitmap, int width, int height) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mImageWidth = width;
- mImageHeight = height;
- mRegionDecoder = null;
- mLevelCount = 0;
- }
-
+ // Caller is responsible to recycle the ScreenNail
public synchronized void setScreenNail(
ScreenNail screenNail, int width, int height) {
Utils.checkNotNull(screenNail);
- updateScreenNail(screenNail, false);
+ mScreenNail = screenNail;
mImageWidth = width;
mImageHeight = height;
mRegionDecoder = null;
mLevelCount = 0;
}
- private void updateScreenNail(ScreenNail screenNail, boolean own) {
- if (mScreenNail != null && mOwnScreenNail) {
- mScreenNail.recycle();
- }
- mScreenNail = screenNail;
- mOwnScreenNail = own;
- }
-
public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) {
mRegionDecoder = Utils.checkNotNull(decoder);
mImageWidth = decoder.getWidth();
diff --git a/src/com/android/gallery3d/ui/TiledScreenNail.java b/src/com/android/gallery3d/ui/TiledScreenNail.java
new file mode 100644
index 0000000..d2b34e3
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledScreenNail.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
+import com.android.gallery3d.data.MediaItem;
+
+// This is a ScreenNail wraps a Bitmap. There are some extra functions:
+//
+// - If we need to draw before the bitmap is available, we draw a rectange of
+// placeholder color (gray).
+//
+// - When the the bitmap is available, and we have drawn the placeholder color
+// before, we will do a fade-in animation.
+public class TiledScreenNail implements ScreenNail {
+ @SuppressWarnings("unused")
+ private static final String TAG = "TiledScreenNail";
+
+ // The duration of the fading animation in milliseconds
+ private static final int DURATION = 180;
+
+ private static int sMaxSide = 640;
+
+ // These are special values for mAnimationStartTime
+ private static final long ANIMATION_NOT_NEEDED = -1;
+ private static final long ANIMATION_NEEDED = -2;
+ private static final long ANIMATION_DONE = -3;
+
+ private int mWidth;
+ private int mHeight;
+ private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+
+ private Bitmap mBitmap;
+ private TiledTexture mTexture;
+
+ public TiledScreenNail(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ mBitmap = bitmap;
+ mTexture = new TiledTexture(bitmap);
+ }
+
+ public TiledScreenNail(int width, int height) {
+ setSize(width, height);
+ }
+
+ // This gets overridden by bitmap_screennail_placeholder
+ // in GalleryUtils.initialize
+ private static int mPlaceholderColor = 0xFF222222;
+ private static boolean mDrawPlaceholder = true;
+
+ public static void setPlaceholderColor(int color) {
+ mPlaceholderColor = color;
+ }
+
+ private void setSize(int width, int height) {
+ if (width == 0 || height == 0) {
+ width = sMaxSide;
+ height = sMaxSide * 3 / 4;
+ }
+ float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
+ mWidth = Math.round(scale * width);
+ mHeight = Math.round(scale * height);
+ }
+
+ private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
+ if (pool == null || bitmap == null) return;
+ pool.recycle(bitmap);
+ }
+
+ // Combines the two ScreenNails.
+ // Returns the used one and recycle the unused one.
+ public ScreenNail combine(ScreenNail other) {
+ if (other == null) {
+ return this;
+ }
+
+ if (!(other instanceof TiledScreenNail)) {
+ recycle();
+ return other;
+ }
+
+ // Now both are TiledScreenNail. Move over the information about width,
+ // height, and Bitmap, then recycle the other.
+ TiledScreenNail newer = (TiledScreenNail) other;
+ mWidth = newer.mWidth;
+ mHeight = newer.mHeight;
+ if (newer.mTexture != null) {
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ if (mTexture != null) mTexture.recycle();
+ mBitmap = newer.mBitmap;
+ mTexture = newer.mTexture;
+ newer.mBitmap = null;
+ newer.mTexture = null;
+ }
+ newer.recycle();
+ return this;
+ }
+
+ public void updatePlaceholderSize(int width, int height) {
+ if (mBitmap != null) return;
+ if (width == 0 || height == 0) return;
+ setSize(width, height);
+ }
+
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void noDraw() {
+ }
+
+ @Override
+ public void recycle() {
+ if (mTexture != null) {
+ mTexture.recycle();
+ mTexture = null;
+ }
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ mBitmap = null;
+ }
+
+ public static void disableDrawPlaceholder() {
+ mDrawPlaceholder = false;
+ }
+
+ public static void enableDrawPlaceholder() {
+ mDrawPlaceholder = true;
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ if (mTexture == null || !mTexture.isReady()) {
+ if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
+ mAnimationStartTime = ANIMATION_NEEDED;
+ }
+ if(mDrawPlaceholder) {
+ canvas.fillRect(x, y, width, height, mPlaceholderColor);
+ }
+ return;
+ }
+
+ if (mAnimationStartTime == ANIMATION_NEEDED) {
+ mAnimationStartTime = AnimationTime.get();
+ }
+
+ if (isAnimating()) {
+ mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
+ width, height);
+ } else {
+ mTexture.draw(canvas, x, y, width, height);
+ }
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, RectF source, RectF dest) {
+ if (mTexture == null || !mTexture.isReady()) {
+ canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
+ mPlaceholderColor);
+ return;
+ }
+
+ mTexture.draw(canvas, source, dest);
+ }
+
+ public boolean isAnimating() {
+ if (mAnimationStartTime < 0) return false;
+ if (AnimationTime.get() - mAnimationStartTime >= DURATION) {
+ mAnimationStartTime = ANIMATION_DONE;
+ return false;
+ }
+ return true;
+ }
+
+ private float getRatio() {
+ float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
+ return Utils.clamp(1.0f - r, 0.0f, 1.0f);
+ }
+
+ public boolean isShowingPlaceholder() {
+ return (mBitmap == null) || isAnimating();
+ }
+
+ public TiledTexture getTexture() {
+ return mTexture;
+ }
+
+ public static void setMaxSide(int size) {
+ sMaxSide = size;
+ }
+}
diff --git a/src/com/android/gallery3d/ui/TiledTexture.java b/src/com/android/gallery3d/ui/TiledTexture.java
new file mode 100644
index 0000000..6e9ad9e
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledTexture.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+
+import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+// This class is similar to BitmapTexture, except the bitmap is
+// split into tiles. By doing so, we may increase the time required to
+// upload the whole bitmap but we reduce the time of uploading each tile
+// so it make the animation more smooth and prevents jank.
+public class TiledTexture {
+ private static final int CONTENT_SIZE = 254;
+ private static final int BORDER_SIZE = 1;
+ private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
+ private static final int INIT_CAPACITY = 8;
+
+ private static Tile sFreeTileHead = null;
+ private static final Object sFreeTileLock = new Object();
+
+ private static Bitmap sUploadBitmap;
+ private static Canvas sCanvas;
+ private static Paint sPaint;
+
+ private int mUploadIndex = 0;
+
+ private final Tile[] mTiles;
+ private final int mWidth;
+ private final int mHeight;
+ private final RectF mSrcRect = new RectF();
+ private final RectF mDestRect = new RectF();
+
+ public static class Uploader implements OnGLIdleListener {
+ private final ArrayDeque<TiledTexture> mTextures =
+ new ArrayDeque<TiledTexture>(INIT_CAPACITY);
+
+ private final GLRoot mGlRoot;
+ private boolean mIsQueued = false;
+
+ public Uploader(GLRoot glRoot) {
+ mGlRoot = glRoot;
+ }
+
+ public synchronized void clear() {
+ mTextures.clear();
+ }
+
+ public synchronized void addTexture(TiledTexture t) {
+ if (t.isReady()) return;
+ mTextures.addLast(t);
+
+ if (mIsQueued) return;
+ mIsQueued = true;
+ mGlRoot.addOnGLIdleListener(this);
+ }
+
+
+ @Override
+ public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
+ ArrayDeque<TiledTexture> deque = mTextures;
+ synchronized (this) {
+ if (!deque.isEmpty()) {
+ TiledTexture t = deque.peekFirst();
+ if (t.uploadNextTile(canvas)) {
+ deque.removeFirst();
+ mGlRoot.requestRender();
+ }
+ }
+ mIsQueued = !mTextures.isEmpty();
+
+ // return true to keep this listener in the queue
+ return mIsQueued;
+ }
+ }
+ }
+
+ private static class Tile extends UploadedTexture {
+ public int offsetX;
+ public int offsetY;
+ public Bitmap bitmap;
+ public Tile nextFreeTile;
+ public int contentWidth;
+ public int contentHeight;
+
+ @Override
+ public void setSize(int width, int height) {
+ contentWidth = width;
+ contentHeight = height;
+ mWidth = width + 2 * BORDER_SIZE;
+ mHeight = height + 2 * BORDER_SIZE;
+ mTextureWidth = TILE_SIZE;
+ mTextureHeight = TILE_SIZE;
+ }
+
+ @Override
+ protected Bitmap onGetBitmap() {
+ int x = BORDER_SIZE - offsetX;
+ int y = BORDER_SIZE - offsetY;
+ int r = bitmap.getWidth() - x;
+ int b = bitmap.getHeight() - y ;
+ sCanvas.drawBitmap(bitmap, x, y, null);
+ bitmap = null;
+
+ // draw borders if need
+ if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
+ if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
+ if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
+ if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
+
+ return sUploadBitmap;
+ }
+
+ @Override
+ protected void onFreeBitmap(Bitmap bitmap) {
+ // do nothing
+ }
+ }
+
+ private static void freeTile(Tile tile) {
+ tile.invalidateContent();
+ tile.bitmap = null;
+ synchronized (sFreeTileLock) {
+ tile.nextFreeTile = sFreeTileHead;
+ sFreeTileHead = tile;
+ }
+ }
+
+ private static Tile obtainTile() {
+ synchronized (sFreeTileLock) {
+ Tile result = sFreeTileHead;
+ if (result == null) return new Tile();
+ sFreeTileHead = result.nextFreeTile;
+ result.nextFreeTile = null;
+ return result;
+ }
+ }
+
+ private boolean uploadNextTile(GLCanvas canvas) {
+ if (mUploadIndex == mTiles.length) return true;
+ Tile next = mTiles[mUploadIndex++];
+ boolean hasBeenLoad = next.isLoaded();
+ next.updateContent(canvas);
+
+ // It will take some time for a texture to be drawn for the first
+ // time. When scrolling, we need to draw several tiles on the screen
+ // at the same time. It may cause a UI jank even these textures has
+ // been uploaded.
+ if (!hasBeenLoad) next.draw(canvas, 0, 0);
+ return mUploadIndex == mTiles.length;
+ }
+
+ public TiledTexture(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ ArrayList<Tile> list = new ArrayList<Tile>();
+
+ for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
+ for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
+ Tile tile = obtainTile();
+ tile.offsetX = x;
+ tile.offsetY = y;
+ tile.bitmap = bitmap;
+ tile.setSize(
+ Math.min(CONTENT_SIZE, mWidth - x),
+ Math.min(CONTENT_SIZE, mHeight - y));
+ list.add(tile);
+ }
+ }
+ mTiles = list.toArray(new Tile[list.size()]);
+ }
+
+ public boolean isReady() {
+ return mUploadIndex == mTiles.length;
+ }
+
+ public void recycle() {
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ freeTile(mTiles[i]);
+ }
+ }
+
+ public static void freeResources() {
+ sUploadBitmap = null;
+ sCanvas = null;
+ sPaint = null;
+ }
+
+ public static void prepareResources() {
+ sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
+ sCanvas = new Canvas(sUploadBitmap);
+ sPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ sPaint.setColor(Color.TRANSPARENT);
+ sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ }
+
+ // We want to draw the "source" on the "target".
+ // This method is to find the "output" rectangle which is
+ // the corresponding area of the "src".
+ // (x,y) target
+ // (x0,y0) source +---------------+
+ // +----------+ | |
+ // | src | | output |
+ // | +--+ | linear map | +----+ |
+ // | +--+ | ----------> | | | |
+ // | | by (scaleX, scaleY) | +----+ |
+ // +----------+ | |
+ // Texture +---------------+
+ // Canvas
+ private static void mapRect(RectF output,
+ RectF src, float x0, float y0, float x, float y, float scaleX,
+ float scaleY) {
+ output.set(x + (src.left - x0) * scaleX,
+ y + (src.top - y0) * scaleY,
+ x + (src.right - x0) * scaleX,
+ y + (src.bottom - y0) * scaleY);
+ }
+
+ // Draws a mixed color of this texture and a specified color onto the
+ // a rectangle. The used color is: from * (1 - ratio) + to * ratio.
+ public void drawMixed(GLCanvas canvas, int color, float ratio,
+ int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws the texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws a sub region of this texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, RectF source, RectF target) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float x0 = source.left;
+ float y0 = source.top;
+ float x = target.left;
+ float y = target.top;
+ float scaleX = target.width() / source.width();
+ float scaleY = target.height() / source.height();
+
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ if (!src.intersect(source)) continue;
+ mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, src, dest);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/util/BucketNames.java b/src/com/android/gallery3d/util/BucketNames.java
index 043dd3d..df7684a 100644
--- a/src/com/android/gallery3d/util/BucketNames.java
+++ b/src/com/android/gallery3d/util/BucketNames.java
@@ -23,4 +23,5 @@
public static final String IMPORTED = "Imported";
public static final String DOWNLOAD = "download";
+ public static final String EDITED_ONLINE_PHOTOS = "EditedOnlinePhotos";
}
diff --git a/src/com/android/gallery3d/util/GalleryUtils.java b/src/com/android/gallery3d/util/GalleryUtils.java
index 1955521..62f2235 100644
--- a/src/com/android/gallery3d/util/GalleryUtils.java
+++ b/src/com/android/gallery3d/util/GalleryUtils.java
@@ -42,7 +42,7 @@
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
-import com.android.gallery3d.ui.BitmapScreenNail;
+import com.android.gallery3d.ui.TiledScreenNail;
import com.android.gallery3d.util.ThreadPool.CancelListener;
import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -81,7 +81,7 @@
wm.getDefaultDisplay().getMetrics(metrics);
sPixelDensity = metrics.density;
Resources r = context.getResources();
- BitmapScreenNail.setPlaceholderColor(r.getColor(
+ TiledScreenNail.setPlaceholderColor(r.getColor(
R.color.bitmap_screennail_placeholder));
initializeThumbnailSizes(metrics, r);
}
@@ -91,7 +91,7 @@
// Never need to completely fill the screen
maxDimensionPixels = maxDimensionPixels / 2;
MediaItem.setThumbnailSizes(maxDimensionPixels, 200);
- BitmapScreenNail.setMaxSide(maxDimensionPixels);
+ TiledScreenNail.setMaxSide(maxDimensionPixels);
}
public static boolean isHighResolution(Context context) {
diff --git a/src/com/android/gallery3d/util/MediaSetUtils.java b/src/com/android/gallery3d/util/MediaSetUtils.java
index 9f5cbba..83b6b32 100644
--- a/src/com/android/gallery3d/util/MediaSetUtils.java
+++ b/src/com/android/gallery3d/util/MediaSetUtils.java
@@ -33,6 +33,9 @@
public static final int DOWNLOAD_BUCKET_ID = GalleryUtils.getBucketId(
Environment.getExternalStorageDirectory().toString() + "/"
+ BucketNames.DOWNLOAD);
+ public static final int EDITED_ONLINE_PHOTOS_BUCKET_ID = GalleryUtils.getBucketId(
+ Environment.getExternalStorageDirectory().toString() + "/"
+ + BucketNames.EDITED_ONLINE_PHOTOS);
public static final int IMPORTED_BUCKET_ID = GalleryUtils.getBucketId(
Environment.getExternalStorageDirectory().toString() + "/"
+ BucketNames.IMPORTED);
diff --git a/src_pd/com/android/gallery3d/util/LightCycleHelper.java b/src_pd/com/android/gallery3d/util/LightCycleHelper.java
index 995eac8..6ebd9ec 100644
--- a/src_pd/com/android/gallery3d/util/LightCycleHelper.java
+++ b/src_pd/com/android/gallery3d/util/LightCycleHelper.java
@@ -17,7 +17,6 @@
package com.android.gallery3d.util;
import android.app.Activity;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -39,6 +38,29 @@
}
}
+ public static class PanoramaViewHelper {
+
+ public PanoramaViewHelper(Activity activity) {
+ /* Do nothing */
+ }
+
+ public void onStart() {
+ /* Do nothing */
+ }
+
+ public void onCreate() {
+ /* Do nothing */
+ }
+
+ public void onStop() {
+ /* Do nothing */
+ }
+
+ public void showPanorama(Uri uri) {
+ /* Do nothing */
+ }
+ }
+
public static void setupCaptureIntent(Context context, Intent it, String outputDir) {
/* Do nothing */
}
@@ -47,10 +69,6 @@
return false;
}
- public static void viewPanorama(Activity activity, Uri uri) {
- /* Do nothing */
- }
-
public static PanoramaMetadata getPanoramaMetadata(Context context, Uri uri) {
return null;
}
diff --git a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
index 2f2d753..5a08b85 100644
--- a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
+++ b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
@@ -83,4 +83,6 @@
public void dumpStatisticsAndClear() {}
public void beginRenderTarget(RawTexture texture) {}
public void endRenderTarget() {}
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target) {}
}
diff --git a/tests/src/com/android/gallery3d/ui/GLRootMock.java b/tests/src/com/android/gallery3d/ui/GLRootMock.java
index 467edfc..b1c4355 100644
--- a/tests/src/com/android/gallery3d/ui/GLRootMock.java
+++ b/tests/src/com/android/gallery3d/ui/GLRootMock.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Matrix;
import com.android.gallery3d.anim.CanvasAnimation;
@@ -42,4 +43,5 @@
public void freeze() {}
public void unfreeze() {}
public void setLightsOutMode(boolean enabled) {}
+ public Context getContext() { return null; }
}
diff --git a/tests/src/com/android/gallery3d/ui/GLRootStub.java b/tests/src/com/android/gallery3d/ui/GLRootStub.java
index 0f3a001..7f134db 100644
--- a/tests/src/com/android/gallery3d/ui/GLRootStub.java
+++ b/tests/src/com/android/gallery3d/ui/GLRootStub.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Matrix;
import com.android.gallery3d.anim.CanvasAnimation;
@@ -35,4 +36,5 @@
public void freeze() {}
public void unfreeze() {}
public void setLightsOutMode(boolean enabled) {}
+ public Context getContext() { return null; }
}