summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gallerycommon/src/com/android/gallery3d/common/Utils.java2
-rw-r--r--src/com/android/gallery3d/anim/StateTransitionAnimation.java77
-rw-r--r--src/com/android/gallery3d/app/ActivityState.java32
-rw-r--r--src/com/android/gallery3d/app/AlbumPage.java27
-rw-r--r--src/com/android/gallery3d/app/AlbumSetPage.java8
-rw-r--r--src/com/android/gallery3d/app/CropImage.java21
-rw-r--r--src/com/android/gallery3d/app/PhotoDataAdapter.java56
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java87
-rw-r--r--src/com/android/gallery3d/app/SinglePhotoDataAdapter.java11
-rw-r--r--src/com/android/gallery3d/app/StateManager.java6
-rw-r--r--src/com/android/gallery3d/app/TrimVideo.java5
-rw-r--r--src/com/android/gallery3d/data/DataManager.java12
-rw-r--r--src/com/android/gallery3d/data/EmptyAlbumImage.java2
-rw-r--r--src/com/android/gallery3d/data/LocalAlbum.java1
-rw-r--r--src/com/android/gallery3d/data/LocalImage.java5
-rw-r--r--src/com/android/gallery3d/data/LocalVideo.java1
-rw-r--r--src/com/android/gallery3d/data/MediaObject.java4
-rw-r--r--src/com/android/gallery3d/data/SecureAlbum.java17
-rw-r--r--src/com/android/gallery3d/data/UriImage.java4
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java66
-rw-r--r--src/com/android/gallery3d/filtershow/PanelController.java42
-rw-r--r--src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java21
-rw-r--r--src/com/android/gallery3d/filtershow/cache/ImageLoader.java85
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java72
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java4
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java46
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java1
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java96
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java330
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java319
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java10
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java2
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java12
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java21
-rw-r--r--src/com/android/gallery3d/filtershow/ui/ControlPoint.java19
-rw-r--r--src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java14
-rw-r--r--src/com/android/gallery3d/filtershow/ui/ImageCurves.java406
-rw-r--r--src/com/android/gallery3d/filtershow/ui/Spline.java341
-rw-r--r--src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java2
-rw-r--r--src/com/android/gallery3d/ui/BasicTexture.java4
-rw-r--r--src/com/android/gallery3d/ui/BitmapScreenNail.java191
-rw-r--r--src/com/android/gallery3d/ui/GLCanvas.java7
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java82
-rw-r--r--src/com/android/gallery3d/ui/GLRoot.java3
-rw-r--r--src/com/android/gallery3d/ui/GLView.java39
-rw-r--r--src/com/android/gallery3d/ui/PhotoView.java25
-rw-r--r--src/com/android/gallery3d/ui/PositionController.java2
-rw-r--r--src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java2
-rw-r--r--src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java3
-rw-r--r--src/com/android/gallery3d/ui/TileImageView.java7
-rw-r--r--src/com/android/gallery3d/ui/TileImageViewAdapter.java32
-rw-r--r--src/com/android/gallery3d/ui/TiledScreenNail.java216
-rw-r--r--src/com/android/gallery3d/ui/TiledTexture.java298
-rw-r--r--src/com/android/gallery3d/util/GalleryUtils.java6
-rw-r--r--tests/src/com/android/gallery3d/ui/GLCanvasStub.java2
-rw-r--r--tests/src/com/android/gallery3d/ui/GLRootMock.java2
-rw-r--r--tests/src/com/android/gallery3d/ui/GLRootStub.java2
57 files changed, 2054 insertions, 1156 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/common/Utils.java b/gallerycommon/src/com/android/gallery3d/common/Utils.java
index f5a266706..3a68745c4 100644
--- a/gallerycommon/src/com/android/gallery3d/common/Utils.java
+++ b/gallerycommon/src/com/android/gallery3d/common/Utils.java
@@ -76,7 +76,7 @@ public class Utils {
// 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 000000000..ffe753db7
--- /dev/null
+++ b/src/com/android/gallery3d/anim/StateTransitionAnimation.java
@@ -0,0 +1,77 @@
+/*
+ * 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 com.android.gallery3d.ui.GLCanvas;
+import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.RawTexture;
+
+public class StateTransitionAnimation extends Animation {
+ private static final float BACKGROUND_ALPHA_FROM = 1f;
+ private static final float BACKGROUND_ALPHA_TO = 0f;
+ private static final float BACKGROUND_SCALE_FROM = 1f;
+ private static final float BACKGROUND_SCALE_TO = 0f;
+ private static final float FOREGROUND_ALPHA_FROM = 0.9f;
+ private static final float FOREGROUND_ALPHA_TO = 1f;
+ private static final float FOREGROUND_SCALE_FROM = 3f;
+ private static final float FOREGROUND_SCALE_TO = 1f;
+
+ private float mCurrentForegroundScale;
+ private float mCurrentBackgroundScale;
+ private float mCurrentBackgroundAlpha;
+ private float mCurrentForegroundAlpha;
+
+ public StateTransitionAnimation(int duration) {
+ setDuration(duration);
+ setInterpolator(new DecelerateInterpolator());
+ }
+
+ @Override
+ protected void onCalculate(float progress) {
+ mCurrentForegroundScale = FOREGROUND_SCALE_FROM
+ + (FOREGROUND_SCALE_TO - FOREGROUND_SCALE_FROM) * progress;
+ mCurrentForegroundAlpha = FOREGROUND_ALPHA_FROM
+ + (FOREGROUND_ALPHA_TO - FOREGROUND_ALPHA_FROM) * progress;
+ mCurrentBackgroundAlpha = BACKGROUND_ALPHA_FROM
+ + (BACKGROUND_ALPHA_TO - BACKGROUND_ALPHA_FROM) * progress;
+ mCurrentBackgroundScale = BACKGROUND_SCALE_FROM
+ + (BACKGROUND_SCALE_TO - BACKGROUND_SCALE_FROM) * progress;
+ }
+
+ public void applyBackground(GLView view, GLCanvas canvas, RawTexture fadeTexture) {
+ canvas.clearBuffer(view.getBackgroundColor());
+ canvas.save();
+ canvas.setAlpha(mCurrentBackgroundAlpha);
+ int xOffset = view.getWidth() / 2;
+ int yOffset = view.getHeight() / 2;
+ canvas.translate(xOffset, yOffset);
+ canvas.scale(mCurrentBackgroundScale, mCurrentBackgroundScale, 1);
+ fadeTexture.draw(canvas, -xOffset, -yOffset);
+ canvas.restore();
+ }
+
+ public void applyForegroundTransformation(GLView view, GLCanvas canvas) {
+ int xOffset = view.getWidth() / 2;
+ int yOffset = view.getHeight() / 2;
+ canvas.translate(xOffset, yOffset);
+ canvas.scale(mCurrentForegroundScale, mCurrentForegroundScale, 1);
+ canvas.translate(-xOffset, -yOffset);
+ canvas.setAlpha(mCurrentForegroundAlpha);
+ }
+}
diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java
index 75327e477..3ea6896cb 100644
--- a/src/com/android/gallery3d/app/ActivityState.java
+++ b/src/com/android/gallery3d/app/ActivityState.java
@@ -36,6 +36,8 @@ import android.view.WindowManager;
import com.android.gallery3d.R;
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 +68,24 @@ abstract public class ActivityState {
private boolean mPlugged = false;
boolean mIsFinishing = false;
+ private static final String KEY_TRANSITION_IN = "transition-in";
+
+ private RawTexture mFadeOutTexture;
+ private GLView mContentPane;
+ private boolean mWantFadeOut = false;
+ private boolean mTransitionIn;
+
protected ActivityState() {
}
protected void setContentPane(GLView content) {
- mActivity.getGLRoot().setContentPane(content);
+ mContentPane = content;
+ if (mTransitionIn) {
+ mContentPane.setFadeOutTexture(mFadeOutTexture);
+ mFadeOutTexture = null;
+ }
+ mContentPane.setBackgroundColor(getBackgroundColor());
+ mActivity.getGLRoot().setContentPane(mContentPane);
}
void initialize(AbstractGalleryActivity activity, Bundle data) {
@@ -84,6 +99,9 @@ abstract public class ActivityState {
}
protected void onBackPressed() {
+ if (mActivity.getStateManager().getStateCount() > 1) {
+ fadeOutOnNextPause();
+ }
mActivity.getStateManager().finishState(this);
}
@@ -157,10 +175,19 @@ abstract public class ActivityState {
win.setAttributes(params);
}
+ protected void fadeOutOnNextPause() {
+ mWantFadeOut = true;
+ }
+
protected void onPause() {
if (0 != (mFlags & FLAG_SCREEN_ON_WHEN_PLUGGED)) {
((Activity) mActivity).unregisterReceiver(mPowerIntentReceiver);
}
+ if (mWantFadeOut) {
+ mWantFadeOut = false;
+ mActivity.getTransitionStore().put(KEY_TRANSITION_IN, true);
+ PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mContentPane);
+ }
}
// should only be called by StateManager
@@ -214,6 +241,9 @@ abstract public class ActivityState {
// a subclass of ActivityState should override the method to resume itself
protected void onResume() {
+ mFadeOutTexture = mActivity.getTransitionStore().get(
+ PreparePageFadeoutTexture.KEY_FADE_TEXTURE);
+ mTransitionIn = mActivity.getTransitionStore().get(KEY_TRANSITION_IN, false);
}
protected boolean onCreateActionBar(Menu menu) {
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index c956bc969..edaf8caa7 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.GLCanvas;
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 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
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 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
} else {
// Render transition in pressed state
mAlbumView.setPressedIndex(slotIndex);
- PreparePageFadeoutTexture.prepareFadeOutTexture(mActivity, mRootPane);
- mAlbumView.setPressedIndex(-1);
pickPhoto(slotIndex);
}
@@ -300,8 +292,12 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
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);
+ }
}
}
@@ -373,15 +369,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
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 +398,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mAlbumDataAdapter.resume();
mAlbumView.resume();
+ mAlbumView.setPressedIndex(-1);
mActionModeHandler.resume();
if (!mInitialSynced) {
setLoadingBit(BIT_LOADING_SYNC);
@@ -561,7 +549,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
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 1719b89f9..49ab683be 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.FadeTexture;
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 @@ public class AlbumSetPage extends ActivityState implements
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 @@ public class AlbumSetPage extends ActivityState implements
& 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 @@ public class AlbumSetPage extends ActivityState implements
}
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 2b7450441..89ca63d44 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.ExifOutputStream;
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 @@ public class CropImage extends AbstractGalleryActivity {
private BitmapRegionDecoder mRegionDecoder;
private Bitmap mBitmapInIntent;
private boolean mUseRegionDecoder = false;
+ private BitmapScreenNail mBitmapScreenNail;
private ProgressDialog mProgressDialog;
private Future<BitmapRegionDecoder> mLoadTask;
@@ -813,8 +815,14 @@ public class CropImage extends AbstractGalleryActivity {
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 @@ public class CropImage extends AbstractGalleryActivity {
}
}
+ @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 66f2874f7..20f15be10 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.MediaItem;
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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
private static final int MSG_RUN_OBJECT = 3;
private static final int MSG_UPDATE_IMAGE_REQUESTS = 4;
- private static final int MIN_LOAD_COUNT = 8;
- private static final int DATA_CACHE_SIZE = 32;
+ private static final int MIN_LOAD_COUNT = 16;
+ private static final int DATA_CACHE_SIZE = 256;
private static final int SCREEN_NAIL_MAX = PhotoView.SCREEN_NAIL_MAX;
private static final int IMAGE_CACHE_SIZE = 2 * SCREEN_NAIL_MAX + 1;
@@ -162,6 +163,7 @@ public class PhotoDataAdapter implements PhotoPage.Model {
private DataListener mDataListener;
private final SourceListener mSourceListener = new SourceListener();
+ private final TiledTexture.Uploader mUploader;
// The path of the current viewing item will be stored in mItemPath.
// If mItemPath is not null, mCurrentIndex is only a hint for where we
@@ -183,6 +185,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
Arrays.fill(mChanges, MediaObject.INVALID_DATA_VERSION);
+ mUploader = new TiledTexture.Uploader(activity.getGLRoot());
+
mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
@SuppressWarnings("unchecked")
@Override
@@ -301,8 +305,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
}
}
updateImageRequests();
+ updateScreenNailUploadQueue();
}
private void updateFullImage(Path path, Future<BitmapRegionDecoder> future) {
@@ -345,6 +350,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
@Override
public void resume() {
mIsActive = true;
+ TiledTexture.prepareResources();
+
mSource.addContentListener(mSourceListener);
updateImageCache();
updateImageRequests();
@@ -371,6 +378,9 @@ public class PhotoDataAdapter implements PhotoPage.Model {
}
mImageCache.clear();
mTileProvider.clear();
+
+ mUploader.clear();
+ TiledTexture.freeResources();
}
private MediaItem getItem(int index) {
@@ -402,6 +412,32 @@ public class PhotoDataAdapter implements PhotoPage.Model {
fireDataChange();
}
+ private void uploadScreenNail(int offset) {
+ int index = mCurrentIndex + offset;
+ if (index < mActiveStart || index >= mActiveEnd) return;
+
+ MediaItem item = getItem(index);
+ if (item == null) return;
+
+ ImageEntry e = mImageCache.get(item.getPath());
+ if (e == null) return;
+
+ ScreenNail s = e.screenNail;
+ if (s instanceof 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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
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 @@ public class PhotoDataAdapter implements PhotoPage.Model {
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 db7f3d9f1..ba9bf8c42 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -34,12 +34,10 @@ import android.os.Message;
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;
@@ -61,7 +59,6 @@ import com.android.gallery3d.data.SnailItem;
import com.android.gallery3d.data.SnailSource;
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,8 +70,6 @@ import com.android.gallery3d.ui.ImportCompleteListener;
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.util.GalleryUtils;
@@ -94,8 +89,9 @@ public class PhotoPage extends ActivityState implements
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 +161,6 @@ public class PhotoPage extends ActivityState implements
private boolean mTreatBackAsUp;
private boolean mStartInFilmstrip;
private boolean mInCameraRoll;
- private boolean mStartedFromAlbumPage;
private boolean mRecenterCameraOnResume = true;
private long mCameraSwitchCutoff = 0;
@@ -176,9 +171,7 @@ public class PhotoPage extends ActivityState implements
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 +186,15 @@ public class PhotoPage extends ActivityState implements
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 +213,6 @@ public class PhotoPage extends ActivityState implements
}
}
- 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 +239,6 @@ public class PhotoPage extends ActivityState implements
}
};
- private final FloatAnimation mBackgroundFade = new BackgroundFadeOut();
-
@Override
protected int getBackgroundColorId() {
return R.color.photo_background;
@@ -255,28 +246,6 @@ public class PhotoPage extends ActivityState implements
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 +335,12 @@ public class PhotoPage extends ActivityState implements
break;
}
case MSG_REFRESH_IMAGE: {
+ final MediaItem photo = mCurrentPhoto;
+ mCurrentPhoto = null;
+ updateCurrentPhoto(photo);
+ break;
+ }
+ case MSG_UPDATE_PHOTO_UI: {
updateUIForCurrentPhoto();
break;
}
@@ -388,9 +363,6 @@ public class PhotoPage extends ActivityState implements
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,6 +520,9 @@ public class PhotoPage extends ActivityState implements
mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot);
mProgressListener = new UpdateProgressListener();
progressManager.addChangeListener(mProgressListener);
+ if (mSecureAlbum != null) {
+ progressManager.addChangeListener(mSecureAlbum);
+ }
}
}
}
@@ -953,11 +928,10 @@ public class PhotoPage extends ActivityState implements
};
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 +950,11 @@ public class PhotoPage extends ActivityState implements
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);
+ }
}
}
@@ -1287,7 +1265,6 @@ public class PhotoPage extends ActivityState implements
// Hide the detail dialog on exit
if (mShowDetails) hideDetails();
if (mModel != null) {
- if (isFinishing()) preparePhotoFallbackView();
mModel.pause();
}
mPhotoView.pause();
@@ -1351,18 +1328,6 @@ public class PhotoPage extends ActivityState implements
} 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 2f6f16f3a..00f2fe78f 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.BitmapUtils;
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 @@ public class SinglePhotoDataAdapter extends TileImageViewAdapter
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 @@ public class SinglePhotoDataAdapter extends TileImageViewAdapter
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 @@ public class SinglePhotoDataAdapter extends TileImageViewAdapter
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 c041b0e6f..10de5d201 100644
--- a/src/com/android/gallery3d/app/StateManager.java
+++ b/src/com/android/gallery3d/app/StateManager.java
@@ -57,6 +57,7 @@ public class StateManager {
}
if (!mStack.isEmpty()) {
ActivityState top = getTopState();
+ top.fadeOutOnNextPause();
if (mIsResumed) top.onPause();
}
state.initialize(mActivity, data);
@@ -81,6 +82,7 @@ public class StateManager {
if (!mStack.isEmpty()) {
ActivityState as = getTopState();
+ as.fadeOutOnNextPause();
as.mReceivedResults = state.mResult;
if (mIsResumed) as.onPause();
} else {
@@ -207,6 +209,10 @@ public class StateManager {
}
// 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.fadeOutOnNextPause();
+ }
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 09a2abdf0..01fe462c2 100644
--- a/src/com/android/gallery3d/app/TrimVideo.java
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -268,9 +268,8 @@ public class TrimVideo extends Activity implements
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 3d2c0c2f0..408a24b13 100644
--- a/src/com/android/gallery3d/data/DataManager.java
+++ b/src/com/android/gallery3d/data/DataManager.java
@@ -85,9 +85,6 @@ public class DataManager {
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 @@ public class DataManager {
}
}
- // 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 dbbc01a33..6f8c37c6b 100644
--- a/src/com/android/gallery3d/data/EmptyAlbumImage.java
+++ b/src/com/android/gallery3d/data/EmptyAlbumImage.java
@@ -24,7 +24,7 @@ public class EmptyAlbumImage extends ActionImage {
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 4682e7792..8bbc364ec 100644
--- a/src/com/android/gallery3d/data/LocalAlbum.java
+++ b/src/com/android/gallery3d/data/LocalAlbum.java
@@ -267,7 +267,6 @@ public class LocalAlbum extends MediaSet {
GalleryUtils.assertNotInRenderThread();
mResolver.delete(mBaseUri, mWhereClause,
new String[]{String.valueOf(mBucketId)});
- mApplication.getDataManager().broadcastLocalDeletion();
}
@Override
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index dba6b68eb..61961d87a 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -288,8 +288,6 @@ public class LocalImage extends LocalMediaItem {
@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 @@ public class LocalImage extends LocalMediaItem {
mPanoramaMetadata = future.get();
mPanoramaMetadataInitialized = true;
if (mListener != null) {
- mListener.onChange(getSupportedOperations());
+ mListener.onChange(LocalImage.this, getSupportedOperations());
}
}
});
@@ -324,7 +322,6 @@ public class LocalImage extends LocalMediaItem {
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 5b6ee4b33..c876d81b1 100644
--- a/src/com/android/gallery3d/data/LocalVideo.java
+++ b/src/com/android/gallery3d/data/LocalVideo.java
@@ -187,7 +187,6 @@ public class LocalVideo extends LocalMediaItem {
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 382a5c792..14cd5242a 100644
--- a/src/com/android/gallery3d/data/MediaObject.java
+++ b/src/com/android/gallery3d/data/MediaObject.java
@@ -50,7 +50,7 @@ public abstract class MediaObject {
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 abstract class MediaObject {
}
public int getSupportedOperations(boolean getAll) {
- return 0;
+ return getSupportedOperations();
}
public void setSupportedOperationsListener(SupportedOperationsListener l) {
diff --git a/src/com/android/gallery3d/data/SecureAlbum.java b/src/com/android/gallery3d/data/SecureAlbum.java
index c666bdc75..0a8c5a827 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.MediaColumns;
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 class SecureAlbum extends MediaSet {
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 5fab667b8..aaa36a917 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -255,8 +255,6 @@ public class UriImage extends MediaItem {
@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 @@ public class UriImage extends MediaItem {
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 f2b817c17..f76179d8b 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -31,6 +31,7 @@ import android.widget.ListView;
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.filtershow.cache.ImageLoader;
@@ -60,7 +61,9 @@ import com.android.gallery3d.filtershow.imageshow.ImageZoom;
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 java.io.File;
import java.lang.ref.WeakReference;
@@ -77,7 +80,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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 +99,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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;
@@ -117,6 +121,19 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
ImageFilterRS.setRenderScriptContext(this);
+ // 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(6));
+ 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 +146,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
}
});
- 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 +157,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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 +166,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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 +195,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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 +206,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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,8 +216,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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));
@@ -298,7 +310,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mPanelController.addComponent(mColorsButton, findViewById(R.id.shadowRecoveryButton));
mPanelController.addView(findViewById(R.id.applyEffect));
-
+ mPanelController.addView(findViewById(R.id.pickCurvesChannel));
findViewById(R.id.resetOperationsButton).setOnClickListener(
createOnClickResetOperationsButton());
@@ -523,8 +535,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mCurrentImageSmallFilter = filter;
filter.setPreviousImageSmallFilter(null);
- preset.setIsFx(true);
- filter.setImagePreset(preset);
+ filter.setImageFilter(new ImageFilterFx(null,ImageFilterFx.ORIG));
filter.setController(this);
filter.setImageLoader(mImageLoader);
@@ -565,10 +576,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
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++) {
@@ -699,6 +710,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
adapter.reset();
ImagePreset original = new ImagePreset(adapter.getItem(0));
mImageShow.setImagePreset(original);
+ mPanelController.resetParameters();
invalidateViews();
}
@@ -712,6 +724,20 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
};
}
+ @Override
+ public void onBackPressed() {
+ if (mPanelController.onBackPressed()) {
+ finish();
+ }
+ }
+
+ 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) {
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index f13cdd497..a21bb4fe1 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -131,12 +131,12 @@ public class PanelController implements OnClickListener {
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 +229,26 @@ public class PanelController implements OnClickListener {
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) {
@@ -379,6 +393,12 @@ public class PanelController implements OnClickListener {
}
}
+ if (view.getId() == R.id.pickCurvesChannel) {
+ ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
+ curves.nextChannel();
+ return;
+ }
+
if (mCurrentImage != null) {
mCurrentImage.unselect();
}
@@ -390,16 +410,13 @@ public class PanelController implements OnClickListener {
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);
break;
}
- */
case R.id.rotateButton: {
mCurrentImage = showImageView(R.id.imageRotate);
String ename = mCurrentImage.getContext().getString(R.string.rotate);
@@ -410,7 +427,7 @@ public class PanelController implements OnClickListener {
mCurrentImage = showImageView(R.id.imageFlip);
String ename = mCurrentImage.getContext().getString(R.string.flip);
mUtilityPanel.setEffectName(ename);
- mUtilityPanel.showParameter(false);
+ mUtilityPanel.setShowParameter(false);
break;
}
case R.id.vignetteButton: {
@@ -422,11 +439,9 @@ public class PanelController implements OnClickListener {
}
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;
@@ -456,6 +471,7 @@ public class PanelController implements OnClickListener {
mCurrentImage = showImageView(R.id.imageShow).setShowControls(false);
String ename = mCurrentImage.getContext().getString(R.string.wbalance);
mUtilityPanel.setEffectName(ename);
+ mUtilityPanel.setShowParameter(false);
ensureFilter("WBalance");
break;
}
diff --git a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
index 67bd49b1c..25d1db414 100644
--- a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
+++ b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java
@@ -62,7 +62,7 @@ public class DirectPresetCache implements Cache {
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 class DirectPresetCache implements Cache {
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 class DirectPresetCache implements Cache {
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 e00a1b77d..eeba4da41 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -48,14 +48,25 @@ public class ImageLoader {
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);
@@ -66,6 +77,10 @@ public class ImageLoader {
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 +105,20 @@ public class ImageLoader {
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 +139,55 @@ public class ImageLoader {
}
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_NORMAL:
+ 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;
+ default:
+ }
+
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
index 01b280b6e..3e8d298a3 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
@@ -9,12 +9,7 @@ public class ImageFilterCurves extends ImageFilter {
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,12 @@ public class ImageFilterCurves extends ImageFilter {
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterCurves filter = (ImageFilterCurves) super.clone();
- filter.setCurve(mCurve);
- filter.setSpline(new Spline(mSpline));
- 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];
+ for (int i = 0; i < 4; i++) {
+ if (mSplines[i] != null) {
+ filter.setSpline(new Spline(mSplines[i]), i);
}
}
+ return filter;
}
@Override
@@ -55,36 +33,48 @@ public class ImageFilterCurves extends ImageFilter {
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 +82,11 @@ public class ImageFilterCurves extends ImageFilter {
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 1575b18bb..7d8f41537 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
@@ -12,7 +12,7 @@ import java.util.Arrays;
public class ImageFilterFx extends ImageFilter {
private static final String TAG = "ImageFilterFx";
Bitmap fxBitmap;
-
+ public static final String ORIG = "Original";
public ImageFilterFx(Bitmap fxBitmap,String name) {
setFilterType(TYPE_FX);
mName = name;
@@ -29,6 +29,8 @@ public class ImageFilterFx extends ImageFilter {
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h,Bitmap fxBitmap, int fxw, int fxh);
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 08c09fb8e..3d48b7e53 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.Bitmap;
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 @@ public class ImageFilterGeometry extends ImageFilter {
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 @@ public class ImageFilterGeometry extends ImageFilter {
// 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/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index 9d9c7e548..5e613ebce 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -18,7 +18,6 @@ public class ImageFilterWBalance extends ImageFilter {
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/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index 0eb2e22aa..352fa5bf3 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -22,13 +22,6 @@ import android.graphics.RectF;
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?).
@@ -39,9 +32,6 @@ public class GeometryMetadata {
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 +46,12 @@ public class GeometryMetadata {
set(g);
}
- public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality){
+ public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality) {
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 +59,6 @@ public class GeometryMetadata {
mCropBounds.set(g.mCropBounds);
mPhotoBounds.set(g.mPhotoBounds);
mFlip = g.mFlip;
- mSafe = g.mSafe;
- mMatrix = g.mMatrix;
mBounds = g.mBounds;
}
@@ -87,10 +74,19 @@ public class GeometryMetadata {
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 +95,6 @@ public class GeometryMetadata {
return new RectF(mPhotoBounds);
}
- public boolean safe() {
- return mSafe;
- }
-
public void setScaleFactor(float scale) {
mScaleFactor = scale;
}
@@ -119,41 +111,12 @@ public class GeometryMetadata {
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 +134,7 @@ public class GeometryMetadata {
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 +147,13 @@ public class GeometryMetadata {
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 +189,34 @@ public class GeometryMetadata {
}
}
- 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 90d36e942..4d171bf4c 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.Bitmap;
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 {
@@ -40,19 +38,14 @@ public class ImageCrop extends ImageGeometry {
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;
@@ -86,22 +79,21 @@ public class ImageCrop extends ImageGeometry {
}
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 RectF getCropBoundsDisplayed() {
RectF bounds = getLocalCropBounds();
RectF crop = new RectF(bounds);
@@ -115,7 +107,7 @@ public class ImageCrop extends ImageGeometry {
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 +129,44 @@ public class ImageCrop extends ImageGeometry {
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 +175,33 @@ public class ImageCrop extends ImageGeometry {
// 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 (newHeight < minWidthHeight) {
- newHeight = 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;
+ }
}
-
RectF pbounds = getLocalPhotoBounds();
if (pbounds.width() < minWidthHeight) {
newWidth = pbounds.width();
@@ -164,18 +211,14 @@ public class ImageCrop extends ImageGeometry {
}
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,33 +245,88 @@ public class ImageCrop extends ImageGeometry {
else if (bottom <= TOUCH_TOLERANCE) {
movingEdges |= MOVE_BOTTOM;
}
+ // Check inside block.
+ if (cropped.contains(x, y) && (movingEdges == 0)) {
+ movingEdges = MOVE_BLOCK;
+ }
invalidate();
}
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
+ int select = movingEdges;
+ if (mFixAspectRatio && (select != MOVE_BLOCK)) {
+ if ((select & MOVE_LEFT) != 0) {
+ select &= ~MOVE_BOTTOM;
+ select |= MOVE_TOP;
+ deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight);
+ }
+ if ((select & MOVE_TOP) != 0) {
+ select &= ~MOVE_RIGHT;
+ select |= MOVE_LEFT;
+ deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight);
+ }
+ if ((select & MOVE_RIGHT) != 0) {
+ select &= ~MOVE_TOP;
+ select |= MOVE_BOTTOM;
+ deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight);
+ }
+ if ((select & MOVE_BOTTOM) != 0) {
+ select &= ~MOVE_LEFT;
+ select |= MOVE_RIGHT;
+ deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight);
+ }
+ }
+
+ 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 {
- if ((movingEdges & MOVE_LEFT) != 0) {
- cropped.left = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight);
- fixRectAspectW(cropped);
+ 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 ((movingEdges & MOVE_TOP) != 0) {
- cropped.top = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight);
- fixRectAspectH(cropped);
+ if ((select & MOVE_BOTTOM) != 0) {
+ dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
+ - cropped.bottom;
}
- if ((movingEdges & MOVE_RIGHT) != 0) {
- cropped.right = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight);
- fixRectAspectW(cropped);
+
+ if (mFixAspectRatio) {
+ if (dx < dy) {
+ dy = getNewHeightForWidthAspect(dx, mAspectWidth, mAspectHeight);
+ } else {
+ dx = getNewWidthForHeightAspect(dy, mAspectWidth, mAspectHeight);
+ }
+ }
+
+ if ((select & MOVE_LEFT) != 0) {
+ cropped.left += dx;
}
- if ((movingEdges & MOVE_BOTTOM) != 0) {
- cropped.bottom = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight);
- fixRectAspectH(cropped);
+ if ((select & MOVE_TOP) != 0) {
+ cropped.top += dy;
+ }
+ if ((select & MOVE_RIGHT) != 0) {
+ cropped.right += dx;
+ }
+ if ((select & MOVE_BOTTOM) != 0) {
+ cropped.bottom += dy;
}
}
Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
@@ -244,24 +342,6 @@ public class ImageCrop extends ImageGeometry {
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 +353,111 @@ public class ImageCrop extends ImageGeometry {
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);
+
+ super.setActionMove(x, y);
+ }
+
+ private void cropSetup() {
+ if (mFixAspectRatio) {
+ RectF cb = getRotatedCropBounds();
+ fixAspectRatio(cb, mAspectWidth, mAspectHeight);
+ RectF cb0 = getUnrotatedCropBounds(cb);
+ setCropBounds(cb0);
} else {
- float dx = x - mTouchCenterX;
- float dy = y - mTouchCenterY;
- mCropOffsetX = dx + mPrevOffsetX;
- mCropOffsetY = dy + mPrevOffsetY;
+ setCropBounds(getLocalCropBounds());
}
- super.setActionMove(x, y);
}
@Override
protected void gainedVisibility() {
- setCropBounds(getLocalCropBounds());
- super.gainedVisibility();
+ cropSetup();
+ mFirstDraw = true;
}
- 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
+ public void resetParameter() {
+ super.resetParameter();
+ cropSetup();
+ }
- 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
+ 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();
-
- RectF scaledCrop = drawCrop(canvas, gPaint, getLocalCropBounds(), scale,
- rotation, mCenterX, mCenterY, mXOffset,
- mYOffset);
-
- boolean notMoving = movingEdges == 0;
- if (((movingEdges & MOVE_TOP) != 0) || notMoving) {
+ drawTransformedBitmap(canvas, image, gPaint, true);
+
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
index 98f892e8c..68df702ea 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -26,7 +26,6 @@ import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -48,10 +47,8 @@ public abstract class ImageGeometry extends ImageSlave {
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 +74,9 @@ public abstract class ImageGeometry extends ImageSlave {
calculateLocalScalingFactorAndOffset();
}
- private float computeScale(float width, float height) {
- float imageWidth = mLocalGeoMetadata.getPhotoBounds().width();
- float imageHeight = mLocalGeoMetadata.getPhotoBounds().height();
+ 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 +85,9 @@ public abstract class ImageGeometry extends ImageSlave {
}
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 +97,7 @@ public abstract class ImageGeometry extends ImageSlave {
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 +113,16 @@ public abstract class ImageGeometry extends ImageSlave {
// 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 +130,62 @@ public abstract class ImageGeometry extends ImageSlave {
}
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,38 +201,18 @@ public abstract class ImageGeometry extends ImageSlave {
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) {
@@ -251,24 +224,6 @@ public abstract class ImageGeometry extends ImageSlave {
}
}
- 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;
- }
- }
-
protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
Path crop = new Path();
crop.moveTo(points[0], points[1]);
@@ -280,26 +235,14 @@ public abstract class ImageGeometry extends ImageSlave {
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 +253,17 @@ public abstract class ImageGeometry extends ImageSlave {
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 +272,6 @@ public abstract class ImageGeometry extends ImageSlave {
protected void gainedVisibility() {
// TODO: Override this stub.
- updateMatrix();
}
protected void lostVisibility() {
@@ -356,8 +293,6 @@ public abstract class ImageGeometry extends ImageSlave {
case (MotionEvent.ACTION_UP):
setActionUp();
saveAndSetPreset();
- Log.v(LOGTAG, "up action");
- logMasterGeo();
break;
case (MotionEvent.ACTION_MOVE):
setActionMove(event.getX(), event.getY());
@@ -405,13 +340,12 @@ public abstract class ImageGeometry extends ImageSlave {
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);
}
@@ -440,100 +374,123 @@ public abstract class ImageGeometry extends ImageSlave {
return new RectF(left, top, right, bottom);
}
- protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
- float rotation, float centerX, float centerY) {
+ 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.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,
- p);
- canvas.drawRect(innerBounds.right, innerBounds.top, dWidth,
- innerBounds.bottom, p);
- canvas.rotate(-rotation, centerX, centerY);
+ canvas.drawBitmap(bitmap, m, paint);
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;
+ 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);
}
- public RectF cropBounds(Bitmap bitmap) {
- Matrix boundsMatrix = computeBoundsMatrix(bitmap);
+ protected RectF straightenBounds() {
RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten());
- RectF transformedBounds = new RectF(bounds);
- boundsMatrix.mapRect(transformedBounds);
- return transformedBounds;
+ Matrix m = getGeoMatrix(bounds, true);
+ m.mapRect(bounds);
+ return bounds;
}
- 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);
-
+ protected void drawStraighten(Canvas canvas, Paint paint) {
+ RectF bounds = straightenBounds();
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.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.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 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 RectF getCropBoundsDisplayed() {
- return getCropBoundsDisplayed(getLocalCropBounds());
+ 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 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, Paint p, RectF innerBounds) {
+ RectF display = new RectF(0, 0, getWidth(), getHeight());
+ drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
+ getHeight() / 2);
}
- 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);
+ 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);
- 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();
}
@Override
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index f4a218472..3fd6d4f85 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -88,6 +88,14 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
private SeekBar mSeekBar = null;
private PanelController mController = null;
+ 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() {
@@ -278,7 +286,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi
canvas.drawRect(textRect, mPaint);
mPaint.setARGB(255, 200, 200, 200);
canvas.drawText(getImagePreset().name(), mTextPadding,
- 10 + mTextPadding, mPaint);
+ 1.5f * mTextPadding, mPaint);
}
if (showControls()) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
index d0c67f783..90986f912 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java
@@ -18,7 +18,7 @@ public class ImageSmallBorder extends ImageSmallFilter {
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);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
index a358e0c9b..a5d99a098 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
@@ -27,12 +27,20 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
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);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index a94d6292f..2fd6b9b35 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -24,6 +24,8 @@ import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
+import com.android.gallery3d.filtershow.imageshow.ImageGeometry.MODES;
+
public class ImageStraighten extends ImageGeometry {
private float mBaseAngle = 0;
@@ -46,11 +48,17 @@ public class ImageStraighten extends ImageGeometry {
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);
+ setCropToStraighten();
}
private float angleFor(float dx, float dy) {
@@ -80,6 +88,17 @@ public class ImageStraighten extends ImageGeometry {
}
@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));
if (getPanelController() != null) {
@@ -98,7 +117,7 @@ public class ImageStraighten extends ImageGeometry {
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/ui/ControlPoint.java b/src/com/android/gallery3d/filtershow/ui/ControlPoint.java
index 68b799a9e..0c08e76fd 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 add(ControlPoint v) {
- return new ControlPoint(x + v.x, y + v.y);
+ public ControlPoint(ControlPoint point) {
+ x = point.x;
+ y = point.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 7f0b0437d..51ed7fb20 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,14 @@ public class ImageButtonTitle extends ImageButton {
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 ImageButtonTitle(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = getContext().obtainStyledAttributes(
@@ -26,6 +33,7 @@ public class ImageButtonTitle extends ImageButton {
mText = a.getString(R.styleable.ImageButtonTitle_android_text);
}
+ @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 660a4fadd..a8445b830 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 @@ public class ImageCurves extends ImageSlave {
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 @@ public class ImageCurves extends ImageSlave {
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 @@ public class ImageCurves extends ImageSlave {
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 class ImageCurves extends ImageSlave {
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 @@ public class ImageCurves extends ImageSlave {
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 @@ public class ImageCurves extends ImageSlave {
}
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 @@ public class ImageCurves extends ImageSlave {
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 @@ public class ImageCurves extends ImageSlave {
}
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 a272d288f..b5c79747d 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,73 +31,290 @@ public class Spline {
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 void addPoint(float x, float y) {
- addPoint(new ControlPoint(x, y));
+ 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;
}
- public void addPoint(ControlPoint v) {
- mPoints.add(v);
- Collections.sort(mPoints);
- delta_t = 1.0f / mPoints.size();
+ 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 ControlPoint getPoint(float t) {
- int p = (int) (t / delta_t);
- int p0 = p - 1;
- int max = mPoints.size() - 1;
+ 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;
- if (p0 < 0) {
- p0 = 0;
- } else if (p0 >= max) {
- p0 = max;
+ // 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);
}
- int p1 = p;
- if (p1 < 0) {
- p1 = 0;
- } else if (p1 >= max) {
- p1 = max;
+ 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);
+ }
}
- int p2 = p + 1;
- if (p2 < 0) {
- p2 = 0;
- } else if (p2 >= max) {
- p2 = max;
+ 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;
}
- int p3 = p + 2;
- if (p3 < 0) {
- p3 = 0;
- } else if (p3 >= max) {
- p3 = max;
+ 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
}
- float lt = (t - delta_t * (float) p) / delta_t;
- return interpolate(lt, mPoints.elementAt(p0),
- mPoints.elementAt(p1), mPoints.elementAt(p2),
- mPoints.elementAt(p3));
+ // 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) {
+ addPoint(new ControlPoint(x, y));
+ }
+
+ public void addPoint(ControlPoint v) {
+ mPoints.add(v);
+ Collections.sort(mPoints);
+ }
+
+ public void deletePoint(int n) {
+ mPoints.remove(n);
+ Collections.sort(mPoints);
}
public int getNbPoints() {
@@ -106,15 +341,6 @@ public class Spline {
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 @@ public class Spline {
}
return spline;
}
+
}
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
index d5337f00d..9b3f29f2b 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
@@ -71,7 +71,7 @@ public class AlbumSetSlotRenderer extends AbstractSlotRenderer {
mWaitLoadingTexture = new ColorTexture(mPlaceholderColor);
mWaitLoadingTexture.setSize(1, 1);
mCameraOverlay = new ResourceTexture(activity,
- R.drawable.frame_overlay_gallery_camera);
+ R.drawable.ic_cameraalbum_overlay);
}
public void setPressedIndex(int index) {
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 7b8e30de4..99cf0571c 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -42,8 +42,8 @@ abstract class BasicTexture implements Texture {
protected int mWidth = UNSPECIFIED;
protected int mHeight = UNSPECIFIED;
- private int mTextureWidth;
- private int mTextureHeight;
+ protected int mTextureWidth;
+ protected int mTextureHeight;
private boolean mHasBorder;
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index bf31bcbdf..741eefbe3 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -19,209 +19,40 @@ package com.android.gallery3d.ui;
import android.graphics.Bitmap;
import android.graphics.RectF;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-import com.android.gallery3d.data.MediaItem;
-
-// This is a ScreenNail wraps a Bitmap. There are some extra functions:
-//
-// - If we need to draw before the bitmap is available, we draw a rectange of
-// placeholder color (gray).
-//
-// - When the the bitmap is available, and we have drawn the placeholder color
-// before, we will do a fade-in animation.
public class BitmapScreenNail implements ScreenNail {
- @SuppressWarnings("unused")
- private static final String TAG = "BitmapScreenNail";
-
- // The duration of the fading animation in milliseconds
- private static final int DURATION = 180;
-
- private static int sMaxSide = 640;
-
- // These are special values for mAnimationStartTime
- private static final long ANIMATION_NOT_NEEDED = -1;
- private static final long ANIMATION_NEEDED = -2;
- private static final long ANIMATION_DONE = -3;
-
- private int mWidth;
- private int mHeight;
- private Bitmap mBitmap;
- private BitmapTexture mTexture;
- private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+ private final BitmapTexture mBitmapTexture;
public BitmapScreenNail(Bitmap bitmap) {
- mWidth = bitmap.getWidth();
- mHeight = bitmap.getHeight();
- mBitmap = bitmap;
- // We create mTexture lazily, so we don't incur the cost if we don't
- // actually need it.
- }
-
- public BitmapScreenNail(int width, int height) {
- setSize(width, height);
- }
-
- // This gets overridden by bitmap_screennail_placeholder
- // in GalleryUtils.initialize
- private static int mPlaceholderColor = 0xFF222222;
- private static boolean mDrawPlaceholder = true;
-
- public static void setPlaceholderColor(int color) {
- mPlaceholderColor = color;
- }
-
- private void setSize(int width, int height) {
- if (width == 0 || height == 0) {
- width = sMaxSide;
- height = sMaxSide * 3 / 4;
- }
- float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
- mWidth = Math.round(scale * width);
- mHeight = Math.round(scale * height);
- }
-
- private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
- if (pool == null || bitmap == null) return;
- pool.recycle(bitmap);
- }
-
- // Combines the two ScreenNails.
- // Returns the used one and recycle the unused one.
- public ScreenNail combine(ScreenNail other) {
- if (other == null) {
- return this;
- }
-
- if (!(other instanceof BitmapScreenNail)) {
- recycle();
- return other;
- }
-
- // Now both are BitmapScreenNail. Move over the information about width,
- // height, and Bitmap, then recycle the other.
- BitmapScreenNail newer = (BitmapScreenNail) other;
- mWidth = newer.mWidth;
- mHeight = newer.mHeight;
- if (newer.mBitmap != null) {
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = newer.mBitmap;
- newer.mBitmap = null;
-
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- }
-
- newer.recycle();
- return this;
- }
-
- public void updatePlaceholderSize(int width, int height) {
- if (mBitmap != null) return;
- if (width == 0 || height == 0) return;
- setSize(width, height);
+ mBitmapTexture = new BitmapTexture(bitmap);
}
@Override
public int getWidth() {
- return mWidth;
+ return mBitmapTexture.getWidth();
}
@Override
public int getHeight() {
- return mHeight;
+ return mBitmapTexture.getHeight();
}
@Override
- public void noDraw() {
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ mBitmapTexture.draw(canvas, x, y, width, height);
}
@Override
- public void recycle() {
- if (mTexture != null) {
- mTexture.recycle();
- mTexture = null;
- }
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = null;
- }
-
- public static void disableDrawPlaceholder() {
- mDrawPlaceholder = false;
- }
-
- public static void enableDrawPlaceholder() {
- mDrawPlaceholder = true;
+ public void noDraw() {
+ // do nothing
}
@Override
- public void draw(GLCanvas canvas, int x, int y, int width, int height) {
- if (mBitmap == null) {
- if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
- mAnimationStartTime = ANIMATION_NEEDED;
- }
- if(mDrawPlaceholder) {
- canvas.fillRect(x, y, width, height, mPlaceholderColor);
- }
- return;
- }
-
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- if (mAnimationStartTime == ANIMATION_NEEDED) {
- mAnimationStartTime = now();
- }
-
- if (isAnimating()) {
- canvas.drawMixed(mTexture, mPlaceholderColor, getRatio(), x, y,
- width, height);
- } else {
- mTexture.draw(canvas, x, y, width, height);
- }
+ public void recycle() {
+ mBitmapTexture.recycle();
}
@Override
public void draw(GLCanvas canvas, RectF source, RectF dest) {
- if (mBitmap == null) {
- canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
- mPlaceholderColor);
- return;
- }
-
- if (mTexture == null) {
- mTexture = new BitmapTexture(mBitmap);
- }
-
- canvas.drawTexture(mTexture, source, dest);
- }
-
- public boolean isAnimating() {
- if (mAnimationStartTime < 0) return false;
- if (now() - mAnimationStartTime >= DURATION) {
- mAnimationStartTime = ANIMATION_DONE;
- return false;
- }
- return true;
- }
-
- private static long now() {
- return AnimationTime.get();
- }
-
- private float getRatio() {
- float r = (float)(now() - mAnimationStartTime) / DURATION;
- return Utils.clamp(1.0f - r, 0.0f, 1.0f);
- }
-
- public boolean isShowingPlaceholder() {
- return (mBitmap == null) || isAnimating();
- }
-
- public static void setMaxSide(int size) {
- sMaxSide = size;
+ canvas.drawTexture(mBitmapTexture, source, dest);
}
}
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index e3a32ef08..6f8baef7e 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -99,6 +99,13 @@ public interface GLCanvas {
public void drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int w, int h);
+ // Draw a region of a texture and a specified color to the specified
+ // rectangle. The actual color used is from * (1 - ratio) + to * ratio.
+ // The region of the texture is defined by parameter "src". The target
+ // rectangle is specified by parameter "target".
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target);
+
// Gets the underlying GL instance. This is used only when direct access to
// GL is needed.
public GL11 getGLInstance();
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index d83daf3e4..45903b3cd 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -415,7 +415,7 @@ public class GLCanvasImpl implements GLCanvas {
// This function changes the source coordinate to the texture coordinates.
// It also clips the source and target coordinates if it is beyond the
// bound of the texture.
- private void convertCoordinate(RectF source, RectF target,
+ private static void convertCoordinate(RectF source, RectF target,
BasicTexture texture) {
int width = texture.getWidth();
@@ -465,23 +465,7 @@ public class GLCanvasImpl implements GLCanvas {
color[3] = alpha;
}
- private void drawMixed(BasicTexture from, int toColor,
- float ratio, int x, int y, int width, int height, float alpha) {
- // change from 0 to 0.01f to prevent getting divided by zero below
- if (ratio <= 0.01f) {
- drawTexture(from, x, y, width, height, alpha);
- return;
- } else if (ratio >= 1) {
- fillRect(x, y, width, height, toColor);
- return;
- }
-
- mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
- || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
-
- final GL11 gl = mGL;
- if (!bindTexture(from)) return;
-
+ private void setMixedColor(int toColor, float ratio, float alpha) {
//
// The formula we want:
// alpha * ((1 - ratio) * from + ratio * to)
@@ -495,9 +479,6 @@ public class GLCanvasImpl implements GLCanvas {
float combo = alpha * (1 - ratio);
float scale = alpha * ratio / (1 - combo);
- // Interpolate the RGB and alpha values between both textures.
- mGLState.setTexEnvMode(GL11.GL_COMBINE);
-
// Specify the interpolation factor via the alpha component of
// GL_TEXTURE_ENV_COLORs.
// RGB component are get from toColor and will used as SRC1
@@ -505,6 +486,7 @@ public class GLCanvasImpl implements GLCanvas {
setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
((toColor >>> 8) & 0xff) * colorScale,
(toColor & 0xff) * colorScale, combo);
+ GL11 gl = mGL;
gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
@@ -522,6 +504,64 @@ public class GLCanvasImpl implements GLCanvas {
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+ }
+
+ @Override
+ public void drawMixed(BasicTexture from, int toColor, float ratio,
+ RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) return;
+
+ if (ratio <= 0.01f) {
+ drawTexture(from, source, target);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+ return;
+ }
+
+ float alpha = mAlpha;
+
+ // Copy the input to avoid changing it.
+ mDrawTextureSourceRect.set(source);
+ mDrawTextureTargetRect.set(target);
+ source = mDrawTextureSourceRect;
+ target = mDrawTextureTargetRect;
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ if (!bindTexture(from)) return;
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ setMixedColor(toColor, ratio, alpha);
+ convertCoordinate(source, target, from);
+ setTextureCoords(source);
+ textureRect(target.left, target.top, target.width(), target.height());
+ mGLState.setTexEnvMode(GL11.GL_REPLACE);
+ }
+
+ private void drawMixed(BasicTexture from, int toColor,
+ float ratio, int x, int y, int width, int height, float alpha) {
+ // change from 0 to 0.01f to prevent getting divided by zero below
+ if (ratio <= 0.01f) {
+ drawTexture(from, x, y, width, height, alpha);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(x, y, width, height, toColor);
+ return;
+ }
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ final GL11 gl = mGL;
+ if (!bindTexture(from)) return;
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ setMixedColor(toColor, ratio, alpha);
+
drawBoundTexture(from, x, y, width, height);
mGLState.setTexEnvMode(GL11.GL_REPLACE);
}
diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java
index 1651b4361..13b610b23 100644
--- a/src/com/android/gallery3d/ui/GLRoot.java
+++ b/src/com/android/gallery3d/ui/GLRoot.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.ui;
+import android.content.Context;
import android.graphics.Matrix;
import com.android.gallery3d.anim.CanvasAnimation;
@@ -45,4 +46,6 @@ public interface GLRoot {
public void freeze();
public void unfreeze();
public void setLightsOutMode(boolean enabled);
+
+ public Context getContext();
}
diff --git a/src/com/android/gallery3d/ui/GLView.java b/src/com/android/gallery3d/ui/GLView.java
index 3924c6e9d..b91b71212 100644
--- a/src/com/android/gallery3d/ui/GLView.java
+++ b/src/com/android/gallery3d/ui/GLView.java
@@ -21,6 +21,7 @@ import android.os.SystemClock;
import android.view.MotionEvent;
import com.android.gallery3d.anim.CanvasAnimation;
+import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.common.Utils;
import java.util.ArrayList;
@@ -77,6 +78,11 @@ public class GLView {
protected int mScrollHeight = 0;
protected int mScrollWidth = 0;
+ public static final int ANIM_TIME_OPENING = 400;
+ private RawTexture mFadeOutTexture;
+ private float [] mBackgroundColor;
+ private StateTransitionAnimation mTransition = new StateTransitionAnimation(ANIM_TIME_OPENING);
+
public void startAnimation(CanvasAnimation animation) {
GLRoot root = getGLRoot();
if (root == null) throw new IllegalStateException();
@@ -217,13 +223,46 @@ public class GLView {
}
protected void render(GLCanvas canvas) {
+ if (mTransition.calculate(AnimationTime.get())) invalidate();
+ canvas.save();
renderBackground(canvas);
+ if (mTransition.isActive()) mTransition.applyForegroundTransformation(this, canvas);
for (int i = 0, n = getComponentCount(); i < n; ++i) {
renderChild(canvas, getComponent(i));
}
+ canvas.restore();
+ }
+
+ public void setFadeOutTexture(RawTexture texture) {
+ mFadeOutTexture = texture;
+ if (mFadeOutTexture != null) {
+ TiledScreenNail.disableDrawPlaceholder();
+ }
+ mTransition.start();
+ }
+
+ public float [] getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ public void setBackgroundColor(float [] color) {
+ mBackgroundColor = color;
}
protected void renderBackground(GLCanvas view) {
+ if (mBackgroundColor != null) {
+ view.clearBuffer(mBackgroundColor);
+ }
+ if (mFadeOutTexture != null) {
+ if (!mTransition.isActive()) {
+ mFadeOutTexture.recycle();
+ mFadeOutTexture = null;
+ TiledScreenNail.enableDrawPlaceholder();
+ } else {
+ mTransition.applyBackground(this, view, mFadeOutTexture);
+ return;
+ }
+ }
}
protected void renderChild(GLCanvas canvas, GLView component) {
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 5978159e4..edd7c72e3 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -17,6 +17,7 @@
package com.android.gallery3d.ui;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -546,10 +547,20 @@ public class PhotoView extends GLView {
}
private int getPanoramaRotation() {
- // Panorama only support rotations of 0 and 90, so if it is greater
- // than that flip the output surface texture to compensate
- if (mDisplayRotation > 180)
+ // This function is magic
+ // The issue here is that Pano makes bad assumptions about rotation and
+ // orientation. The first is it assumes only two rotations are possible,
+ // 0 and 90. Thus, if display rotation is >= 180, we invert the output.
+ // The second is that it assumes landscape is a 90 rotation from portrait,
+ // however on landscape devices this is not true. Thus, if we are in portrait
+ // on a landscape device, we need to invert the output
+ int orientation = getGLRoot().getContext().getResources().getConfiguration().orientation;
+ boolean invertPortrait = (orientation == Configuration.ORIENTATION_PORTRAIT
+ && (mDisplayRotation == 90 || mDisplayRotation == 270));
+ boolean invert = (mDisplayRotation >= 180);
+ if (invert != invertPortrait) {
return (mCompensation + 180) % 360;
+ }
return mCompensation;
}
@@ -839,8 +850,8 @@ public class PhotoView extends GLView {
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
@Override
@@ -1781,8 +1792,8 @@ public class PhotoView extends GLView {
MediaItem item = mModel.getMediaItem(i);
if (item == null) continue;
ScreenNail sc = mModel.getScreenNail(i);
- if (!(sc instanceof BitmapScreenNail)
- || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
+ if (!(sc instanceof TiledScreenNail)
+ || ((TiledScreenNail) sc).isShowingPlaceholder()) continue;
// Now, sc is BitmapScreenNail and is not showing placeholder
Rect rect = new Rect(getPhotoRect(i));
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index fffa7e038..0111847eb 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -67,7 +67,7 @@ class PositionController {
SNAPBACK_ANIMATION_TIME, // ANIM_KIND_SNAPBACK
400, // ANIM_KIND_SLIDE
300, // ANIM_KIND_ZOOM
- PhotoPage.ANIM_TIME_OPENING, // ANIM_KIND_OPENING
+ GLView.ANIM_TIME_OPENING, // ANIM_KIND_OPENING
0, // ANIM_KIND_FLING (the duration is calculated dynamically)
0, // ANIM_KIND_FLING_X (see the comment above)
0, // ANIM_KIND_DELETE (the duration is calculated dynamically)
diff --git a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
index 812e831e1..7cab753c3 100644
--- a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
+++ b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java
@@ -6,7 +6,7 @@ import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
public class PreparePageFadeoutTexture implements OnGLIdleListener {
- private static final long TIMEOUT = FadeTexture.DURATION;
+ private static final long TIMEOUT = 500;
public static final String KEY_FADE_TEXTURE = "fade_texture";
private RawTexture mTexture;
diff --git a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
index 1930e3877..7cb894845 100644
--- a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
+++ b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java
@@ -109,6 +109,7 @@ public abstract class SurfaceTextureScreenNail implements ScreenNail,
canvas.translate(cx, cy);
canvas.scale(1, -1, 1);
canvas.translate(-cx, -cy);
+ updateTransformMatrix(mTransform);
canvas.drawTexture(mExtTexture, mTransform, x, y, width, height);
canvas.restore();
}
@@ -119,6 +120,8 @@ public abstract class SurfaceTextureScreenNail implements ScreenNail,
throw new UnsupportedOperationException();
}
+ protected void updateTransformMatrix(float[] matrix) {}
+
@Override
abstract public void noDraw();
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index 5ce06bec4..8f26981fe 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -462,8 +462,8 @@ public class TileImageView extends GLView {
}
private boolean isScreenNailAnimating() {
- return (mScreenNail instanceof BitmapScreenNail)
- && ((BitmapScreenNail) mScreenNail).isAnimating();
+ return (mScreenNail instanceof TiledScreenNail)
+ && ((TiledScreenNail) mScreenNail).isAnimating();
}
private void uploadBackgroundTiles(GLCanvas canvas) {
@@ -575,8 +575,10 @@ public class TileImageView extends GLView {
}
if (tile == null) break;
if (!tile.isContentValid()) {
+ boolean hasBeenLoaded = tile.isLoaded();
Utils.assertTrue(tile.mTileState == STATE_DECODED);
tile.updateContent(canvas);
+ if (!hasBeenLoaded) tile.draw(canvas, 0, 0);
--quota;
}
}
@@ -621,7 +623,6 @@ public class TileImageView extends GLView {
}
}
- // TODO: avoid drawing the unused part of the textures.
static boolean drawTile(
Tile tile, GLCanvas canvas, RectF source, RectF target) {
while (true) {
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 08d337921..45e2ce218 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -40,51 +40,25 @@ public class TileImageViewAdapter implements TileImageView.Model {
public TileImageViewAdapter() {
}
- public TileImageViewAdapter(
- Bitmap bitmap, BitmapRegionDecoder regionDecoder) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mRegionDecoder = regionDecoder;
- mImageWidth = regionDecoder.getWidth();
- mImageHeight = regionDecoder.getHeight();
- mLevelCount = calculateLevelCount();
- }
-
public synchronized void clear() {
- updateScreenNail(null, false);
+ mScreenNail = null;
mImageWidth = 0;
mImageHeight = 0;
mLevelCount = 0;
mRegionDecoder = null;
}
- public synchronized void setScreenNail(Bitmap bitmap, int width, int height) {
- Utils.checkNotNull(bitmap);
- updateScreenNail(new BitmapScreenNail(bitmap), true);
- mImageWidth = width;
- mImageHeight = height;
- mRegionDecoder = null;
- mLevelCount = 0;
- }
-
+ // Caller is responsible to recycle the ScreenNail
public synchronized void setScreenNail(
ScreenNail screenNail, int width, int height) {
Utils.checkNotNull(screenNail);
- updateScreenNail(screenNail, false);
+ mScreenNail = screenNail;
mImageWidth = width;
mImageHeight = height;
mRegionDecoder = null;
mLevelCount = 0;
}
- private void updateScreenNail(ScreenNail screenNail, boolean own) {
- if (mScreenNail != null && mOwnScreenNail) {
- mScreenNail.recycle();
- }
- mScreenNail = screenNail;
- mOwnScreenNail = own;
- }
-
public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) {
mRegionDecoder = Utils.checkNotNull(decoder);
mImageWidth = decoder.getWidth();
diff --git a/src/com/android/gallery3d/ui/TiledScreenNail.java b/src/com/android/gallery3d/ui/TiledScreenNail.java
new file mode 100644
index 000000000..d2b34e3bf
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledScreenNail.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
+import com.android.gallery3d.data.MediaItem;
+
+// This is a ScreenNail wraps a Bitmap. There are some extra functions:
+//
+// - If we need to draw before the bitmap is available, we draw a rectange of
+// placeholder color (gray).
+//
+// - When the the bitmap is available, and we have drawn the placeholder color
+// before, we will do a fade-in animation.
+public class TiledScreenNail implements ScreenNail {
+ @SuppressWarnings("unused")
+ private static final String TAG = "TiledScreenNail";
+
+ // The duration of the fading animation in milliseconds
+ private static final int DURATION = 180;
+
+ private static int sMaxSide = 640;
+
+ // These are special values for mAnimationStartTime
+ private static final long ANIMATION_NOT_NEEDED = -1;
+ private static final long ANIMATION_NEEDED = -2;
+ private static final long ANIMATION_DONE = -3;
+
+ private int mWidth;
+ private int mHeight;
+ private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
+
+ private Bitmap mBitmap;
+ private TiledTexture mTexture;
+
+ public TiledScreenNail(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ mBitmap = bitmap;
+ mTexture = new TiledTexture(bitmap);
+ }
+
+ public TiledScreenNail(int width, int height) {
+ setSize(width, height);
+ }
+
+ // This gets overridden by bitmap_screennail_placeholder
+ // in GalleryUtils.initialize
+ private static int mPlaceholderColor = 0xFF222222;
+ private static boolean mDrawPlaceholder = true;
+
+ public static void setPlaceholderColor(int color) {
+ mPlaceholderColor = color;
+ }
+
+ private void setSize(int width, int height) {
+ if (width == 0 || height == 0) {
+ width = sMaxSide;
+ height = sMaxSide * 3 / 4;
+ }
+ float scale = Math.min(1, (float) sMaxSide / Math.max(width, height));
+ mWidth = Math.round(scale * width);
+ mHeight = Math.round(scale * height);
+ }
+
+ private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
+ if (pool == null || bitmap == null) return;
+ pool.recycle(bitmap);
+ }
+
+ // Combines the two ScreenNails.
+ // Returns the used one and recycle the unused one.
+ public ScreenNail combine(ScreenNail other) {
+ if (other == null) {
+ return this;
+ }
+
+ if (!(other instanceof TiledScreenNail)) {
+ recycle();
+ return other;
+ }
+
+ // Now both are TiledScreenNail. Move over the information about width,
+ // height, and Bitmap, then recycle the other.
+ TiledScreenNail newer = (TiledScreenNail) other;
+ mWidth = newer.mWidth;
+ mHeight = newer.mHeight;
+ if (newer.mTexture != null) {
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ if (mTexture != null) mTexture.recycle();
+ mBitmap = newer.mBitmap;
+ mTexture = newer.mTexture;
+ newer.mBitmap = null;
+ newer.mTexture = null;
+ }
+ newer.recycle();
+ return this;
+ }
+
+ public void updatePlaceholderSize(int width, int height) {
+ if (mBitmap != null) return;
+ if (width == 0 || height == 0) return;
+ setSize(width, height);
+ }
+
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void noDraw() {
+ }
+
+ @Override
+ public void recycle() {
+ if (mTexture != null) {
+ mTexture.recycle();
+ mTexture = null;
+ }
+ recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ mBitmap = null;
+ }
+
+ public static void disableDrawPlaceholder() {
+ mDrawPlaceholder = false;
+ }
+
+ public static void enableDrawPlaceholder() {
+ mDrawPlaceholder = true;
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ if (mTexture == null || !mTexture.isReady()) {
+ if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
+ mAnimationStartTime = ANIMATION_NEEDED;
+ }
+ if(mDrawPlaceholder) {
+ canvas.fillRect(x, y, width, height, mPlaceholderColor);
+ }
+ return;
+ }
+
+ if (mAnimationStartTime == ANIMATION_NEEDED) {
+ mAnimationStartTime = AnimationTime.get();
+ }
+
+ if (isAnimating()) {
+ mTexture.drawMixed(canvas, mPlaceholderColor, getRatio(), x, y,
+ width, height);
+ } else {
+ mTexture.draw(canvas, x, y, width, height);
+ }
+ }
+
+ @Override
+ public void draw(GLCanvas canvas, RectF source, RectF dest) {
+ if (mTexture == null || !mTexture.isReady()) {
+ canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
+ mPlaceholderColor);
+ return;
+ }
+
+ mTexture.draw(canvas, source, dest);
+ }
+
+ public boolean isAnimating() {
+ if (mAnimationStartTime < 0) return false;
+ if (AnimationTime.get() - mAnimationStartTime >= DURATION) {
+ mAnimationStartTime = ANIMATION_DONE;
+ return false;
+ }
+ return true;
+ }
+
+ private float getRatio() {
+ float r = (float) (AnimationTime.get() - mAnimationStartTime) / DURATION;
+ return Utils.clamp(1.0f - r, 0.0f, 1.0f);
+ }
+
+ public boolean isShowingPlaceholder() {
+ return (mBitmap == null) || isAnimating();
+ }
+
+ public TiledTexture getTexture() {
+ return mTexture;
+ }
+
+ public static void setMaxSide(int size) {
+ sMaxSide = size;
+ }
+}
diff --git a/src/com/android/gallery3d/ui/TiledTexture.java b/src/com/android/gallery3d/ui/TiledTexture.java
new file mode 100644
index 000000000..6e9ad9ea8
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TiledTexture.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+
+import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+// This class is similar to BitmapTexture, except the bitmap is
+// split into tiles. By doing so, we may increase the time required to
+// upload the whole bitmap but we reduce the time of uploading each tile
+// so it make the animation more smooth and prevents jank.
+public class TiledTexture {
+ private static final int CONTENT_SIZE = 254;
+ private static final int BORDER_SIZE = 1;
+ private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
+ private static final int INIT_CAPACITY = 8;
+
+ private static Tile sFreeTileHead = null;
+ private static final Object sFreeTileLock = new Object();
+
+ private static Bitmap sUploadBitmap;
+ private static Canvas sCanvas;
+ private static Paint sPaint;
+
+ private int mUploadIndex = 0;
+
+ private final Tile[] mTiles;
+ private final int mWidth;
+ private final int mHeight;
+ private final RectF mSrcRect = new RectF();
+ private final RectF mDestRect = new RectF();
+
+ public static class Uploader implements OnGLIdleListener {
+ private final ArrayDeque<TiledTexture> mTextures =
+ new ArrayDeque<TiledTexture>(INIT_CAPACITY);
+
+ private final GLRoot mGlRoot;
+ private boolean mIsQueued = false;
+
+ public Uploader(GLRoot glRoot) {
+ mGlRoot = glRoot;
+ }
+
+ public synchronized void clear() {
+ mTextures.clear();
+ }
+
+ public synchronized void addTexture(TiledTexture t) {
+ if (t.isReady()) return;
+ mTextures.addLast(t);
+
+ if (mIsQueued) return;
+ mIsQueued = true;
+ mGlRoot.addOnGLIdleListener(this);
+ }
+
+
+ @Override
+ public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
+ ArrayDeque<TiledTexture> deque = mTextures;
+ synchronized (this) {
+ if (!deque.isEmpty()) {
+ TiledTexture t = deque.peekFirst();
+ if (t.uploadNextTile(canvas)) {
+ deque.removeFirst();
+ mGlRoot.requestRender();
+ }
+ }
+ mIsQueued = !mTextures.isEmpty();
+
+ // return true to keep this listener in the queue
+ return mIsQueued;
+ }
+ }
+ }
+
+ private static class Tile extends UploadedTexture {
+ public int offsetX;
+ public int offsetY;
+ public Bitmap bitmap;
+ public Tile nextFreeTile;
+ public int contentWidth;
+ public int contentHeight;
+
+ @Override
+ public void setSize(int width, int height) {
+ contentWidth = width;
+ contentHeight = height;
+ mWidth = width + 2 * BORDER_SIZE;
+ mHeight = height + 2 * BORDER_SIZE;
+ mTextureWidth = TILE_SIZE;
+ mTextureHeight = TILE_SIZE;
+ }
+
+ @Override
+ protected Bitmap onGetBitmap() {
+ int x = BORDER_SIZE - offsetX;
+ int y = BORDER_SIZE - offsetY;
+ int r = bitmap.getWidth() - x;
+ int b = bitmap.getHeight() - y ;
+ sCanvas.drawBitmap(bitmap, x, y, null);
+ bitmap = null;
+
+ // draw borders if need
+ if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
+ if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
+ if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
+ if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
+
+ return sUploadBitmap;
+ }
+
+ @Override
+ protected void onFreeBitmap(Bitmap bitmap) {
+ // do nothing
+ }
+ }
+
+ private static void freeTile(Tile tile) {
+ tile.invalidateContent();
+ tile.bitmap = null;
+ synchronized (sFreeTileLock) {
+ tile.nextFreeTile = sFreeTileHead;
+ sFreeTileHead = tile;
+ }
+ }
+
+ private static Tile obtainTile() {
+ synchronized (sFreeTileLock) {
+ Tile result = sFreeTileHead;
+ if (result == null) return new Tile();
+ sFreeTileHead = result.nextFreeTile;
+ result.nextFreeTile = null;
+ return result;
+ }
+ }
+
+ private boolean uploadNextTile(GLCanvas canvas) {
+ if (mUploadIndex == mTiles.length) return true;
+ Tile next = mTiles[mUploadIndex++];
+ boolean hasBeenLoad = next.isLoaded();
+ next.updateContent(canvas);
+
+ // It will take some time for a texture to be drawn for the first
+ // time. When scrolling, we need to draw several tiles on the screen
+ // at the same time. It may cause a UI jank even these textures has
+ // been uploaded.
+ if (!hasBeenLoad) next.draw(canvas, 0, 0);
+ return mUploadIndex == mTiles.length;
+ }
+
+ public TiledTexture(Bitmap bitmap) {
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ ArrayList<Tile> list = new ArrayList<Tile>();
+
+ for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
+ for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
+ Tile tile = obtainTile();
+ tile.offsetX = x;
+ tile.offsetY = y;
+ tile.bitmap = bitmap;
+ tile.setSize(
+ Math.min(CONTENT_SIZE, mWidth - x),
+ Math.min(CONTENT_SIZE, mHeight - y));
+ list.add(tile);
+ }
+ }
+ mTiles = list.toArray(new Tile[list.size()]);
+ }
+
+ public boolean isReady() {
+ return mUploadIndex == mTiles.length;
+ }
+
+ public void recycle() {
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ freeTile(mTiles[i]);
+ }
+ }
+
+ public static void freeResources() {
+ sUploadBitmap = null;
+ sCanvas = null;
+ sPaint = null;
+ }
+
+ public static void prepareResources() {
+ sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
+ sCanvas = new Canvas(sUploadBitmap);
+ sPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ sPaint.setColor(Color.TRANSPARENT);
+ sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ }
+
+ // We want to draw the "source" on the "target".
+ // This method is to find the "output" rectangle which is
+ // the corresponding area of the "src".
+ // (x,y) target
+ // (x0,y0) source +---------------+
+ // +----------+ | |
+ // | src | | output |
+ // | +--+ | linear map | +----+ |
+ // | +--+ | ----------> | | | |
+ // | | by (scaleX, scaleY) | +----+ |
+ // +----------+ | |
+ // Texture +---------------+
+ // Canvas
+ private static void mapRect(RectF output,
+ RectF src, float x0, float y0, float x, float y, float scaleX,
+ float scaleY) {
+ output.set(x + (src.left - x0) * scaleX,
+ y + (src.top - y0) * scaleY,
+ x + (src.right - x0) * scaleX,
+ y + (src.bottom - y0) * scaleY);
+ }
+
+ // Draws a mixed color of this texture and a specified color onto the
+ // a rectangle. The used color is: from * (1 - ratio) + to * ratio.
+ public void drawMixed(GLCanvas canvas, int color, float ratio,
+ int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws the texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float scaleX = (float) width / mWidth ;
+ float scaleY = (float) height / mHeight;
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, mSrcRect, mDestRect);
+ }
+ }
+
+ // Draws a sub region of this texture on to the specified rectangle.
+ public void draw(GLCanvas canvas, RectF source, RectF target) {
+ RectF src = mSrcRect;
+ RectF dest = mDestRect;
+ float x0 = source.left;
+ float y0 = source.top;
+ float x = target.left;
+ float y = target.top;
+ float scaleX = target.width() / source.width();
+ float scaleY = target.height() / source.height();
+
+ for (int i = 0, n = mTiles.length; i < n; ++i) {
+ Tile t = mTiles[i];
+ src.set(0, 0, t.contentWidth, t.contentHeight);
+ src.offset(t.offsetX, t.offsetY);
+ if (!src.intersect(source)) continue;
+ mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
+ src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
+ canvas.drawTexture(t, src, dest);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/util/GalleryUtils.java b/src/com/android/gallery3d/util/GalleryUtils.java
index 195552116..62f22355e 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.app.PackagesMonitor;
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 @@ public class GalleryUtils {
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 @@ public class GalleryUtils {
// 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/tests/src/com/android/gallery3d/ui/GLCanvasStub.java b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
index 2f2d753e0..5a08b8599 100644
--- a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
+++ b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
@@ -83,4 +83,6 @@ public class GLCanvasStub implements GLCanvas {
public void dumpStatisticsAndClear() {}
public void beginRenderTarget(RawTexture texture) {}
public void endRenderTarget() {}
+ public void drawMixed(BasicTexture from, int toColor,
+ float ratio, RectF src, RectF target) {}
}
diff --git a/tests/src/com/android/gallery3d/ui/GLRootMock.java b/tests/src/com/android/gallery3d/ui/GLRootMock.java
index 467edfc7f..b1c4355d0 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 class GLRootMock implements GLRoot {
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 0f3a00164..7f134dbf1 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 class GLRootStub implements GLRoot {
public void freeze() {}
public void unfreeze() {}
public void setLightsOutMode(boolean enabled) {}
+ public Context getContext() { return null; }
}