summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/gallery3d/app/AbstractGalleryActivity.java50
-rw-r--r--src/com/android/gallery3d/app/ActivityState.java37
-rw-r--r--src/com/android/gallery3d/app/AlbumDataLoader.java3
-rw-r--r--src/com/android/gallery3d/app/AlbumPage.java29
-rw-r--r--src/com/android/gallery3d/app/AlbumSetPage.java19
-rw-r--r--src/com/android/gallery3d/app/BatchService.java45
-rw-r--r--src/com/android/gallery3d/app/CommonControllerOverlay.java2
-rw-r--r--src/com/android/gallery3d/app/CropImage.java1040
-rw-r--r--src/com/android/gallery3d/app/FilmstripPage.java21
-rw-r--r--src/com/android/gallery3d/app/Gallery.java11
-rw-r--r--src/com/android/gallery3d/app/GalleryActionBar.java16
-rw-r--r--src/com/android/gallery3d/app/MovieActivity.java22
-rw-r--r--src/com/android/gallery3d/app/MoviePlayer.java3
-rw-r--r--src/com/android/gallery3d/app/MuteVideo.java104
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java28
-rw-r--r--src/com/android/gallery3d/app/PickerActivity.java10
-rw-r--r--src/com/android/gallery3d/app/SinglePhotoDataAdapter.java1
-rw-r--r--src/com/android/gallery3d/app/SinglePhotoPage.java21
-rw-r--r--src/com/android/gallery3d/app/StateManager.java4
-rw-r--r--src/com/android/gallery3d/app/TrimControllerOverlay.java51
-rw-r--r--src/com/android/gallery3d/app/TrimVideo.java150
-rw-r--r--src/com/android/gallery3d/app/VideoUtils.java (renamed from src/com/android/gallery3d/app/TrimVideoUtils.java)58
-rw-r--r--src/com/android/gallery3d/app/Wallpaper.java23
-rw-r--r--src/com/android/gallery3d/data/Exif.java161
-rw-r--r--src/com/android/gallery3d/data/LocalAlbum.java4
-rw-r--r--src/com/android/gallery3d/data/LocalMergeAlbum.java5
-rw-r--r--src/com/android/gallery3d/data/LocalVideo.java2
-rw-r--r--src/com/android/gallery3d/data/MediaDetails.java60
-rw-r--r--src/com/android/gallery3d/data/MediaObject.java1
-rw-r--r--src/com/android/gallery3d/filtershow/CropExtras.java121
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java257
-rw-r--r--src/com/android/gallery3d/filtershow/PanelController.java118
-rw-r--r--src/com/android/gallery3d/filtershow/cache/ImageLoader.java279
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilter.java37
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java21
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java45
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java157
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java20
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java11
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java23
-rw-r--r--src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java50
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java340
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/CropMath.java191
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java76
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java98
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java731
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java206
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java148
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java20
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java18
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java5
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java50
-rw-r--r--src/com/android/gallery3d/filtershow/tools/BitmapTask.java68
-rw-r--r--src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java22
-rw-r--r--src/com/android/gallery3d/gadget/WidgetConfigure.java19
-rw-r--r--src/com/android/gallery3d/ui/ActionModeHandler.java30
-rw-r--r--src/com/android/gallery3d/ui/BasicTexture.java5
-rw-r--r--src/com/android/gallery3d/ui/CropView.java801
-rw-r--r--src/com/android/gallery3d/ui/DetailsHelper.java12
-rw-r--r--src/com/android/gallery3d/ui/DialogDetailsView.java46
-rw-r--r--src/com/android/gallery3d/ui/ExtTexture.java34
-rw-r--r--src/com/android/gallery3d/ui/GLCanvas.java200
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java154
-rw-r--r--src/com/android/gallery3d/ui/GLES20Canvas.java1068
-rw-r--r--src/com/android/gallery3d/ui/GLId.java29
-rw-r--r--src/com/android/gallery3d/ui/GLIdImpl.java68
-rw-r--r--src/com/android/gallery3d/ui/GLRootView.java4
-rw-r--r--src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java31
-rw-r--r--src/com/android/gallery3d/ui/GestureRecognizer.java4
-rw-r--r--src/com/android/gallery3d/ui/MenuExecutor.java25
-rw-r--r--src/com/android/gallery3d/ui/NinePatchTexture.java42
-rw-r--r--src/com/android/gallery3d/ui/PhotoView.java20
-rw-r--r--src/com/android/gallery3d/ui/PopupList.java2
-rw-r--r--src/com/android/gallery3d/ui/PositionController.java8
-rw-r--r--src/com/android/gallery3d/ui/RawTexture.java40
-rw-r--r--src/com/android/gallery3d/ui/SlideshowView.java9
-rw-r--r--src/com/android/gallery3d/ui/UploadedTexture.java53
-rw-r--r--src/com/android/gallery3d/util/AccessibilityUtils.java54
-rw-r--r--src/com/android/gallery3d/util/IntArray.java5
-rw-r--r--src/com/android/gallery3d/util/SaveVideoFileInfo.java29
-rw-r--r--src/com/android/gallery3d/util/SaveVideoFileUtils.java141
89 files changed, 4622 insertions, 3500 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index 88ac028e1..081b87aee 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -17,22 +17,25 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.os.IBinder;
import android.view.Window;
import android.view.WindowManager;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.BitmapPool;
@@ -40,10 +43,10 @@ import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLRootView;
-import com.android.gallery3d.util.ThreadPool;
import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
+import com.android.gallery3d.util.ThreadPool;
-public class AbstractGalleryActivity extends Activity implements GalleryContext {
+public class AbstractGalleryActivity extends SherlockActivity implements GalleryContext {
@SuppressWarnings("unused")
private static final String TAG = "AbstractGalleryActivity";
private GLRootView mGLRootView;
@@ -71,6 +74,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
getWindow().setBackgroundDrawable(null);
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
+ doBindBatchService();
}
@Override
@@ -237,6 +241,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
} finally {
mGLRootView.unlockRenderThread();
}
+ doUnbindBatchService();
}
@Override
@@ -308,4 +313,39 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
return (getWindow().getAttributes().flags
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
}
+
+ private BatchService mBatchService;
+ private boolean mBatchServiceIsBound = false;
+ private ServiceConnection mBatchServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mBatchService = ((BatchService.LocalBinder)service).getService();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mBatchService = null;
+ }
+ };
+
+ private void doBindBatchService() {
+ bindService(new Intent(this, BatchService.class), mBatchServiceConnection, Context.BIND_AUTO_CREATE);
+ mBatchServiceIsBound = true;
+ }
+
+ private void doUnbindBatchService() {
+ if (mBatchServiceIsBound) {
+ // Detach our existing connection.
+ unbindService(mBatchServiceConnection);
+ mBatchServiceIsBound = false;
+ }
+ }
+
+ public ThreadPool getBatchServiceThreadPoolIfAvailable() {
+ if (mBatchServiceIsBound && mBatchService != null) {
+ return mBatchService.getThreadPool();
+ } else {
+ // Fall back on the old behavior if for some reason the
+ // service is not available.
+ return getThreadPool();
+ }
+ }
}
diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java
index cdd91ff4d..b2e39b1cb 100644
--- a/src/com/android/gallery3d/app/ActivityState.java
+++ b/src/com/android/gallery3d/app/ActivityState.java
@@ -16,24 +16,23 @@
package com.android.gallery3d.app;
-import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.BatteryManager;
import android.os.Bundle;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.view.Window;
import android.view.WindowManager;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.ui.GLView;
@@ -62,9 +61,6 @@ abstract public class ActivityState {
public Intent resultData;
}
- protected boolean mHapticsEnabled;
- private ContentResolver mContentResolver;
-
private boolean mDestroyed = false;
private boolean mPlugged = false;
boolean mIsFinishing = false;
@@ -92,7 +88,6 @@ abstract public class ActivityState {
void initialize(AbstractGalleryActivity activity, Bundle data) {
mActivity = activity;
mData = data;
- mContentResolver = activity.getAndroidContext().getContentResolver();
}
public Bundle getData() {
@@ -175,15 +170,20 @@ abstract public class ActivityState {
protected void transitionOnNextPause(Class<? extends ActivityState> outgoing,
Class<? extends ActivityState> incoming, StateTransitionAnimation.Transition hint) {
- if (outgoing == PhotoPage.class && incoming == AlbumPage.class) {
+ if (outgoing == SinglePhotoPage.class && incoming == AlbumPage.class) {
mNextTransition = StateTransitionAnimation.Transition.Outgoing;
- } else if (outgoing == AlbumPage.class && incoming == PhotoPage.class) {
+ } else if (outgoing == AlbumPage.class && incoming == SinglePhotoPage.class) {
mNextTransition = StateTransitionAnimation.Transition.PhotoIncoming;
} else {
mNextTransition = hint;
}
}
+ protected void performHapticFeedback(int feedbackConstant) {
+ mActivity.getWindow().getDecorView().performHapticFeedback(feedbackConstant,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+
protected void onPause() {
if (0 != (mFlags & FLAG_SCREEN_ON_WHEN_PLUGGED)) {
((Activity) mActivity).unregisterReceiver(mPowerIntentReceiver);
@@ -198,7 +198,7 @@ abstract public class ActivityState {
// should only be called by StateManager
void resume() {
AbstractGalleryActivity activity = mActivity;
- ActionBar actionBar = activity.getActionBar();
+ ActionBar actionBar = ((SherlockActivity) activity).getSupportActionBar();
if (actionBar != null) {
if ((mFlags & FLAG_HIDE_ACTION_BAR) != 0) {
actionBar.hide();
@@ -231,13 +231,6 @@ abstract public class ActivityState {
activity.registerReceiver(mPowerIntentReceiver, filter);
}
- try {
- mHapticsEnabled = Settings.System.getInt(mContentResolver,
- Settings.System.HAPTIC_FEEDBACK_ENABLED) != 0;
- } catch (SettingNotFoundException e) {
- mHapticsEnabled = false;
- }
-
onResume();
// the transition store should be cleared after resume;
@@ -279,6 +272,6 @@ abstract public class ActivityState {
}
protected MenuInflater getSupportMenuInflater() {
- return mActivity.getMenuInflater();
+ return ((SherlockActivity) mActivity).getSupportMenuInflater();
}
}
diff --git a/src/com/android/gallery3d/app/AlbumDataLoader.java b/src/com/android/gallery3d/app/AlbumDataLoader.java
index 0ee1b03af..28a822830 100644
--- a/src/com/android/gallery3d/app/AlbumDataLoader.java
+++ b/src/com/android/gallery3d/app/AlbumDataLoader.java
@@ -120,8 +120,7 @@ public class AlbumDataLoader {
public MediaItem get(int index) {
if (!isActive(index)) {
- throw new IllegalArgumentException(String.format(
- "%s not in (%s, %s)", index, mActiveStart, mActiveEnd));
+ return mSource.getMediaItem(index, 1).get(0);
}
return mData[index % mData.length];
}
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index ee7a107fd..60000903d 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -24,13 +24,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.Vibrator;
import android.provider.MediaStore;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.widget.Toast;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -57,6 +57,9 @@ import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.MediaSetUtils;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.CropExtras;
+
public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
SelectionManager.SelectionListener, MediaSet.SyncListener, GalleryActionBar.OnAlbumModeSelectedListener {
@@ -89,7 +92,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
private AlbumDataLoader mAlbumDataAdapter;
protected SelectionManager mSelectionManager;
- private Vibrator mVibrator;
private boolean mGetContent;
private boolean mShowClusterMenu;
@@ -304,10 +306,10 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
startInFilmstrip);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, mMediaSet.isCameraRoll());
if (startInFilmstrip) {
- mActivity.getStateManager().switchState(this, PhotoPage.class, data);
+ mActivity.getStateManager().switchState(this, FilmstripPage.class, data);
} else {
mActivity.getStateManager().startStateForResult(
- PhotoPage.class, REQUEST_PHOTO, data);
+ SinglePhotoPage.class, REQUEST_PHOTO, data);
}
}
}
@@ -318,11 +320,11 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
if (mData.getString(Gallery.EXTRA_CROP) != null) {
// TODO: Handle MtpImagew
Uri uri = dm.getContentUri(item.getPath());
- Intent intent = new Intent(CropImage.ACTION_CROP, uri)
+ Intent intent = new Intent(FilterShowActivity.CROP_ACTION, uri)
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
.putExtras(getData());
if (mData.getParcelable(MediaStore.EXTRA_OUTPUT) == null) {
- intent.putExtra(CropImage.KEY_RETURN_DATA, true);
+ intent.putExtra(CropExtras.KEY_RETURN_DATA, true);
}
activity.startActivity(intent);
activity.finish();
@@ -371,7 +373,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
mDetailsSource = new MyDetailsSource();
Context context = mActivity.getAndroidContext();
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
// Enable auto-select-all for mtp album
if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
@@ -379,7 +380,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
}
mLaunchedFromPhotoPage =
- mActivity.getStateManager().hasStateClass(PhotoPage.class);
+ mActivity.getStateManager().hasStateClass(FilmstripPage.class);
mInCameraApp = data.getBoolean(PhotoPage.KEY_APP_BRIDGE, false);
mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
@@ -443,7 +444,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mSelectionManager.leaveSelectionMode();
}
mAlbumView.setSlotFilter(null);
-
+ mActionModeHandler.pause();
mAlbumDataAdapter.pause();
mAlbumView.pause();
DetailsHelper.pause();
@@ -456,7 +457,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mSyncTask = null;
clearLoadingBit(BIT_LOADING_SYNC);
}
- mActionModeHandler.pause();
}
@Override
@@ -465,6 +465,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
if (mAlbumDataAdapter != null) {
mAlbumDataAdapter.setLoadingListener(null);
}
+ mActionModeHandler.destroy();
}
private void initializeViews() {
@@ -662,7 +663,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
switch (mode) {
case SelectionManager.ENTER_SELECTION_MODE: {
mActionModeHandler.startActionMode();
- if (mHapticsEnabled) mVibrator.vibrate(100);
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index cae606be1..df68ffbaa 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -23,16 +23,16 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.Vibrator;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -92,7 +92,6 @@ public class AlbumSetPage extends ActivityState implements
private boolean mShowClusterMenu;
private GalleryActionBar mActionBar;
private int mSelectedAction;
- private Vibrator mVibrator;
protected SelectionManager mSelectionManager;
private AlbumSetDataLoader mAlbumSetDataAdapter;
@@ -275,7 +274,7 @@ public class AlbumSetPage extends ActivityState implements
data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP, true);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, targetSet.isCameraRoll());
mActivity.getStateManager().startStateForResult(
- PhotoPage.class, AlbumPage.REQUEST_PHOTO, data);
+ FilmstripPage.class, AlbumPage.REQUEST_PHOTO, data);
return;
}
data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
@@ -332,7 +331,6 @@ public class AlbumSetPage extends ActivityState implements
mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
mEyePosition = new EyePosition(context, this);
mDetailsSource = new MyDetailsSource();
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mActionBar = mActivity.getGalleryActionBar();
mSelectedAction = data.getInt(AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE,
FilterUtils.CLUSTER_BY_ALBUM);
@@ -353,8 +351,9 @@ public class AlbumSetPage extends ActivityState implements
@Override
public void onDestroy() {
- cleanupCameraButton();
super.onDestroy();
+ cleanupCameraButton();
+ mActionModeHandler.destroy();
}
private boolean setupCameraButton() {
@@ -439,9 +438,9 @@ public class AlbumSetPage extends ActivityState implements
public void onPause() {
super.onPause();
mIsActive = false;
- mActionModeHandler.pause();
mAlbumSetDataAdapter.pause();
mAlbumSetView.pause();
+ mActionModeHandler.pause();
mEyePosition.pause();
DetailsHelper.pause();
// Call disableClusterMenu to avoid receiving callback after paused.
@@ -655,7 +654,7 @@ public class AlbumSetPage extends ActivityState implements
case SelectionManager.ENTER_SELECTION_MODE: {
mActionBar.disableClusterMenu(true);
mActionModeHandler.startActionMode();
- if (mHapticsEnabled) mVibrator.vibrate(100);
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
diff --git a/src/com/android/gallery3d/app/BatchService.java b/src/com/android/gallery3d/app/BatchService.java
new file mode 100644
index 000000000..98a1d8215
--- /dev/null
+++ b/src/com/android/gallery3d/app/BatchService.java
@@ -0,0 +1,45 @@
+/*
+ * 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.app;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.gallery3d.util.ThreadPool;
+
+public class BatchService extends Service {
+
+ public class LocalBinder extends Binder {
+ BatchService getService() {
+ return BatchService.this;
+ }
+ }
+
+ private final IBinder mBinder = new LocalBinder();
+ private ThreadPool mThreadPool = new ThreadPool(1, 1);
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public ThreadPool getThreadPool() {
+ return mThreadPool;
+ }
+}
diff --git a/src/com/android/gallery3d/app/CommonControllerOverlay.java b/src/com/android/gallery3d/app/CommonControllerOverlay.java
index ab43dada5..089872fa5 100644
--- a/src/com/android/gallery3d/app/CommonControllerOverlay.java
+++ b/src/com/android/gallery3d/app/CommonControllerOverlay.java
@@ -154,7 +154,7 @@ public abstract class CommonControllerOverlay extends FrameLayout implements
@Override
public void showEnded() {
mState = State.ENDED;
- showMainView(mPlayPauseReplayView);
+ if (mCanReplay) showMainView(mPlayPauseReplayView);
}
@Override
diff --git a/src/com/android/gallery3d/app/CropImage.java b/src/com/android/gallery3d/app/CropImage.java
deleted file mode 100644
index 89ca63d44..000000000
--- a/src/com/android/gallery3d/app/CropImage.java
+++ /dev/null
@@ -1,1040 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.app;
-
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.ProgressDialog;
-import android.app.WallpaperManager;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Images;
-import android.util.FloatMath;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-import com.android.camera.Util;
-import com.android.gallery3d.R;
-import com.android.gallery3d.common.ApiHelper;
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.DataManager;
-import com.android.gallery3d.data.LocalImage;
-import com.android.gallery3d.data.MediaItem;
-import com.android.gallery3d.data.MediaObject;
-import com.android.gallery3d.data.Path;
-import com.android.gallery3d.exif.ExifData;
-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;
-import com.android.gallery3d.ui.SynchronizedHandler;
-import com.android.gallery3d.ui.TileImageViewAdapter;
-import com.android.gallery3d.util.BucketNames;
-import com.android.gallery3d.util.Future;
-import com.android.gallery3d.util.FutureListener;
-import com.android.gallery3d.util.GalleryUtils;
-import com.android.gallery3d.util.InterruptableOutputStream;
-import com.android.gallery3d.util.ThreadPool.CancelListener;
-import com.android.gallery3d.util.ThreadPool.Job;
-import com.android.gallery3d.util.ThreadPool.JobContext;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteOrder;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * The activity can crop specific region of interest from an image.
- */
-public class CropImage extends AbstractGalleryActivity {
- private static final String TAG = "CropImage";
- public static final String ACTION_CROP = "com.android.camera.action.CROP";
-
- private static final int MAX_PIXEL_COUNT = 5 * 1000000; // 5M pixels
- private static final int MAX_FILE_INDEX = 1000;
- private static final int TILE_SIZE = 512;
- private static final int BACKUP_PIXEL_COUNT = 480000; // around 800x600
-
- private static final int MSG_LARGE_BITMAP = 1;
- private static final int MSG_BITMAP = 2;
- private static final int MSG_SAVE_COMPLETE = 3;
- private static final int MSG_SHOW_SAVE_ERROR = 4;
- private static final int MSG_CANCEL_DIALOG = 5;
-
- private static final int MAX_BACKUP_IMAGE_SIZE = 320;
- private static final int DEFAULT_COMPRESS_QUALITY = 90;
- private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss";
-
- public static final String KEY_RETURN_DATA = "return-data";
- public static final String KEY_CROPPED_RECT = "cropped-rect";
- public static final String KEY_ASPECT_X = "aspectX";
- public static final String KEY_ASPECT_Y = "aspectY";
- public static final String KEY_SPOTLIGHT_X = "spotlightX";
- public static final String KEY_SPOTLIGHT_Y = "spotlightY";
- public static final String KEY_OUTPUT_X = "outputX";
- public static final String KEY_OUTPUT_Y = "outputY";
- public static final String KEY_SCALE = "scale";
- public static final String KEY_DATA = "data";
- public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded";
- public static final String KEY_OUTPUT_FORMAT = "outputFormat";
- public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper";
- public static final String KEY_NO_FACE_DETECTION = "noFaceDetection";
- public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
-
- private static final String KEY_STATE = "state";
-
- private static final int STATE_INIT = 0;
- private static final int STATE_LOADED = 1;
- private static final int STATE_SAVING = 2;
-
- public static final File DOWNLOAD_BUCKET = new File(
- Environment.getExternalStorageDirectory(), BucketNames.DOWNLOAD);
-
- public static final String CROP_ACTION = "com.android.camera.action.CROP";
-
- private int mState = STATE_INIT;
-
- private CropView mCropView;
-
- private boolean mDoFaceDetection = true;
-
- private Handler mMainHandler;
-
- // We keep the following members so that we can free them
-
- // mBitmap is the unrotated bitmap we pass in to mCropView for detect faces.
- // mCropView is responsible for rotating it to the way that it is viewed by users.
- private Bitmap mBitmap;
- private BitmapTileProvider mBitmapTileProvider;
- private BitmapRegionDecoder mRegionDecoder;
- private Bitmap mBitmapInIntent;
- private boolean mUseRegionDecoder = false;
- private BitmapScreenNail mBitmapScreenNail;
-
- private ProgressDialog mProgressDialog;
- private Future<BitmapRegionDecoder> mLoadTask;
- private Future<Bitmap> mLoadBitmapTask;
- private Future<Intent> mSaveTask;
-
- private MediaItem mMediaItem;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- requestWindowFeature(Window.FEATURE_ACTION_BAR);
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-
- // Initialize UI
- setContentView(R.layout.cropimage);
- mCropView = new CropView(this);
- getGLRoot().setContentPane(mCropView);
-
- ActionBar actionBar = getActionBar();
- int displayOptions = ActionBar.DISPLAY_HOME_AS_UP
- | ActionBar.DISPLAY_SHOW_TITLE;
- actionBar.setDisplayOptions(displayOptions, displayOptions);
-
- Bundle extra = getIntent().getExtras();
- if (extra != null) {
- if (extra.getBoolean(KEY_SET_AS_WALLPAPER, false)) {
- actionBar.setTitle(getString(R.string.set_wallpaper));
- }
- if (extra.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- }
- }
-
- mMainHandler = new SynchronizedHandler(getGLRoot()) {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_LARGE_BITMAP: {
- dismissProgressDialogIfShown();
- onBitmapRegionDecoderAvailable((BitmapRegionDecoder) message.obj);
- break;
- }
- case MSG_BITMAP: {
- dismissProgressDialogIfShown();
- onBitmapAvailable((Bitmap) message.obj);
- break;
- }
- case MSG_SHOW_SAVE_ERROR: {
- dismissProgressDialogIfShown();
- setResult(RESULT_CANCELED);
- Toast.makeText(CropImage.this,
- CropImage.this.getString(R.string.save_error),
- Toast.LENGTH_LONG).show();
- finish();
- }
- case MSG_SAVE_COMPLETE: {
- dismissProgressDialogIfShown();
- setResult(RESULT_OK, (Intent) message.obj);
- finish();
- break;
- }
- case MSG_CANCEL_DIALOG: {
- setResult(RESULT_CANCELED);
- finish();
- break;
- }
- }
- }
- };
-
- setCropParameters();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle saveState) {
- saveState.putInt(KEY_STATE, mState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.crop, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home: {
- finish();
- break;
- }
- case R.id.cancel: {
- setResult(RESULT_CANCELED);
- finish();
- break;
- }
- case R.id.save: {
- onSaveClicked();
- break;
- }
- }
- return true;
- }
-
- @Override
- public void onBackPressed() {
- finish();
- }
-
- private class SaveOutput implements Job<Intent> {
- private final RectF mCropRect;
-
- public SaveOutput(RectF cropRect) {
- mCropRect = cropRect;
- }
-
- @Override
- public Intent run(JobContext jc) {
- RectF cropRect = mCropRect;
- Bundle extra = getIntent().getExtras();
-
- Rect rect = new Rect(
- Math.round(cropRect.left), Math.round(cropRect.top),
- Math.round(cropRect.right), Math.round(cropRect.bottom));
-
- Intent result = new Intent();
- result.putExtra(KEY_CROPPED_RECT, rect);
- Bitmap cropped = null;
- boolean outputted = false;
- if (extra != null) {
- Uri uri = (Uri) extra.getParcelable(MediaStore.EXTRA_OUTPUT);
- if (uri != null) {
- if (jc.isCancelled()) return null;
- outputted = true;
- cropped = getCroppedImage(rect);
- if (!saveBitmapToUri(jc, cropped, uri)) return null;
- }
- if (extra.getBoolean(KEY_RETURN_DATA, false)) {
- if (jc.isCancelled()) return null;
- outputted = true;
- if (cropped == null) cropped = getCroppedImage(rect);
- result.putExtra(KEY_DATA, cropped);
- }
- if (extra.getBoolean(KEY_SET_AS_WALLPAPER, false)) {
- if (jc.isCancelled()) return null;
- outputted = true;
- if (cropped == null) cropped = getCroppedImage(rect);
- if (!setAsWallpaper(jc, cropped)) return null;
- }
- }
- if (!outputted) {
- if (jc.isCancelled()) return null;
- if (cropped == null) cropped = getCroppedImage(rect);
- Uri data = saveToMediaProvider(jc, cropped);
- if (data != null) result.setData(data);
- }
- return result;
- }
- }
-
- public static String determineCompressFormat(MediaObject obj) {
- String compressFormat = "JPEG";
- if (obj instanceof MediaItem) {
- String mime = ((MediaItem) obj).getMimeType();
- if (mime.contains("png") || mime.contains("gif")) {
- // Set the compress format to PNG for png and gif images
- // because they may contain alpha values.
- compressFormat = "PNG";
- }
- }
- return compressFormat;
- }
-
- private boolean setAsWallpaper(JobContext jc, Bitmap wallpaper) {
- try {
- WallpaperManager.getInstance(this).setBitmap(wallpaper);
- } catch (IOException e) {
- Log.w(TAG, "fail to set wall paper", e);
- }
- return true;
- }
-
- private File saveMedia(
- JobContext jc, Bitmap cropped, File directory, String filename, ExifData exifData) {
- // Try file-1.jpg, file-2.jpg, ... until we find a filename
- // which does not exist yet.
- File candidate = null;
- String fileExtension = getFileExtension();
- for (int i = 1; i < MAX_FILE_INDEX; ++i) {
- candidate = new File(directory, filename + "-" + i + "."
- + fileExtension);
- try {
- if (candidate.createNewFile()) break;
- } catch (IOException e) {
- Log.e(TAG, "fail to create new file: "
- + candidate.getAbsolutePath(), e);
- return null;
- }
- }
- if (!candidate.exists() || !candidate.isFile()) {
- throw new RuntimeException("cannot create file: " + filename);
- }
-
- candidate.setReadable(true, false);
- candidate.setWritable(true, false);
-
- try {
- FileOutputStream fos = new FileOutputStream(candidate);
- try {
- if (exifData != null) {
- ExifOutputStream eos = new ExifOutputStream(fos);
- eos.setExifData(exifData);
- saveBitmapToOutputStream(jc, cropped,
- convertExtensionToCompressFormat(fileExtension), eos);
- } else {
- saveBitmapToOutputStream(jc, cropped,
- convertExtensionToCompressFormat(fileExtension), fos);
- }
- } finally {
- fos.close();
- }
- } catch (IOException e) {
- Log.e(TAG, "fail to save image: "
- + candidate.getAbsolutePath(), e);
- candidate.delete();
- return null;
- }
-
- if (jc.isCancelled()) {
- candidate.delete();
- return null;
- }
-
- return candidate;
- }
-
- private ExifData getExifData(String path) {
- FileInputStream is = null;
- try {
- is = new FileInputStream(path);
- ExifReader reader = new ExifReader();
- ExifData data = reader.read(is);
- return data;
- } catch (Throwable t) {
- Log.w(TAG, "Cannot read EXIF data", t);
- return null;
- } finally {
- Util.closeSilently(is);
- }
- }
-
- private static final String EXIF_SOFTWARE_VALUE = "Android Gallery";
-
- private void changeExifData(ExifData data, int width, int height) {
- data.addTag(ExifTag.TAG_IMAGE_WIDTH).setValue(width);
- data.addTag(ExifTag.TAG_IMAGE_LENGTH).setValue(height);
- data.addTag(ExifTag.TAG_SOFTWARE).setValue(EXIF_SOFTWARE_VALUE);
- data.addTag(ExifTag.TAG_DATE_TIME).setTimeValue(System.currentTimeMillis());
- // Remove the original thumbnail
- // TODO: generate a new thumbnail for the cropped image.
- data.removeThumbnailData();
- }
-
- private Uri saveToMediaProvider(JobContext jc, Bitmap cropped) {
- if (PicasaSource.isPicasaImage(mMediaItem)) {
- return savePicasaImage(jc, cropped);
- } else if (mMediaItem instanceof LocalImage) {
- return saveLocalImage(jc, cropped);
- } else {
- return saveGenericImage(jc, cropped);
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private static void setImageSize(ContentValues values, int width, int height) {
- // The two fields are available since ICS but got published in JB
- if (ApiHelper.HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT) {
- values.put(Images.Media.WIDTH, width);
- values.put(Images.Media.HEIGHT, height);
- }
- }
-
- private Uri savePicasaImage(JobContext jc, Bitmap cropped) {
- if (!DOWNLOAD_BUCKET.isDirectory() && !DOWNLOAD_BUCKET.mkdirs()) {
- throw new RuntimeException("cannot create download folder");
- }
- String filename = PicasaSource.getImageTitle(mMediaItem);
- int pos = filename.lastIndexOf('.');
- if (pos >= 0) filename = filename.substring(0, pos);
- ExifData exifData = new ExifData(ByteOrder.BIG_ENDIAN);
- PicasaSource.extractExifValues(mMediaItem, exifData);
- changeExifData(exifData, cropped.getWidth(), cropped.getHeight());
- File output = saveMedia(jc, cropped, DOWNLOAD_BUCKET, filename, exifData);
- if (output == null) return null;
-
- long now = System.currentTimeMillis() / 1000;
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, PicasaSource.getImageTitle(mMediaItem));
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, PicasaSource.getDateTaken(mMediaItem));
- values.put(Images.Media.DATE_MODIFIED, now);
- values.put(Images.Media.DATE_ADDED, now);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- double latitude = PicasaSource.getLatitude(mMediaItem);
- double longitude = PicasaSource.getLongitude(mMediaItem);
- if (GalleryUtils.isValidLocation(latitude, longitude)) {
- values.put(Images.Media.LATITUDE, latitude);
- values.put(Images.Media.LONGITUDE, longitude);
- }
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private Uri saveLocalImage(JobContext jc, Bitmap cropped) {
- LocalImage localImage = (LocalImage) mMediaItem;
-
- File oldPath = new File(localImage.filePath);
- File directory = new File(oldPath.getParent());
-
- String filename = oldPath.getName();
- int pos = filename.lastIndexOf('.');
- if (pos >= 0) filename = filename.substring(0, pos);
- File output = null;
-
- ExifData exifData = null;
- if (convertExtensionToCompressFormat(getFileExtension()) == CompressFormat.JPEG) {
- exifData = getExifData(oldPath.getAbsolutePath());
- if (exifData != null) {
- changeExifData(exifData, cropped.getWidth(), cropped.getHeight());
- }
- }
- output = saveMedia(jc, cropped, directory, filename, exifData);
- if (output == null) return null;
-
- long now = System.currentTimeMillis() / 1000;
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, localImage.caption);
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, localImage.dateTakenInMs);
- values.put(Images.Media.DATE_MODIFIED, now);
- values.put(Images.Media.DATE_ADDED, now);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
-
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- if (GalleryUtils.isValidLocation(localImage.latitude, localImage.longitude)) {
- values.put(Images.Media.LATITUDE, localImage.latitude);
- values.put(Images.Media.LONGITUDE, localImage.longitude);
- }
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private Uri saveGenericImage(JobContext jc, Bitmap cropped) {
- if (!DOWNLOAD_BUCKET.isDirectory() && !DOWNLOAD_BUCKET.mkdirs()) {
- throw new RuntimeException("cannot create download folder");
- }
-
- long now = System.currentTimeMillis();
- String filename = new SimpleDateFormat(TIME_STAMP_NAME).
- format(new Date(now));
-
- File output = saveMedia(jc, cropped, DOWNLOAD_BUCKET, filename, null);
- if (output == null) return null;
-
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, filename);
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, now);
- values.put(Images.Media.DATE_MODIFIED, now / 1000);
- values.put(Images.Media.DATE_ADDED, now / 1000);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
-
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private boolean saveBitmapToOutputStream(
- JobContext jc, Bitmap bitmap, CompressFormat format, OutputStream os) {
- // We wrap the OutputStream so that it can be interrupted.
- final InterruptableOutputStream ios = new InterruptableOutputStream(os);
- jc.setCancelListener(new CancelListener() {
- @Override
- public void onCancel() {
- ios.interrupt();
- }
- });
- try {
- bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, ios);
- return !jc.isCancelled();
- } finally {
- jc.setCancelListener(null);
- Utils.closeSilently(ios);
- }
- }
-
- private boolean saveBitmapToUri(JobContext jc, Bitmap bitmap, Uri uri) {
- try {
- OutputStream out = getContentResolver().openOutputStream(uri);
- try {
- return saveBitmapToOutputStream(jc, bitmap,
- convertExtensionToCompressFormat(getFileExtension()), out);
- } finally {
- Utils.closeSilently(out);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "cannot write output", e);
- }
- return true;
- }
-
- private CompressFormat convertExtensionToCompressFormat(String extension) {
- return extension.equals("png")
- ? CompressFormat.PNG
- : CompressFormat.JPEG;
- }
-
- private String getOutputMimeType() {
- return getFileExtension().equals("png") ? "image/png" : "image/jpeg";
- }
-
- private String getFileExtension() {
- String requestFormat = getIntent().getStringExtra(KEY_OUTPUT_FORMAT);
- String outputFormat = (requestFormat == null)
- ? determineCompressFormat(mMediaItem)
- : requestFormat;
-
- outputFormat = outputFormat.toLowerCase();
- return (outputFormat.equals("png") || outputFormat.equals("gif"))
- ? "png" // We don't support gif compression.
- : "jpg";
- }
-
- private void onSaveClicked() {
- Bundle extra = getIntent().getExtras();
- RectF cropRect = mCropView.getCropRectangle();
- if (cropRect == null) return;
- mState = STATE_SAVING;
- int messageId = extra != null && extra.getBoolean(KEY_SET_AS_WALLPAPER)
- ? R.string.wallpaper
- : R.string.saving_image;
- mProgressDialog = ProgressDialog.show(
- this, null, getString(messageId), true, false);
- mSaveTask = getThreadPool().submit(new SaveOutput(cropRect),
- new FutureListener<Intent>() {
- @Override
- public void onFutureDone(Future<Intent> future) {
- mSaveTask = null;
- if (future.isCancelled()) return;
- Intent intent = future.get();
- if (intent != null) {
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_SAVE_COMPLETE, intent));
- } else {
- mMainHandler.sendEmptyMessage(MSG_SHOW_SAVE_ERROR);
- }
- }
- });
- }
-
- private Bitmap getCroppedImage(Rect rect) {
- Utils.assertTrue(rect.width() > 0 && rect.height() > 0);
-
- Bundle extras = getIntent().getExtras();
- // (outputX, outputY) = the width and height of the returning bitmap.
- int outputX = rect.width();
- int outputY = rect.height();
- if (extras != null) {
- outputX = extras.getInt(KEY_OUTPUT_X, outputX);
- outputY = extras.getInt(KEY_OUTPUT_Y, outputY);
- }
-
- if (outputX * outputY > MAX_PIXEL_COUNT) {
- float scale = FloatMath.sqrt((float) MAX_PIXEL_COUNT / outputX / outputY);
- Log.w(TAG, "scale down the cropped image: " + scale);
- outputX = Math.round(scale * outputX);
- outputY = Math.round(scale * outputY);
- }
-
- // (rect.width() * scaleX, rect.height() * scaleY) =
- // the size of drawing area in output bitmap
- float scaleX = 1;
- float scaleY = 1;
- Rect dest = new Rect(0, 0, outputX, outputY);
- if (extras == null || extras.getBoolean(KEY_SCALE, true)) {
- scaleX = (float) outputX / rect.width();
- scaleY = (float) outputY / rect.height();
- if (extras == null || !extras.getBoolean(
- KEY_SCALE_UP_IF_NEEDED, false)) {
- if (scaleX > 1f) scaleX = 1;
- if (scaleY > 1f) scaleY = 1;
- }
- }
-
- // Keep the content in the center (or crop the content)
- int rectWidth = Math.round(rect.width() * scaleX);
- int rectHeight = Math.round(rect.height() * scaleY);
- dest.set(Math.round((outputX - rectWidth) / 2f),
- Math.round((outputY - rectHeight) / 2f),
- Math.round((outputX + rectWidth) / 2f),
- Math.round((outputY + rectHeight) / 2f));
-
- if (mBitmapInIntent != null) {
- Bitmap source = mBitmapInIntent;
- Bitmap result = Bitmap.createBitmap(
- outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- canvas.drawBitmap(source, rect, dest, null);
- return result;
- }
-
- if (mUseRegionDecoder) {
- int rotation = mMediaItem.getFullImageRotation();
- rotateRectangle(rect, mCropView.getImageWidth(),
- mCropView.getImageHeight(), 360 - rotation);
- rotateRectangle(dest, outputX, outputY, 360 - rotation);
-
- BitmapFactory.Options options = new BitmapFactory.Options();
- int sample = BitmapUtils.computeSampleSizeLarger(
- Math.max(scaleX, scaleY));
- options.inSampleSize = sample;
-
- // The decoding result is what we want if
- // 1. The size of the decoded bitmap match the destination's size
- // 2. The destination covers the whole output bitmap
- // 3. No rotation
- if ((rect.width() / sample) == dest.width()
- && (rect.height() / sample) == dest.height()
- && (outputX == dest.width()) && (outputY == dest.height())
- && rotation == 0) {
- // To prevent concurrent access in GLThread
- synchronized (mRegionDecoder) {
- return mRegionDecoder.decodeRegion(rect, options);
- }
- }
- Bitmap result = Bitmap.createBitmap(
- outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- rotateCanvas(canvas, outputX, outputY, rotation);
- drawInTiles(canvas, mRegionDecoder, rect, dest, sample);
- return result;
- } else {
- int rotation = mMediaItem.getRotation();
- rotateRectangle(rect, mCropView.getImageWidth(),
- mCropView.getImageHeight(), 360 - rotation);
- rotateRectangle(dest, outputX, outputY, 360 - rotation);
- Bitmap result = Bitmap.createBitmap(outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- rotateCanvas(canvas, outputX, outputY, rotation);
- canvas.drawBitmap(mBitmap,
- rect, dest, new Paint(Paint.FILTER_BITMAP_FLAG));
- return result;
- }
- }
-
- private static void rotateCanvas(
- Canvas canvas, int width, int height, int rotation) {
- canvas.translate(width / 2, height / 2);
- canvas.rotate(rotation);
- if (((rotation / 90) & 0x01) == 0) {
- canvas.translate(-width / 2, -height / 2);
- } else {
- canvas.translate(-height / 2, -width / 2);
- }
- }
-
- private static void rotateRectangle(
- Rect rect, int width, int height, int rotation) {
- if (rotation == 0 || rotation == 360) return;
-
- int w = rect.width();
- int h = rect.height();
- switch (rotation) {
- case 90: {
- rect.top = rect.left;
- rect.left = height - rect.bottom;
- rect.right = rect.left + h;
- rect.bottom = rect.top + w;
- return;
- }
- case 180: {
- rect.left = width - rect.right;
- rect.top = height - rect.bottom;
- rect.right = rect.left + w;
- rect.bottom = rect.top + h;
- return;
- }
- case 270: {
- rect.left = rect.top;
- rect.top = width - rect.right;
- rect.right = rect.left + h;
- rect.bottom = rect.top + w;
- return;
- }
- default: throw new AssertionError();
- }
- }
-
- private void drawInTiles(Canvas canvas,
- BitmapRegionDecoder decoder, Rect rect, Rect dest, int sample) {
- int tileSize = TILE_SIZE * sample;
- Rect tileRect = new Rect();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inPreferredConfig = Config.ARGB_8888;
- options.inSampleSize = sample;
- canvas.translate(dest.left, dest.top);
- canvas.scale((float) sample * dest.width() / rect.width(),
- (float) sample * dest.height() / rect.height());
- Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
- for (int tx = rect.left, x = 0;
- tx < rect.right; tx += tileSize, x += TILE_SIZE) {
- for (int ty = rect.top, y = 0;
- ty < rect.bottom; ty += tileSize, y += TILE_SIZE) {
- tileRect.set(tx, ty, tx + tileSize, ty + tileSize);
- if (tileRect.intersect(rect)) {
- Bitmap bitmap;
-
- // To prevent concurrent access in GLThread
- synchronized (decoder) {
- bitmap = decoder.decodeRegion(tileRect, options);
- }
- canvas.drawBitmap(bitmap, x, y, paint);
- bitmap.recycle();
- }
- }
- }
- }
-
- private void onBitmapRegionDecoderAvailable(
- BitmapRegionDecoder regionDecoder) {
-
- if (regionDecoder == null) {
- Toast.makeText(this, R.string.fail_to_load_image, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- mRegionDecoder = regionDecoder;
- mUseRegionDecoder = true;
- mState = STATE_LOADED;
-
- BitmapFactory.Options options = new BitmapFactory.Options();
- int width = regionDecoder.getWidth();
- int height = regionDecoder.getHeight();
- options.inSampleSize = BitmapUtils.computeSampleSize(width, height,
- BitmapUtils.UNCONSTRAINED, BACKUP_PIXEL_COUNT);
- mBitmap = regionDecoder.decodeRegion(
- new Rect(0, 0, width, height), options);
-
- 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 {
- mCropView.initializeHighlightRectangle();
- }
- }
-
- private void onBitmapAvailable(Bitmap bitmap) {
- if (bitmap == null) {
- Toast.makeText(this, R.string.fail_to_load_image, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- mUseRegionDecoder = false;
- mState = STATE_LOADED;
-
- mBitmap = bitmap;
- BitmapFactory.Options options = new BitmapFactory.Options();
- mCropView.setDataModel(new BitmapTileProvider(bitmap, 512),
- mMediaItem.getRotation());
- if (mDoFaceDetection) {
- mCropView.detectFaces(bitmap);
- } else {
- mCropView.initializeHighlightRectangle();
- }
- }
-
- private void setCropParameters() {
- Bundle extras = getIntent().getExtras();
- if (extras == null)
- return;
- int aspectX = extras.getInt(KEY_ASPECT_X, 0);
- int aspectY = extras.getInt(KEY_ASPECT_Y, 0);
- if (aspectX != 0 && aspectY != 0) {
- mCropView.setAspectRatio((float) aspectX / aspectY);
- }
-
- float spotlightX = extras.getFloat(KEY_SPOTLIGHT_X, 0);
- float spotlightY = extras.getFloat(KEY_SPOTLIGHT_Y, 0);
- if (spotlightX != 0 && spotlightY != 0) {
- mCropView.setSpotlightRatio(spotlightX, spotlightY);
- }
- }
-
- private void initializeData() {
- Bundle extras = getIntent().getExtras();
-
- if (extras != null) {
- if (extras.containsKey(KEY_NO_FACE_DETECTION)) {
- mDoFaceDetection = !extras.getBoolean(KEY_NO_FACE_DETECTION);
- }
-
- mBitmapInIntent = extras.getParcelable(KEY_DATA);
-
- if (mBitmapInIntent != null) {
- mBitmapTileProvider =
- new BitmapTileProvider(mBitmapInIntent, MAX_BACKUP_IMAGE_SIZE);
- mCropView.setDataModel(mBitmapTileProvider, 0);
- if (mDoFaceDetection) {
- mCropView.detectFaces(mBitmapInIntent);
- } else {
- mCropView.initializeHighlightRectangle();
- }
- mState = STATE_LOADED;
- return;
- }
- }
-
- mProgressDialog = ProgressDialog.show(
- this, null, getString(R.string.loading_image), true, true);
- mProgressDialog.setCanceledOnTouchOutside(false);
- mProgressDialog.setCancelMessage(mMainHandler.obtainMessage(MSG_CANCEL_DIALOG));
-
- mMediaItem = getMediaItemFromIntentData();
- if (mMediaItem == null) return;
-
- boolean supportedByBitmapRegionDecoder =
- (mMediaItem.getSupportedOperations() & MediaItem.SUPPORT_FULL_IMAGE) != 0;
- if (supportedByBitmapRegionDecoder) {
- mLoadTask = getThreadPool().submit(new LoadDataTask(mMediaItem),
- new FutureListener<BitmapRegionDecoder>() {
- @Override
- public void onFutureDone(Future<BitmapRegionDecoder> future) {
- mLoadTask = null;
- BitmapRegionDecoder decoder = future.get();
- if (future.isCancelled()) {
- if (decoder != null) decoder.recycle();
- return;
- }
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_LARGE_BITMAP, decoder));
- }
- });
- } else {
- mLoadBitmapTask = getThreadPool().submit(new LoadBitmapDataTask(mMediaItem),
- new FutureListener<Bitmap>() {
- @Override
- public void onFutureDone(Future<Bitmap> future) {
- mLoadBitmapTask = null;
- Bitmap bitmap = future.get();
- if (future.isCancelled()) {
- if (bitmap != null) bitmap.recycle();
- return;
- }
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_BITMAP, bitmap));
- }
- });
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mState == STATE_INIT) initializeData();
- if (mState == STATE_SAVING) onSaveClicked();
-
- // TODO: consider to do it in GLView system
- GLRoot root = getGLRoot();
- root.lockRenderThread();
- try {
- mCropView.resume();
- } finally {
- root.unlockRenderThread();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- dismissProgressDialogIfShown();
-
- Future<BitmapRegionDecoder> loadTask = mLoadTask;
- if (loadTask != null && !loadTask.isDone()) {
- // load in progress, try to cancel it
- loadTask.cancel();
- loadTask.waitDone();
- }
-
- Future<Bitmap> loadBitmapTask = mLoadBitmapTask;
- if (loadBitmapTask != null && !loadBitmapTask.isDone()) {
- // load in progress, try to cancel it
- loadBitmapTask.cancel();
- loadBitmapTask.waitDone();
- }
-
- Future<Intent> saveTask = mSaveTask;
- if (saveTask != null && !saveTask.isDone()) {
- // save in progress, try to cancel it
- saveTask.cancel();
- saveTask.waitDone();
- }
- GLRoot root = getGLRoot();
- root.lockRenderThread();
- try {
- mCropView.pause();
- } finally {
- root.unlockRenderThread();
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mBitmapScreenNail != null) {
- mBitmapScreenNail.recycle();
- mBitmapScreenNail = null;
- }
- }
-
- private void dismissProgressDialogIfShown() {
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
- }
-
- private MediaItem getMediaItemFromIntentData() {
- Uri uri = getIntent().getData();
- DataManager manager = getDataManager();
- Path path = manager.findPathByUri(uri, getIntent().getType());
- if (path == null) {
- Log.w(TAG, "cannot get path for: " + uri + ", or no data given");
- return null;
- }
- return (MediaItem) manager.getMediaObject(path);
- }
-
- private class LoadDataTask implements Job<BitmapRegionDecoder> {
- MediaItem mItem;
-
- public LoadDataTask(MediaItem item) {
- mItem = item;
- }
-
- @Override
- public BitmapRegionDecoder run(JobContext jc) {
- return mItem == null ? null : mItem.requestLargeImage().run(jc);
- }
- }
-
- private class LoadBitmapDataTask implements Job<Bitmap> {
- MediaItem mItem;
-
- public LoadBitmapDataTask(MediaItem item) {
- mItem = item;
- }
- @Override
- public Bitmap run(JobContext jc) {
- return mItem == null
- ? null
- : mItem.requestImage(MediaItem.TYPE_THUMBNAIL).run(jc);
- }
- }
-}
diff --git a/src/com/android/gallery3d/app/FilmstripPage.java b/src/com/android/gallery3d/app/FilmstripPage.java
new file mode 100644
index 000000000..a9726cdc9
--- /dev/null
+++ b/src/com/android/gallery3d/app/FilmstripPage.java
@@ -0,0 +1,21 @@
+/*
+ * 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.app;
+
+public class FilmstripPage extends PhotoPage {
+
+}
diff --git a/src/com/android/gallery3d/app/Gallery.java b/src/com/android/gallery3d/app/Gallery.java
index e28404fac..354e325d4 100644
--- a/src/com/android/gallery3d/app/Gallery.java
+++ b/src/com/android/gallery3d/app/Gallery.java
@@ -26,10 +26,9 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.Toast;
+import com.actionbarsherlock.view.Window;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -49,7 +48,6 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
public static final String KEY_GET_ALBUM = "get-album";
public static final String KEY_TYPE_BITS = "type-bits";
public static final String KEY_MEDIA_TYPES = "mediaTypes";
- public static final String KEY_DISMISS_KEYGUARD = "dismiss-keyguard";
private static final String TAG = "Gallery";
private Dialog mVersionCheckDialog;
@@ -60,11 +58,6 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- if (getIntent().getBooleanExtra(KEY_DISMISS_KEYGUARD, false)) {
- getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
-
setContentView(R.layout.main);
if (savedInstanceState != null) {
@@ -222,7 +215,7 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
}
}
- getStateManager().startState(PhotoPage.class, data);
+ getStateManager().startState(SinglePhotoPage.class, data);
}
}
}
diff --git a/src/com/android/gallery3d/app/GalleryActionBar.java b/src/com/android/gallery3d/app/GalleryActionBar.java
index 0fb5e51b4..49f4186e8 100644
--- a/src/com/android/gallery3d/app/GalleryActionBar.java
+++ b/src/com/android/gallery3d/app/GalleryActionBar.java
@@ -17,9 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.ActionBar.OnMenuVisibilityListener;
-import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -27,15 +24,18 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.TwoLineListItem;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnMenuVisibilityListener;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
@@ -186,7 +186,7 @@ public class GalleryActionBar implements OnNavigationListener {
}
public GalleryActionBar(AbstractGalleryActivity activity) {
- mActionBar = activity.getActionBar();
+ mActionBar = activity.getSupportActionBar();
mContext = activity.getAndroidContext();
mActivity = activity;
mInflater = ((Activity) mActivity).getLayoutInflater();
@@ -396,7 +396,7 @@ public class GalleryActionBar implements OnNavigationListener {
private Intent mShareIntent;
public void createActionBarMenu(int menuRes, Menu menu) {
- mActivity.getMenuInflater().inflate(menuRes, menu);
+ mActivity.getSupportMenuInflater().inflate(menuRes, menu);
mActionBarMenu = menu;
MenuItem item = menu.findItem(R.id.action_share_panorama);
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 3123644c7..d725e6d12 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -17,8 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Intent;
@@ -33,13 +31,15 @@ import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.ShareActionProvider;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
@@ -51,7 +51,7 @@ import com.android.gallery3d.common.Utils;
* to set the action bar logo so the playback process looks more seamlessly integrated with
* the original activity.
*/
-public class MovieActivity extends Activity {
+public class MovieActivity extends SherlockActivity {
@SuppressWarnings("unused")
private static final String TAG = "MovieActivity";
public static final String KEY_LOGO_BITMAP = "logo-bitmap";
@@ -75,8 +75,8 @@ public class MovieActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_ACTION_BAR);
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ getSherlock().requestFeature(Window.FEATURE_ACTION_BAR);
+ getSherlock().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.movie_view);
View rootView = findViewById(R.id.movie_view_root);
@@ -119,14 +119,14 @@ public class MovieActivity extends Activity {
private void setActionBarLogoFromIntent(Intent intent) {
Bitmap logo = intent.getParcelableExtra(KEY_LOGO_BITMAP);
if (logo != null) {
- getActionBar().setLogo(
+ getSupportActionBar().setLogo(
new BitmapDrawable(getResources(), logo));
}
}
private void initializeActionBar(Intent intent) {
mUri = intent.getData();
- final ActionBar actionBar = getActionBar();
+ final ActionBar actionBar = getSupportActionBar();
setActionBarLogoFromIntent(intent);
actionBar.setDisplayOptions(
ActionBar.DISPLAY_HOME_AS_UP,
@@ -166,7 +166,7 @@ public class MovieActivity extends Activity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.movie, menu);
+ getSupportMenuInflater().inflate(R.menu.movie, menu);
// Document says EXTRA_STREAM should be a content: Uri
// So, we only share the video if it's "content:".
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 85dc4427e..00e4cd63b 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -74,8 +74,8 @@ public class MoviePlayer implements
private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
private Context mContext;
- private final View mRootView;
private final VideoView mVideoView;
+ private final View mRootView;
private final Bookmarker mBookmarker;
private final Uri mUri;
private final Handler mHandler = new Handler();
@@ -191,7 +191,6 @@ public class MoviePlayer implements
if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
mController.show();
- mRootView.setBackgroundColor(Color.BLACK);
}
}
});
diff --git a/src/com/android/gallery3d/app/MuteVideo.java b/src/com/android/gallery3d/app/MuteVideo.java
new file mode 100644
index 000000000..012b682ef
--- /dev/null
+++ b/src/com/android/gallery3d/app/MuteVideo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.app;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.widget.Toast;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.util.SaveVideoFileInfo;
+import com.android.gallery3d.util.SaveVideoFileUtils;
+
+import java.io.IOException;
+
+public class MuteVideo {
+
+ private ProgressDialog mMuteProgress;
+
+ private MediaItem mCurrentItem = null;
+ private Uri mUri = null;
+ private SaveVideoFileInfo mDstFileInfo = null;
+ private Activity mActivity = null;
+ private final Handler mHandler = new Handler();
+
+ final String TIME_STAMP_NAME = "'MUTE'_yyyyMMdd_HHmmss";
+
+ public MuteVideo(MediaItem current, Uri uri, Activity activity) {
+ mUri = uri;
+ mCurrentItem = current;
+ mActivity = activity;
+ }
+
+ public void muteInBackground() {
+ mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
+ mActivity.getContentResolver(), mUri,
+ mActivity.getString(R.string.folder_download));
+
+ showProgressDialog();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ VideoUtils.startMute(mCurrentItem.getFilePath(), mDstFileInfo);
+ SaveVideoFileUtils.insertContent(
+ mDstFileInfo, mActivity.getContentResolver(), mUri);
+ } catch (IOException e) {
+ Toast.makeText(mActivity, mActivity.getString(R.string.video_mute_err),
+ Toast.LENGTH_SHORT).show();
+ }
+ // After muting is done, trigger the UI changed.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(mActivity.getApplicationContext(),
+ mActivity.getString(R.string.save_into,
+ mDstFileInfo.mFolderName),
+ Toast.LENGTH_SHORT)
+ .show();
+
+ if (mMuteProgress != null) {
+ mMuteProgress.dismiss();
+ mMuteProgress = null;
+
+ // Show the result only when the activity not
+ // stopped.
+ Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
+ intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
+ mActivity.startActivity(intent);
+ }
+ }
+ });
+ }
+ }).start();
+ }
+
+ private void showProgressDialog() {
+ mMuteProgress = new ProgressDialog(mActivity);
+ mMuteProgress.setTitle(mActivity.getString(R.string.muting));
+ mMuteProgress.setMessage(mActivity.getString(R.string.please_wait));
+ mMuteProgress.setCancelable(false);
+ mMuteProgress.setCanceledOnTouchOutside(false);
+ mMuteProgress.show();
+ }
+}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 506d1ca6f..3c0c4330b 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -17,7 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar.OnMenuVisibilityListener;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -32,13 +31,15 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-import android.view.Menu;
-import android.view.MenuItem;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.android.camera.CameraActivity;
import com.android.camera.ProxyLauncher;
+
+import com.actionbarsherlock.app.ActionBar.OnMenuVisibilityListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.ComboAlbum;
@@ -71,7 +72,7 @@ import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.GalleryUtils;
-public class PhotoPage extends ActivityState implements
+public abstract class PhotoPage extends ActivityState implements
PhotoView.Listener, AppBridge.Server,
PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener {
private static final String TAG = "PhotoPage";
@@ -1019,11 +1020,16 @@ public class PhotoPage extends ActivityState implements
refreshHidingMessage();
MediaItem current = mModel.getMediaItem(0);
+ // This is a shield for monkey when it clicks the action bar
+ // menu when transitioning from filmstrip to camera
+ if (current instanceof SnailItem) return true;
+ // TODO: We should check the current photo against the MediaItem
+ // that the menu was initially created for. We need to fix this
+ // after PhotoPage being refactored.
if (current == null) {
// item is not ready, ignore
return true;
}
-
int currentIndex = mModel.getCurrentIndex();
Path path = current.getPath();
@@ -1064,6 +1070,12 @@ public class PhotoPage extends ActivityState implements
mActivity.startActivityForResult(intent, REQUEST_TRIM);
return true;
}
+ case R.id.action_mute: {
+ MuteVideo muteVideo = new MuteVideo(current,
+ manager.getContentUri(path), mActivity);
+ muteVideo.muteInBackground();
+ return true;
+ }
case R.id.action_edit: {
launchPhotoEditor();
return true;
@@ -1156,9 +1168,7 @@ public class PhotoPage extends ActivityState implements
} else if (goBack) {
onBackPressed();
} else if (unlock) {
- Intent intent = new Intent(mActivity, Gallery.class);
- intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true);
- mActivity.startActivity(intent);
+ mActivity.getStateManager().finishState(this);
} else if (launchCamera) {
launchCamera();
} else {
@@ -1242,7 +1252,7 @@ public class PhotoPage extends ActivityState implements
Bundle data = new Bundle(getData());
data.putString(KEY_MEDIA_SET_PATH, albumPath.toString());
data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString());
- mActivity.getStateManager().startState(PhotoPage.class, data);
+ mActivity.getStateManager().startState(SinglePhotoPage.class, data);
return;
}
mModel.setCurrentPhoto(path, mCurrentIndex);
diff --git a/src/com/android/gallery3d/app/PickerActivity.java b/src/com/android/gallery3d/app/PickerActivity.java
index d5bb218ea..1eb95d0c6 100644
--- a/src/com/android/gallery3d/app/PickerActivity.java
+++ b/src/com/android/gallery3d/app/PickerActivity.java
@@ -17,13 +17,13 @@
package com.android.gallery3d.app;
import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.Window;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
import com.android.gallery3d.R;
import com.android.gallery3d.ui.GLRootView;
@@ -62,7 +62,7 @@ public class PickerActivity extends AbstractGalleryActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
+ MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.pickup, menu);
return true;
}
diff --git a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
index 00f2fe78f..f0848ad22 100644
--- a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
@@ -34,6 +34,7 @@ import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.ui.TileImageViewAdapter;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.LightCycleHelper;
import com.android.gallery3d.util.ThreadPool;
public class SinglePhotoDataAdapter extends TileImageViewAdapter
diff --git a/src/com/android/gallery3d/app/SinglePhotoPage.java b/src/com/android/gallery3d/app/SinglePhotoPage.java
new file mode 100644
index 000000000..beb87d358
--- /dev/null
+++ b/src/com/android/gallery3d/app/SinglePhotoPage.java
@@ -0,0 +1,21 @@
+/*
+ * 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.app;
+
+public class SinglePhotoPage extends PhotoPage {
+
+}
diff --git a/src/com/android/gallery3d/app/StateManager.java b/src/com/android/gallery3d/app/StateManager.java
index d77279f78..64daa6afe 100644
--- a/src/com/android/gallery3d/app/StateManager.java
+++ b/src/com/android/gallery3d/app/StateManager.java
@@ -21,10 +21,10 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Parcelable;
-import android.view.Menu;
-import android.view.MenuItem;
import com.android.gallery3d.anim.StateTransitionAnimation;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.common.Utils;
import java.util.Stack;
diff --git a/src/com/android/gallery3d/app/TrimControllerOverlay.java b/src/com/android/gallery3d/app/TrimControllerOverlay.java
index 9127ad159..cae016626 100644
--- a/src/com/android/gallery3d/app/TrimControllerOverlay.java
+++ b/src/com/android/gallery3d/app/TrimControllerOverlay.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
+import com.android.gallery3d.common.ApiHelper;
+
/**
* The controller for the Trimming Video.
*/
@@ -41,36 +43,41 @@ public class TrimControllerOverlay extends CommonControllerOverlay {
if (mState == State.PLAYING) {
mPlayPauseReplayView.setVisibility(View.INVISIBLE);
}
- mPlayPauseReplayView.setAlpha(1f);
+ if (ApiHelper.HAS_OBJECT_ANIMATION) {
+ mPlayPauseReplayView.setAlpha(1f);
+ }
}
@Override
public void showPlaying() {
super.showPlaying();
+ if (ApiHelper.HAS_OBJECT_ANIMATION) {
+ // Add animation to hide the play button while playing.
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mPlayPauseReplayView, "alpha", 1f, 0f);
+ anim.setDuration(200);
+ anim.start();
+ anim.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
- // Add animation to hide the play button while playing.
- ObjectAnimator anim = ObjectAnimator.ofFloat(mPlayPauseReplayView, "alpha", 1f, 0f);
- anim.setDuration(200);
- anim.start();
- anim.addListener(new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- hidePlayButtonIfPlaying();
- }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- hidePlayButtonIfPlaying();
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ } else {
+ hidePlayButtonIfPlaying();
+ }
}
@Override
diff --git a/src/com/android/gallery3d/app/TrimVideo.java b/src/com/android/gallery3d/app/TrimVideo.java
index 38b403b10..7a76be5dc 100644
--- a/src/com/android/gallery3d/app/TrimVideo.java
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -16,8 +16,6 @@
package com.android.gallery3d.app;
-import android.app.ActionBar;
-import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -39,20 +37,25 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
import com.android.gallery3d.R;
import com.android.gallery3d.util.BucketNames;
+import com.android.gallery3d.util.SaveVideoFileInfo;
+import com.android.gallery3d.util.SaveVideoFileUtils;
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;
-public class TrimVideo extends Activity implements
+public class TrimVideo extends SherlockActivity implements
MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener,
ControllerOverlay.Listener {
private VideoView mVideoView;
+ private TextView mSaveVideoTextView;
private TrimControllerOverlay mController;
private Context mContext;
private Uri mUri;
@@ -70,13 +73,8 @@ public class TrimVideo extends Activity implements
private boolean mHasPaused = false;
private String mSrcVideoPath = null;
- private String mSaveFileName = null;
private static final String TIME_STAMP_NAME = "'TRIM'_yyyyMMdd_HHmmss";
- private File mSrcFile = null;
- private File mDstFile = null;
- private File mSaveDirectory = null;
- // For showing the result.
- private String saveFolderName = null;
+ private SaveVideoFileInfo mDstFileInfo = null;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -86,20 +84,21 @@ public class TrimVideo extends Activity implements
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- ActionBar actionBar = getActionBar();
+ ActionBar actionBar = getSupportActionBar();
int displayOptions = ActionBar.DISPLAY_SHOW_HOME;
actionBar.setDisplayOptions(0, displayOptions);
displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM;
actionBar.setDisplayOptions(displayOptions, displayOptions);
actionBar.setCustomView(R.layout.trim_menu);
- TextView mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
+ mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
mSaveVideoTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
trimVideo();
}
});
+ mSaveVideoTextView.setEnabled(false);
Intent intent = getIntent();
mUri = intent.getData();
@@ -221,72 +220,24 @@ public class TrimVideo extends Activity implements
mController.showPaused();
}
- // Copy from SaveCopyTask.java in terms of how to handle the destination
- // path and filename : querySource() and getSaveDirectory().
- private interface ContentResolverQueryCallback {
- void onCursorResult(Cursor cursor);
- }
-
- private void querySource(String[] projection, ContentResolverQueryCallback callback) {
- ContentResolver contentResolver = getContentResolver();
- Cursor cursor = null;
- try {
- cursor = contentResolver.query(mUri, projection, null, null, null);
- if ((cursor != null) && cursor.moveToNext()) {
- callback.onCursorResult(cursor);
- }
- } catch (Exception e) {
- // Ignore error for lacking the data column from the source.
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- private File getSaveDirectory() {
- final File[] dir = new File[1];
- querySource(new String[] {
- VideoColumns.DATA }, new ContentResolverQueryCallback() {
- @Override
- public void onCursorResult(Cursor cursor) {
- dir[0] = new File(cursor.getString(0)).getParentFile();
- }
- });
- return dir[0];
- }
-
- private void trimVideo() {
+ private boolean isModified() {
int delta = mTrimEndTime - mTrimStartTime;
+
// Considering that we only trim at sync frame, we don't want to trim
// when the time interval is too short or too close to the origin.
- if (delta < 100 ) {
- Toast.makeText(getApplicationContext(),
- getString(R.string.trim_too_short),
- Toast.LENGTH_SHORT).show();
- return;
- }
- if (Math.abs(mVideoView.getDuration() - delta) < 100) {
- // If no change has been made, go back
- onBackPressed();
- return;
- }
- // Use the default save directory if the source directory cannot be
- // saved.
- mSaveDirectory = getSaveDirectory();
- if ((mSaveDirectory == null) || !mSaveDirectory.canWrite()) {
- mSaveDirectory = new File(Environment.getExternalStorageDirectory(),
- BucketNames.DOWNLOAD);
- saveFolderName = getString(R.string.folder_download);
+ if (delta < 100 || Math.abs(mVideoView.getDuration() - delta) < 100) {
+ return false;
} else {
- saveFolderName = mSaveDirectory.getName();
+ return true;
}
- mSaveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(
- new Date(System.currentTimeMillis()));
+ }
- mDstFile = new File(mSaveDirectory, mSaveFileName + ".mp4");
- mSrcFile = new File(mSrcVideoPath);
+ private void trimVideo() {
+
+ mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
+ getContentResolver(), mUri, getString(R.string.folder_download));
+ final File mSrcFile = new File(mSrcVideoPath);
showProgressDialog();
@@ -294,9 +245,11 @@ public class TrimVideo extends Activity implements
@Override
public void run() {
try {
- TrimVideoUtils.startTrim(mSrcFile, mDstFile, mTrimStartTime, mTrimEndTime);
+ VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile,
+ mTrimStartTime, mTrimEndTime, mVideoView.getDuration());
// Update the database for adding a new video file.
- insertContent(mDstFile);
+ SaveVideoFileUtils.insertContent(mDstFileInfo,
+ getContentResolver(), mUri);
} catch (IOException e) {
e.printStackTrace();
}
@@ -305,7 +258,7 @@ public class TrimVideo extends Activity implements
@Override
public void run() {
Toast.makeText(getApplicationContext(),
- getString(R.string.save_into) + " " + saveFolderName,
+ getString(R.string.save_into, mDstFileInfo.mFolderName),
Toast.LENGTH_SHORT)
.show();
// TODO: change trimming into a service to avoid
@@ -315,7 +268,7 @@ public class TrimVideo extends Activity implements
mProgress = null;
// Show the result only when the activity not stopped.
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
- intent.setDataAndTypeAndNormalize(Uri.fromFile(mDstFile), "video/*");
+ intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
startActivity(intent);
finish();
@@ -338,53 +291,6 @@ public class TrimVideo extends Activity implements
mProgress.show();
}
- /**
- * Insert the content (saved file) with proper video properties.
- */
- private Uri insertContent(File file) {
- long nowInMs = System.currentTimeMillis();
- long nowInSec = nowInMs / 1000;
- final ContentValues values = new ContentValues(12);
- values.put(Video.Media.TITLE, mSaveFileName);
- values.put(Video.Media.DISPLAY_NAME, file.getName());
- values.put(Video.Media.MIME_TYPE, "video/mp4");
- values.put(Video.Media.DATE_TAKEN, nowInMs);
- values.put(Video.Media.DATE_MODIFIED, nowInSec);
- values.put(Video.Media.DATE_ADDED, nowInSec);
- values.put(Video.Media.DATA, file.getAbsolutePath());
- values.put(Video.Media.SIZE, file.length());
- // Copy the data taken and location info from src.
- String[] projection = new String[] {
- VideoColumns.DATE_TAKEN,
- VideoColumns.LATITUDE,
- VideoColumns.LONGITUDE,
- VideoColumns.RESOLUTION,
- };
-
- // Copy some info from the source file.
- querySource(projection, new ContentResolverQueryCallback() {
- @Override
- public void onCursorResult(Cursor cursor) {
- long timeTaken = cursor.getLong(0);
- if (timeTaken > 0) {
- values.put(Video.Media.DATE_TAKEN, timeTaken);
- }
- double latitude = cursor.getDouble(1);
- double longitude = cursor.getDouble(2);
- // TODO: Change || to && after the default location issue is
- // fixed.
- if ((latitude != 0f) || (longitude != 0f)) {
- values.put(Video.Media.LATITUDE, latitude);
- values.put(Video.Media.LONGITUDE, longitude);
- }
- values.put(Video.Media.RESOLUTION, cursor.getString(3));
-
- }
- });
-
- return getContentResolver().insert(Video.Media.EXTERNAL_CONTENT_URI, values);
- }
-
@Override
public void onPlayPause() {
if (mVideoView.isPlaying()) {
@@ -410,6 +316,8 @@ public class TrimVideo extends Activity implements
mTrimStartTime = start;
mTrimEndTime = end;
setProgress();
+ // Enable save if there's modifications
+ mSaveVideoTextView.setEnabled(isModified());
}
@Override
diff --git a/src/com/android/gallery3d/app/TrimVideoUtils.java b/src/com/android/gallery3d/app/VideoUtils.java
index ae9b1e9ce..8ffc3d5eb 100644
--- a/src/com/android/gallery3d/app/TrimVideoUtils.java
+++ b/src/com/android/gallery3d/app/VideoUtils.java
@@ -19,6 +19,7 @@
package com.android.gallery3d.app;
+import com.android.gallery3d.util.SaveVideoFileInfo;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.googlecode.mp4parser.authoring.Movie;
@@ -36,12 +37,46 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-/**
- * Shortens/Crops a track
- */
-public class TrimVideoUtils {
+public class VideoUtils {
- public static void startTrim(File src, File dst, int startMs, int endMs) throws IOException {
+ public static void startMute(String filePath, SaveVideoFileInfo dstFileInfo) throws IOException {
+ File dst = dstFileInfo.mFile;
+ File src = new File(filePath);
+ RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
+ Movie movie = MovieCreator.build(randomAccessFile.getChannel());
+
+ // remove all tracks we will create new tracks from the old
+ List<Track> tracks = movie.getTracks();
+ movie.setTracks(new LinkedList<Track>());
+
+ for (Track track : tracks) {
+ if (track.getHandler().equals("vide")) {
+ movie.addTrack(track);
+ }
+ }
+ writeMovieIntoFile(dst, movie);
+ randomAccessFile.close();
+ }
+
+ private static void writeMovieIntoFile(File dst, Movie movie)
+ throws IOException {
+ if (!dst.exists()) {
+ dst.createNewFile();
+ }
+
+ IsoFile out = new DefaultMp4Builder().build(movie);
+ FileOutputStream fos = new FileOutputStream(dst);
+ FileChannel fc = fos.getChannel();
+ out.getBox(fc); // This one build up the memory.
+
+ fc.close();
+ fos.close();
+ }
+
+ /**
+ * Shortens/Crops a track
+ */
+ public static void startTrim(File src, File dst, int startMs, int endMs, int totalMs) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
Movie movie = MovieCreator.build(randomAccessFile.getChannel());
@@ -100,18 +135,7 @@ public class TrimVideoUtils {
}
movie.addTrack(new CroppedTrack(track, startSample, endSample));
}
- IsoFile out = new DefaultMp4Builder().build(movie);
-
- if (!dst.exists()) {
- dst.createNewFile();
- }
-
- FileOutputStream fos = new FileOutputStream(dst);
- FileChannel fc = fos.getChannel();
- out.getBox(fc); // This one build up the memory.
-
- fc.close();
- fos.close();
+ writeMovieIntoFile(dst, movie);
randomAccessFile.close();
}
diff --git a/src/com/android/gallery3d/app/Wallpaper.java b/src/com/android/gallery3d/app/Wallpaper.java
index 996d3f080..1bbe8d2c6 100644
--- a/src/com/android/gallery3d/app/Wallpaper.java
+++ b/src/com/android/gallery3d/app/Wallpaper.java
@@ -26,6 +26,8 @@ import android.os.Bundle;
import android.view.Display;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.CropExtras;
/**
* Wallpaper picker for the gallery application. This just redirects to the
@@ -98,19 +100,18 @@ public class Wallpaper extends Activity {
Point size = getDefaultDisplaySize(new Point());
float spotlightX = (float) size.x / width;
float spotlightY = (float) size.y / height;
- Intent request = new Intent(CropImage.ACTION_CROP)
+ Intent request = new Intent(FilterShowActivity.CROP_ACTION)
.setDataAndType(mPickedItem, IMAGE_TYPE)
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
- .putExtra(CropImage.KEY_OUTPUT_X, width)
- .putExtra(CropImage.KEY_OUTPUT_Y, height)
- .putExtra(CropImage.KEY_ASPECT_X, width)
- .putExtra(CropImage.KEY_ASPECT_Y, height)
- .putExtra(CropImage.KEY_SPOTLIGHT_X, spotlightX)
- .putExtra(CropImage.KEY_SPOTLIGHT_Y, spotlightY)
- .putExtra(CropImage.KEY_SCALE, true)
- .putExtra(CropImage.KEY_SCALE_UP_IF_NEEDED, true)
- .putExtra(CropImage.KEY_NO_FACE_DETECTION, true)
- .putExtra(CropImage.KEY_SET_AS_WALLPAPER, true);
+ .putExtra(CropExtras.KEY_OUTPUT_X, width)
+ .putExtra(CropExtras.KEY_OUTPUT_Y, height)
+ .putExtra(CropExtras.KEY_ASPECT_X, width)
+ .putExtra(CropExtras.KEY_ASPECT_Y, height)
+ .putExtra(CropExtras.KEY_SPOTLIGHT_X, spotlightX)
+ .putExtra(CropExtras.KEY_SPOTLIGHT_Y, spotlightY)
+ .putExtra(CropExtras.KEY_SCALE, true)
+ .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true)
+ .putExtra(CropExtras.KEY_SET_AS_WALLPAPER, true);
startActivity(request);
finish();
}
diff --git a/src/com/android/gallery3d/data/Exif.java b/src/com/android/gallery3d/data/Exif.java
index ba5862a86..30aba7e97 100644
--- a/src/com/android/gallery3d/data/Exif.java
+++ b/src/com/android/gallery3d/data/Exif.java
@@ -18,144 +18,55 @@ package com.android.gallery3d.data;
import android.util.Log;
+import com.android.gallery3d.exif.ExifInvalidFormatException;
+import com.android.gallery3d.exif.ExifParser;
+import com.android.gallery3d.exif.ExifTag;
+
import java.io.IOException;
import java.io.InputStream;
public class Exif {
- private static final String TAG = "CameraExif";
+ private static final String TAG = "GalleryExif";
public static int getOrientation(InputStream is) {
if (is == null) {
return 0;
}
- byte[] buf = new byte[8];
- int length = 0;
-
- // ISO/IEC 10918-1:1993(E)
- while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
- int marker = buf[1] & 0xFF;
-
- // Check if the marker is a padding.
- if (marker == 0xFF) {
- continue;
- }
-
- // Check if the marker is SOI or TEM.
- if (marker == 0xD8 || marker == 0x01) {
- continue;
- }
- // Check if the marker is EOI or SOS.
- if (marker == 0xD9 || marker == 0xDA) {
- return 0;
- }
-
- // Get the length and check if it is reasonable.
- if (!read(is, buf, 2)) {
- return 0;
- }
- length = pack(buf, 0, 2, false);
- if (length < 2) {
- Log.e(TAG, "Invalid length");
- return 0;
- }
- length -= 2;
-
- // Break if the marker is EXIF in APP1.
- if (marker == 0xE1 && length >= 6) {
- if (!read(is, buf, 6)) return 0;
- length -= 6;
- if (pack(buf, 0, 4, false) == 0x45786966 &&
- pack(buf, 4, 2, false) == 0) {
- break;
- }
- }
-
- // Skip other markers.
- try {
- is.skip(length);
- } catch (IOException ex) {
- return 0;
- }
- length = 0;
- }
-
- // JEITA CP-3451 Exif Version 2.2
- if (length > 8) {
- int offset = 0;
- byte[] jpeg = new byte[length];
- if (!read(is, jpeg, length)) {
- return 0;
- }
-
- // Identify the byte order.
- int tag = pack(jpeg, offset, 4, false);
- if (tag != 0x49492A00 && tag != 0x4D4D002A) {
- Log.e(TAG, "Invalid byte order");
- return 0;
- }
- boolean littleEndian = (tag == 0x49492A00);
-
- // Get the offset and check if it is reasonable.
- int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
- if (count < 10 || count > length) {
- Log.e(TAG, "Invalid offset");
- return 0;
- }
- offset += count;
- length -= count;
-
- // Get the count and go through all the elements.
- count = pack(jpeg, offset - 2, 2, littleEndian);
- while (count-- > 0 && length >= 12) {
- // Get the tag and check if it is orientation.
- tag = pack(jpeg, offset, 2, littleEndian);
- if (tag == 0x0112) {
- // We do not really care about type and count, do we?
- int orientation = pack(jpeg, offset + 8, 2, littleEndian);
- switch (orientation) {
- case 1:
- return 0;
- case 3:
- return 180;
- case 6:
- return 90;
- case 8:
- return 270;
+ try {
+ ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ if (event == ExifParser.EVENT_NEW_TAG) {
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifTag.TAG_ORIENTATION &&
+ tag.hasValue()) {
+ int orient = (int) tag.getValueAt(0);
+ switch (orient) {
+ case ExifTag.Orientation.TOP_LEFT:
+ return 0;
+ case ExifTag.Orientation.BOTTOM_LEFT:
+ return 180;
+ case ExifTag.Orientation.RIGHT_TOP:
+ return 90;
+ case ExifTag.Orientation.RIGHT_BOTTOM:
+ return 270;
+ default:
+ Log.i(TAG, "Unsupported orientation");
+ return 0;
+ }
}
- Log.i(TAG, "Unsupported orientation");
- return 0;
}
- offset += 12;
- length -= 12;
+ event = parser.next();
}
- }
-
- Log.i(TAG, "Orientation not found");
- return 0;
- }
-
- private static int pack(byte[] bytes, int offset, int length,
- boolean littleEndian) {
- int step = 1;
- if (littleEndian) {
- offset += length - 1;
- step = -1;
- }
-
- int value = 0;
- while (length-- > 0) {
- value = (value << 8) | (bytes[offset] & 0xFF);
- offset += step;
- }
- return value;
- }
-
- private static boolean read(InputStream is, byte[] buf, int length) {
- try {
- return is.read(buf, 0, length) == length;
- } catch (IOException ex) {
- return false;
+ Log.i(TAG, "Orientation not found");
+ return 0;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read EXIF orientation", e);
+ return 0;
+ } catch (ExifInvalidFormatException e) {
+ Log.w(TAG, "Failed to read EXIF orientation", e);
+ return 0;
}
}
}
diff --git a/src/com/android/gallery3d/data/LocalAlbum.java b/src/com/android/gallery3d/data/LocalAlbum.java
index e05aac01b..6c5feb5c8 100644
--- a/src/com/android/gallery3d/data/LocalAlbum.java
+++ b/src/com/android/gallery3d/data/LocalAlbum.java
@@ -61,7 +61,7 @@ public class LocalAlbum extends MediaSet {
mApplication = application;
mResolver = application.getContentResolver();
mBucketId = bucketId;
- mName = getLocalizedName(application.getResources(), bucketId, name);
+ mName = name;
mIsImage = isImage;
if (isImage) {
@@ -245,7 +245,7 @@ public class LocalAlbum extends MediaSet {
@Override
public String getName() {
- return mName;
+ return getLocalizedName(mApplication.getResources(), mBucketId, mName);
}
@Override
diff --git a/src/com/android/gallery3d/data/LocalMergeAlbum.java b/src/com/android/gallery3d/data/LocalMergeAlbum.java
index cbaf82fff..f0b5e5726 100644
--- a/src/com/android/gallery3d/data/LocalMergeAlbum.java
+++ b/src/com/android/gallery3d/data/LocalMergeAlbum.java
@@ -41,7 +41,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener {
private final Comparator<MediaItem> mComparator;
private final MediaSet[] mSources;
- private String mName;
private FetchCache[] mFetcher;
private int mSupportedOperation;
private int mBucketId;
@@ -54,7 +53,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener {
super(path, INVALID_DATA_VERSION);
mComparator = comparator;
mSources = sources;
- mName = sources.length == 0 ? "" : sources[0].getName();
mBucketId = bucketId;
for (MediaSet set : mSources) {
set.addContentListener(this);
@@ -82,7 +80,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener {
mSupportedOperation = supported;
mIndex.clear();
mIndex.put(0, new int[mSources.length]);
- mName = mSources.length == 0 ? "" : mSources[0].getName();
}
private void invalidateCache() {
@@ -111,7 +108,7 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener {
@Override
public String getName() {
- return mName;
+ return mSources.length == 0 ? "" : mSources[0].getName();
}
@Override
diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java
index 44b853901..b1e1cb3aa 100644
--- a/src/com/android/gallery3d/data/LocalVideo.java
+++ b/src/com/android/gallery3d/data/LocalVideo.java
@@ -180,7 +180,7 @@ public class LocalVideo extends LocalMediaItem {
@Override
public int getSupportedOperations() {
- return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM;
+ return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM | SUPPORT_MUTE;
}
@Override
diff --git a/src/com/android/gallery3d/data/MediaDetails.java b/src/com/android/gallery3d/data/MediaDetails.java
index 298224729..16716dae4 100644
--- a/src/com/android/gallery3d/data/MediaDetails.java
+++ b/src/com/android/gallery3d/data/MediaDetails.java
@@ -19,9 +19,15 @@ package com.android.gallery3d.data;
import android.media.ExifInterface;
import com.android.gallery3d.R;
-import com.android.gallery3d.common.ExifTags;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifData;
+import com.android.gallery3d.exif.ExifInvalidFormatException;
+import com.android.gallery3d.exif.ExifReader;
+import com.android.gallery3d.exif.ExifTag;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
@@ -105,10 +111,18 @@ public class MediaDetails implements Iterable<Entry<Integer, Object>> {
return mUnits.get(index);
}
- private static void setExifData(MediaDetails details, ExifInterface exif, String tag,
+ private static void setExifData(MediaDetails details, ExifTag tag,
int key) {
- String value = exif.getAttribute(tag);
- if (value != null) {
+ if (tag != null) {
+ String value = null;
+ int type = tag.getDataType();
+ if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) {
+ value = String.valueOf(tag.getRational(0).toDouble());
+ } else if (type == ExifTag.TYPE_ASCII) {
+ value = tag.getString();
+ } else {
+ value = String.valueOf(tag.getValueAt(0));
+ }
if (key == MediaDetails.INDEX_FLASH) {
MediaDetails.FlashState state = new MediaDetails.FlashState(
Integer.valueOf(value.toString()));
@@ -120,29 +134,37 @@ public class MediaDetails implements Iterable<Entry<Integer, Object>> {
}
public static void extractExifInfo(MediaDetails details, String filePath) {
+ InputStream is = null;
try {
- ExifInterface exif = new ExifInterface(filePath);
- setExifData(details, exif, ExifInterface.TAG_FLASH, MediaDetails.INDEX_FLASH);
- setExifData(details, exif, ExifInterface.TAG_IMAGE_WIDTH, MediaDetails.INDEX_WIDTH);
- setExifData(details, exif, ExifInterface.TAG_IMAGE_LENGTH,
- MediaDetails.INDEX_HEIGHT);
- setExifData(details, exif, ExifInterface.TAG_MAKE, MediaDetails.INDEX_MAKE);
- setExifData(details, exif, ExifInterface.TAG_MODEL, MediaDetails.INDEX_MODEL);
- setExifData(details, exif, ExifTags.TAG_APERTURE, MediaDetails.INDEX_APERTURE);
- setExifData(details, exif, ExifTags.TAG_ISO, MediaDetails.INDEX_ISO);
- setExifData(details, exif, ExifInterface.TAG_WHITE_BALANCE,
+ is = new FileInputStream(filePath);
+ ExifData data = new ExifReader().read(is);
+ setExifData(details, data.getTag(ExifTag.TAG_FLASH), MediaDetails.INDEX_FLASH);
+ setExifData(details, data.getTag(ExifTag.TAG_IMAGE_WIDTH), MediaDetails.INDEX_WIDTH);
+ setExifData(details, data.getTag(ExifTag.TAG_IMAGE_LENGTH), MediaDetails.INDEX_HEIGHT);
+ setExifData(details, data.getTag(ExifTag.TAG_MAKE), MediaDetails.INDEX_MAKE);
+ setExifData(details, data.getTag(ExifTag.TAG_MODEL),MediaDetails.INDEX_MODEL);
+ setExifData(details, data.getTag(ExifTag.TAG_APERTURE_VALUE),
+ MediaDetails.INDEX_APERTURE);
+ setExifData(details, data.getTag(ExifTag.TAG_ISO_SPEED_RATINGS),
+ MediaDetails.INDEX_ISO);
+ setExifData(details, data.getTag(ExifTag.TAG_WHITE_BALANCE),
MediaDetails.INDEX_WHITE_BALANCE);
- setExifData(details, exif, ExifTags.TAG_EXPOSURE_TIME,
+ setExifData(details, data.getTag(ExifTag.TAG_EXPOSURE_TIME),
MediaDetails.INDEX_EXPOSURE_TIME);
-
- double data = exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, 0);
- if (data != 0f) {
- details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH, data);
+ ExifTag focalTag = data.getTag(ExifTag.TAG_FOCAL_LENGTH);
+ if (focalTag != null) {
+ details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH,
+ focalTag.getRational(0).toDouble());
details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm);
}
} catch (IOException ex) {
// ignore it.
Log.w(TAG, "", ex);
+ } catch (ExifInvalidFormatException ex) {
+ // ignore it.
+ Log.w(TAG, "", ex);
+ } finally {
+ Utils.closeSilently(is);
}
}
}
diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java
index a41b275fb..9c82661f6 100644
--- a/src/com/android/gallery3d/data/MediaObject.java
+++ b/src/com/android/gallery3d/data/MediaObject.java
@@ -41,6 +41,7 @@ public abstract class MediaObject {
public static final int SUPPORT_BACK = 1 << 14;
public static final int SUPPORT_ACTION = 1 << 15;
public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 16;
+ public static final int SUPPORT_MUTE = 1 << 17;
public static final int SUPPORT_ALL = 0xffffffff;
// These are the bits returned from getMediaType():
diff --git a/src/com/android/gallery3d/filtershow/CropExtras.java b/src/com/android/gallery3d/filtershow/CropExtras.java
new file mode 100644
index 000000000..7ed8f1eb5
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/CropExtras.java
@@ -0,0 +1,121 @@
+/*
+ * 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.filtershow;
+
+import android.net.Uri;
+
+public class CropExtras {
+
+ public static final String KEY_CROPPED_RECT = "cropped-rect";
+ public static final String KEY_OUTPUT_X = "outputX";
+ public static final String KEY_OUTPUT_Y = "outputY";
+ public static final String KEY_SCALE = "scale";
+ public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded";
+ public static final String KEY_ASPECT_X = "aspectX";
+ public static final String KEY_ASPECT_Y = "aspectY";
+ public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper";
+ public static final String KEY_RETURN_DATA = "return-data";
+ public static final String KEY_DATA = "data";
+ public static final String KEY_SPOTLIGHT_X = "spotlightX";
+ public static final String KEY_SPOTLIGHT_Y = "spotlightY";
+ public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
+ public static final String KEY_OUTPUT_FORMAT = "outputFormat";
+
+ private int mOutputX = 0;
+ private int mOutputY = 0;
+ private boolean mScaleUp = true;
+ private int mAspectX = 0;
+ private int mAspectY = 0;
+ private boolean mSetAsWallpaper = false;
+ private boolean mReturnData = false;
+ private Uri mExtraOutput = null;
+ private String mOutputFormat = null;
+ private boolean mShowWhenLocked = false;
+ private float mSpotlightX = 0;
+ private float mSpotlightY = 0;
+
+ public CropExtras(int outputX, int outputY, boolean scaleUp, int aspectX, int aspectY,
+ boolean setAsWallpaper, boolean returnData, Uri extraOutput, String outputFormat,
+ boolean showWhenLocked, float spotlightX, float spotlightY) {
+ mOutputX = outputX;
+ mOutputY = outputY;
+ mScaleUp = scaleUp;
+ mAspectX = aspectX;
+ mAspectY = aspectY;
+ mSetAsWallpaper = setAsWallpaper;
+ mReturnData = returnData;
+ mExtraOutput = extraOutput;
+ mOutputFormat = outputFormat;
+ mShowWhenLocked = showWhenLocked;
+ mSpotlightX = spotlightX;
+ mSpotlightY = spotlightY;
+ }
+
+ public CropExtras(CropExtras c) {
+ this(c.mOutputX, c.mOutputY, c.mScaleUp, c.mAspectX, c.mAspectY, c.mSetAsWallpaper,
+ c.mReturnData, c.mExtraOutput, c.mOutputFormat, c.mShowWhenLocked,
+ c.mSpotlightX, c.mSpotlightY);
+ }
+
+ public int getOutputX() {
+ return mOutputX;
+ }
+
+ public int getOutputY() {
+ return mOutputY;
+ }
+
+ public boolean getScaleUp() {
+ return mScaleUp;
+ }
+
+ public int getAspectX() {
+ return mAspectX;
+ }
+
+ public int getAspectY() {
+ return mAspectY;
+ }
+
+ public boolean getSetAsWallpaper() {
+ return mSetAsWallpaper;
+ }
+
+ public boolean getReturnData() {
+ return mReturnData;
+ }
+
+ public Uri getExtraOutput() {
+ return mExtraOutput;
+ }
+
+ public String getOutputFormat() {
+ return mOutputFormat;
+ }
+
+ public boolean getShowWhenLocked() {
+ return mShowWhenLocked;
+ }
+
+ public float getSpotlightX() {
+ return mSpotlightX;
+ }
+
+ public float getSpotlightY() {
+ return mSpotlightY;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index c1e4f6add..356a0a074 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -20,6 +20,7 @@ import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.ProgressDialog;
+import android.app.WallpaperManager;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Configuration;
@@ -32,6 +33,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -58,6 +60,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterBorder;
import com.android.gallery3d.filtershow.filters.ImageFilterBwFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterContrast;
+import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
import com.android.gallery3d.filtershow.filters.ImageFilterExposure;
import com.android.gallery3d.filtershow.filters.ImageFilterFx;
import com.android.gallery3d.filtershow.filters.ImageFilterHue;
@@ -65,6 +68,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilterParametricBorder;
import com.android.gallery3d.filtershow.filters.ImageFilterRS;
import com.android.gallery3d.filtershow.filters.ImageFilterSaturated;
import com.android.gallery3d.filtershow.filters.ImageFilterShadows;
+import com.android.gallery3d.filtershow.filters.ImageFilterSharpen;
import com.android.gallery3d.filtershow.filters.ImageFilterTinyPlanet;
import com.android.gallery3d.filtershow.filters.ImageFilterVibrance;
import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
@@ -72,13 +76,13 @@ import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
import com.android.gallery3d.filtershow.imageshow.ImageBorder;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageFlip;
+import com.android.gallery3d.filtershow.imageshow.ImageRedEyes;
import com.android.gallery3d.filtershow.imageshow.ImageRotate;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.ImageSmallBorder;
import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
import com.android.gallery3d.filtershow.imageshow.ImageStraighten;
import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet;
-import com.android.gallery3d.filtershow.imageshow.ImageWithIcon;
import com.android.gallery3d.filtershow.imageshow.ImageZoom;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.provider.SharedImageProvider;
@@ -90,6 +94,7 @@ import com.android.gallery3d.filtershow.ui.Spline;
import com.android.gallery3d.util.GalleryUtils;
import java.io.File;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Vector;
@@ -97,14 +102,19 @@ import java.util.Vector;
public class FilterShowActivity extends Activity implements OnItemClickListener,
OnShareTargetSelectedListener {
- public static final String CROP_ACTION = "com.android.camera.action.EDITOR_CROP";
+ // fields for supporting crop action
+ public static final String CROP_ACTION = "com.android.camera.action.CROP";
+ private CropExtras mCropExtras = null;
+
public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
+ public static final int MAX_BMAP_IN_INTENT = 990000;
private final PanelController mPanelController = new PanelController();
private ImageLoader mImageLoader = null;
private ImageShow mImageShow = null;
private ImageCurves mImageCurves = null;
private ImageBorder mImageBorders = null;
+ private ImageRedEyes mImageRedEyes = null;
private ImageStraighten mImageStraighten = null;
private ImageZoom mImageZoom = null;
private ImageCrop mImageCrop = null;
@@ -117,6 +127,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
private View mListGeometry = null;
private View mListColors = null;
private View mListFilterButtons = null;
+ private View mSaveButton = null;
private ImageButton mFxButton = null;
private ImageButton mBorderButton = null;
@@ -155,7 +166,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
ImageFilterRS.setRenderScriptContext(this);
ImageShow.setDefaultBackgroundColor(getResources().getColor(R.color.background_screen));
- ImageSmallFilter.setDefaultBackgroundColor(getResources().getColor(R.color.background_main_toolbar));
+ ImageSmallFilter.setDefaultBackgroundColor(getResources().getColor(
+ R.color.background_main_toolbar));
// TODO: get those values from XML.
ImageZoom.setZoomedSize(getPixelsFromDip(256));
FramedTextButton.setTextSize((int) getPixelsFromDip(14));
@@ -180,7 +192,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(R.layout.filtershow_actionbar);
- actionBar.getCustomView().setOnClickListener(new OnClickListener() {
+ mSaveButton = actionBar.getCustomView();
+ mSaveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
saveImage();
@@ -202,9 +215,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageRotate = (ImageRotate) findViewById(R.id.imageRotate);
mImageFlip = (ImageFlip) findViewById(R.id.imageFlip);
mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet);
+ mImageRedEyes = (ImageRedEyes) findViewById(R.id.imageRedEyes);
mImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
+ ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
mImageViews.add(mImageShow);
mImageViews.add(mImageCurves);
mImageViews.add(mImageBorders);
@@ -214,6 +229,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageViews.add(mImageRotate);
mImageViews.add(mImageFlip);
mImageViews.add(mImageTinyPlanet);
+ mImageViews.add(mImageRedEyes);
+ for (ImageShow imageShow : mImageViews) {
+ mImageLoader.addCacheListener(imageShow);
+ }
mListFx = findViewById(R.id.fxList);
mListBorders = findViewById(R.id.bordersList);
@@ -248,6 +267,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageFlip.setMaster(mImageShow);
mImageTinyPlanet.setImageLoader(mImageLoader);
mImageTinyPlanet.setMaster(mImageShow);
+ mImageRedEyes.setImageLoader(mImageLoader);
+ mImageRedEyes.setMaster(mImageShow);
mPanelController.setActivity(this);
@@ -260,6 +281,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mPanelController.addImageView(findViewById(R.id.imageFlip));
mPanelController.addImageView(findViewById(R.id.imageZoom));
mPanelController.addImageView(findViewById(R.id.imageTinyPlanet));
+ mPanelController.addImageView(findViewById(R.id.imageRedEyes));
mPanelController.addPanel(mFxButton, mListFx, 0);
mPanelController.addPanel(mBorderButton, mListBorders, 1);
@@ -269,86 +291,40 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.rotateButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton));
+ mPanelController.addComponent(mGeometryButton, findViewById(R.id.redEyeButton));
mPanelController.addPanel(mColorsButton, mListColors, 3);
- int[] recastIDs = {
- R.id.tinyplanetButton,
- R.id.vignetteButton,
- R.id.vibranceButton,
- R.id.contrastButton,
- R.id.saturationButton,
- R.id.bwfilterButton,
- R.id.wbalanceButton,
- R.id.hueButton,
- R.id.exposureButton,
- R.id.shadowRecoveryButton
- };
ImageFilter[] filters = {
new ImageFilterTinyPlanet(),
+ new ImageFilterWBalance(),
+ new ImageFilterExposure(),
new ImageFilterVignette(),
- new ImageFilterVibrance(),
new ImageFilterContrast(),
- new ImageFilterSaturated(),
- new ImageFilterBwFilter(),
- new ImageFilterWBalance(),
+ new ImageFilterShadows(),
+ new ImageFilterVibrance(),
+ new ImageFilterSharpen(),
+ new ImageFilterCurves(),
new ImageFilterHue(),
- new ImageFilterExposure(),
- new ImageFilterShadows()
+ new ImageFilterSaturated(),
+ new ImageFilterBwFilter()
};
for (int i = 0; i < filters.length; i++) {
ImageSmallFilter fView = new ImageSmallFilter(this);
- View v = listColors.findViewById(recastIDs[i]);
- int pos = listColors.indexOfChild(v);
- listColors.removeView(v);
-
filters[i].setParameter(filters[i].getPreviewParameter());
- if (v instanceof ImageButtonTitle)
- filters[i].setName(((ImageButtonTitle) v).getText());
+ filters[i].setName(getString(filters[i].getTextId()));
fView.setImageFilter(filters[i]);
fView.setController(this);
fView.setImageLoader(mImageLoader);
- fView.setId(recastIDs[i]);
- mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView, pos);
- }
-
- int[] overlayIDs = {
- R.id.sharpenButton,
- R.id.curvesButtonRGB
- };
- int[] overlayBitmaps = {
- R.drawable.filtershow_button_colors_sharpen,
- R.drawable.filtershow_button_colors_curve
- };
- int[] overlayNames = {
- R.string.sharpness,
- R.string.curvesRGB
- };
-
- for (int i = 0; i < overlayIDs.length; i++) {
- ImageWithIcon fView = new ImageWithIcon(this);
- View v = listColors.findViewById(overlayIDs[i]);
- int pos = listColors.indexOfChild(v);
- listColors.removeView(v);
- final int sid = overlayNames[i];
- ImageFilterExposure efilter = new ImageFilterExposure() {
- {
- mName = getString(sid);
- }
- };
- efilter.setParameter(-300);
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
- overlayBitmaps[i]);
-
- fView.setIcon(bitmap);
- fView.setImageFilter(efilter);
- fView.setController(this);
- fView.setImageLoader(mImageLoader);
- fView.setId(overlayIDs[i]);
+ fView.setId(filters[i].getButtonId());
+ if (filters[i].getOverlayBitmaps() != 0) {
+ Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
+ filters[i].getOverlayBitmaps());
+ fView.setOverlayBitmap(bitmap);
+ }
mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView, pos);
+ listColors.addView(fView);
}
mPanelController.addView(findViewById(R.id.applyEffect));
@@ -390,8 +366,37 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
pickImage();
}
+ // Handle behavior for various actions
String action = intent.getAction();
if (action.equalsIgnoreCase(CROP_ACTION)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
+ extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SCALE, true) &&
+ extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
+ extras.getInt(CropExtras.KEY_ASPECT_X, 0),
+ extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
+ extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
+ (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
+ extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
+ extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
+
+ if (mCropExtras.getShowWhenLocked()) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+ mImageShow.getImagePreset().mGeoData.setCropExtras(mCropExtras);
+
+ mImageCrop.setExtras(mCropExtras);
+ String s = getString(R.string.Fixed);
+ mImageCrop.setAspectString(s);
+ mImageCrop.setCropActionFlag(true);
+ mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0
+ && mCropExtras.getAspectY() > 0);
+ }
mPanelController.showComponent(findViewById(R.id.cropButton));
} else if (action.equalsIgnoreCase(TINY_PLANET_ACTION)) {
mPanelController.showComponent(findViewById(R.id.tinyplanetButton));
@@ -411,7 +416,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mLoadBitmapTask.execute(uri);
}
- private class LoadBitmapTask extends AsyncTask<Uri, Void, Boolean> {
+ private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
View mTinyPlanetButton;
int mBitmapSize;
@@ -422,19 +427,26 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
@Override
protected Boolean doInBackground(Uri... params) {
- mImageLoader.loadBitmap(params[0], mBitmapSize);
- publishProgress();
- return mImageLoader.queryLightCycle360();
+ if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
+ return false;
+ }
+ publishProgress(mImageLoader.queryLightCycle360());
+ return true;
}
@Override
- protected void onProgressUpdate(Void... values) {
+ protected void onProgressUpdate(Boolean... values) {
super.onProgressUpdate(values);
- if (isCancelled()) return;
+ if (isCancelled()) {
+ return;
+ }
final View filters = findViewById(R.id.filtersPanel);
final View loading = findViewById(R.id.loading);
loading.setVisibility(View.GONE);
filters.setVisibility(View.VISIBLE);
+ if (values[0]) {
+ mTinyPlanetButton.setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -442,9 +454,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
if (isCancelled()) {
return;
}
- if (result) {
- mTinyPlanetButton.setVisibility(View.VISIBLE);
+ if (!result) {
+ cannotLoadImage();
}
+
mLoadBitmapTask = null;
super.onPostExecute(result);
}
@@ -640,6 +653,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
return false;
}
+ public void enableSave(boolean enable) {
+ if (mSaveButton != null)
+ mSaveButton.setEnabled(enable);
+ }
+
private void fillListImages(LinearLayout listFilters) {
// TODO: use listview
// TODO: load the filters straight from the filesystem
@@ -990,17 +1008,88 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
}
}
+ private boolean mSaveToExtraUri = false;
+ private boolean mSaveAsWallpaper = false;
+ private boolean mReturnAsExtra = false;
+ private boolean outputted = false;
+
public void saveImage() {
- if (mImageShow.hasModifications()) {
- // Get the name of the album, to which the image will be saved
- File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
- int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
- String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
- showSavingProgress(albumName);
- mImageShow.saveImage(this, null);
- } else {
- finish();
+ // boolean outputted = false;
+ if (mCropExtras != null) {
+ if (mCropExtras.getExtraOutput() != null) {
+ mSaveToExtraUri = true;
+ outputted = true;
+ }
+ if (mCropExtras.getSetAsWallpaper()) {
+ mSaveAsWallpaper = true;
+ outputted = true;
+ }
+ if (mCropExtras.getReturnData()) {
+
+ mReturnAsExtra = true;
+ outputted = true;
+ }
+
+ if (outputted) {
+ mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true);
+ showSavingProgress(null);
+ mImageShow.returnFilteredResult(this);
+ }
+ }
+ if (!outputted) {
+ if (mImageShow.hasModifications()) {
+ // Get the name of the album, to which the image will be saved
+ File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
+ int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
+ String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
+ showSavingProgress(albumName);
+ mImageShow.saveImage(this, null);
+ } else {
+ done();
+ }
+ }
+ }
+
+ public void onFilteredResult(Bitmap filtered) {
+ Intent intent = new Intent();
+ intent.putExtra(CropExtras.KEY_CROPPED_RECT, mImageShow.getImageCropBounds());
+ if (mSaveToExtraUri) {
+ mImageShow.saveToUri(filtered, mCropExtras.getExtraOutput(),
+ mCropExtras.getOutputFormat(), this);
+ }
+ if (mSaveAsWallpaper) {
+ try {
+ WallpaperManager.getInstance(this).setBitmap(filtered);
+ } catch (IOException e) {
+ Log.w(LOGTAG, "fail to set wall paper", e);
+ }
}
+ if (mReturnAsExtra) {
+ if (filtered != null) {
+ int bmapSize = filtered.getRowBytes() * filtered.getHeight();
+ /*
+ * Max size of Binder transaction buffer is 1Mb, so constrain
+ * Bitmap to be somewhat less than this, otherwise we get
+ * TransactionTooLargeExceptions.
+ */
+ if (bmapSize > MAX_BMAP_IN_INTENT) {
+ Log.w(LOGTAG, "Bitmap too large to be returned via intent");
+ } else {
+ intent.putExtra(CropExtras.KEY_DATA, filtered);
+ }
+ }
+ }
+ setResult(RESULT_OK, intent);
+ if (!mSaveToExtraUri) {
+ done();
+ }
+ }
+
+ public void done() {
+ if (outputted) {
+ hideSavingProgress();
+ }
+ finish();
}
static {
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index 52bf98aa7..1d9cc22b5 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -42,6 +42,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
+import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.ui.FramedTextButton;
import com.android.gallery3d.filtershow.ui.ImageCurves;
@@ -56,6 +57,11 @@ public class PanelController implements OnClickListener {
private static int HORIZONTAL_MOVE = 1;
private static final int ANIM_DURATION = 200;
private static final String LOGTAG = "PanelController";
+ private boolean mFixedAspect = false;
+
+ public void setFixedAspect(boolean t) {
+ mFixedAspect = t;
+ }
class Panel {
private final View mView;
@@ -587,6 +593,27 @@ public class PanelController implements OnClickListener {
}
mUtilityPanel.hideAspectButtons();
mUtilityPanel.hideCurvesButtons();
+
+ if (view instanceof ImageSmallFilter) {
+ ImageSmallFilter component = (ImageSmallFilter) view;
+ ImageFilter filter = component.getImageFilter();
+ if (filter.getEditingViewId() != 0) {
+ mCurrentImage = showImageView(filter.getEditingViewId());
+ mCurrentImage.setShowControls(filter.showEditingControls());
+ String ename = mCurrentImage.getContext().getString(filter.getTextId());
+ mUtilityPanel.setEffectName(ename);
+ if (view.getId() == R.id.curvesButtonRGB) {
+ // TODO: delegate to the filter / editing view the management of the
+ // panel accessory view
+ mUtilityPanel.setShowParameter(false);
+ mUtilityPanel.showCurvesButtons();
+ }
+ ensureFilter(ename);
+ mCurrentImage.select();
+ }
+ return;
+ }
+
switch (view.getId()) {
case R.id.tinyplanetButton: {
mCurrentImage = showImageView(R.id.imageTinyPlanet).setShowControls(true);
@@ -606,11 +633,13 @@ public class PanelController implements OnClickListener {
String ename = mCurrentImage.getContext().getString(R.string.crop);
mUtilityPanel.setEffectName(ename);
mUtilityPanel.setShowParameter(false);
- if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed){
- ((ImageCrop) mCurrentImage).applyClear();
+ if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed) {
+ ((ImageCrop) mCurrentImage).clear();
mUtilityPanel.firstTimeCropDisplayed = false;
}
- mUtilityPanel.showAspectButtons();
+ if (!mFixedAspect) {
+ mUtilityPanel.showAspectButtons();
+ }
break;
}
case R.id.rotateButton: {
@@ -626,89 +655,8 @@ public class PanelController implements OnClickListener {
mUtilityPanel.setShowParameter(false);
break;
}
- case R.id.vignetteButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.vignette);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.curvesButtonRGB: {
- ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
- String ename = curves.getContext().getString(R.string.curvesRGB);
- mUtilityPanel.setEffectName(ename);
- mUtilityPanel.setShowParameter(false);
- mUtilityPanel.showCurvesButtons();
- mCurrentImage = curves;
- ensureFilter(ename);
- break;
- }
- case R.id.sharpenButton: {
- mCurrentImage = showImageView(R.id.imageZoom).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.sharpness);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.contrastButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.contrast);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.saturationButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.saturation);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.bwfilterButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.bwfilter);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.wbalanceButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(false);
- String ename = mCurrentImage.getContext().getString(R.string.wbalance);
- mUtilityPanel.setEffectName(ename);
- mUtilityPanel.setShowParameter(false);
- ensureFilter(ename);
- break;
- }
- case R.id.hueButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.hue);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.exposureButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.exposure);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.vibranceButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.vibrance);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.shadowRecoveryButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.shadow_recovery);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
case R.id.redEyeButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
+ mCurrentImage = showImageView(R.id.imageRedEyes).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.redeye);
mUtilityPanel.setEffectName(ename);
ensureFilter(ename);
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index afef58aad..a1a1ba186 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.cache;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -26,6 +27,7 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.Bitmap.CompressFormat;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
@@ -36,19 +38,27 @@ import com.adobe.xmp.XMPMeta;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInvalidFormatException;
+import com.android.gallery3d.exif.ExifParser;
+import com.android.gallery3d.exif.ExifTag;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.HistoryAdapter;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.tools.BitmapTask;
import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.util.InterruptableOutputStream;
import com.android.gallery3d.util.XmpUtilHelper;
import java.io.Closeable;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Vector;
import java.util.concurrent.locks.ReentrantLock;
@@ -69,13 +79,16 @@ public class ImageLoader {
private FilterShowActivity mActivity = null;
- public static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
- public static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
+ public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
+ public static final int DEFAULT_COMPRESS_QUALITY = 95;
+
+ public static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
+ public static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
public static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
public static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
- public static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
- public static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
- public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
+ public static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
+ public static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
+ public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
public static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
private Context mContext = null;
@@ -101,18 +114,24 @@ public class ImageLoader {
return mActivity;
}
- public void loadBitmap(Uri uri,int size) {
+ public boolean loadBitmap(Uri uri, int size) {
mLoadingLock.lock();
mUri = uri;
mOrientation = getOrientation(mContext, uri);
mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
if (mOriginalBitmapSmall == null) {
// Couldn't read the bitmap, let's exit
- mActivity.cannotLoadImage();
+ mLoadingLock.unlock();
+ return false;
}
mOriginalBitmapLarge = loadScaledBitmap(uri, size);
+ if (mOriginalBitmapLarge == null) {
+ mLoadingLock.unlock();
+ return false;
+ }
updateBitmaps();
mLoadingLock.unlock();
+ return true;
}
public Uri getUri() {
@@ -135,21 +154,25 @@ public class ImageLoader {
MediaStore.Images.ImageColumns.ORIENTATION
},
null, null, null);
- 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{
+ 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){
+ } catch (SQLiteException e) {
return ExifInterface.ORIENTATION_UNDEFINED;
} catch (IllegalArgumentException e) {
return ExifInterface.ORIENTATION_UNDEFINED;
@@ -160,12 +183,27 @@ public class ImageLoader {
static int getOrientationFromPath(String path) {
int orientation = -1;
+ InputStream is = null;
try {
- ExifInterface EXIF = new ExifInterface(path);
- orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION,
- 1);
+ is = new FileInputStream(path);
+ ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ if (event == ExifParser.EVENT_NEW_TAG) {
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifTag.TAG_ORIENTATION) {
+ orientation = (int) tag.getValueAt(0);
+ break;
+ }
+ }
+ event = parser.next();
+ }
} catch (IOException e) {
e.printStackTrace();
+ } catch (ExifInvalidFormatException e) {
+ e.printStackTrace();
+ } finally {
+ Utils.closeSilently(is);
}
return orientation;
}
@@ -181,46 +219,46 @@ public class ImageLoader {
warnListeners();
}
- public static Bitmap rotateToPortrait(Bitmap bitmap,int ori) {
- Matrix matrix = new Matrix();
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- if (ori == ORI_ROTATE_90 ||
- ori == ORI_ROTATE_270 ||
- ori == ORI_TRANSPOSE||
- ori == ORI_TRANSVERSE) {
- int tmp = w;
- w = h;
- h = tmp;
- }
- switch(ori){
- case ORI_ROTATE_90:
- matrix.setRotate(90,w/2f,h/2f);
- break;
- case ORI_ROTATE_180:
- matrix.setRotate(180,w/2f,h/2f);
- break;
- case ORI_ROTATE_270:
- matrix.setRotate(270,w/2f,h/2f);
- break;
- case ORI_FLIP_HOR:
- matrix.preScale(-1, 1);
- break;
- case ORI_FLIP_VERT:
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSPOSE:
- matrix.setRotate(90,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSVERSE:
- matrix.setRotate(270,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_NORMAL:
- default:
- return bitmap;
- }
+ public static Bitmap rotateToPortrait(Bitmap bitmap, int ori) {
+ Matrix matrix = new Matrix();
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ if (ori == ORI_ROTATE_90 ||
+ ori == ORI_ROTATE_270 ||
+ ori == ORI_TRANSPOSE ||
+ ori == ORI_TRANSVERSE) {
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+ switch (ori) {
+ case ORI_ROTATE_90:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_180:
+ matrix.setRotate(180, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_270:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ break;
+ case ORI_FLIP_HOR:
+ matrix.preScale(-1, 1);
+ break;
+ case ORI_FLIP_VERT:
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSPOSE:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSVERSE:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_NORMAL:
+ default:
+ return bitmap;
+ }
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
@@ -253,6 +291,7 @@ public class ImageLoader {
}
static final int MAX_BITMAP_DIM = 2048;
+
private Bitmap loadScaledBitmap(Uri uri, int size) {
InputStream is = null;
try {
@@ -338,7 +377,8 @@ public class ImageLoader {
}
};
- // TODO: this currently does the loading + filtering on the UI thread -- need to
+ // TODO: this currently does the loading + filtering on the UI thread --
+ // need to
// move this to a background thread.
public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
boolean force) {
@@ -354,6 +394,7 @@ public class ImageLoader {
bmp2 = imagePreset.apply(bmp2);
imagePreset.setScaleFactor(scaleFactor);
mZoomCache.setImage(imagePreset, bounds, bmp2);
+ mLoadingLock.unlock();
return bmp2;
}
}
@@ -366,9 +407,11 @@ public class ImageLoader {
boolean hiRes) {
mLoadingLock.lock();
if (mOriginalBitmapSmall == null) {
+ mLoadingLock.unlock();
return null;
}
if (mOriginalBitmapLarge == null) {
+ mLoadingLock.unlock();
return null;
}
@@ -415,6 +458,109 @@ public class ImageLoader {
}).execute(preset);
}
+ public static Bitmap loadMutableBitmap(Context context, Uri sourceUri)
+ throws FileNotFoundException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
+ // exist)
+ options.inMutable = true;
+
+ InputStream is = context.getContentResolver().openInputStream(sourceUri);
+ Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+ int orientation = ImageLoader.getOrientation(context, sourceUri);
+ bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
+ return bitmap;
+ }
+
+ public void returnFilteredResult(ImagePreset preset,
+ final FilterShowActivity filterShowActivity) {
+ preset.setIsHighQuality(true);
+ preset.setScaleFactor(1.0f);
+
+ BitmapTask.Callbacks<ImagePreset> cb = new BitmapTask.Callbacks<ImagePreset>() {
+
+ @Override
+ public void onComplete(Bitmap result) {
+ filterShowActivity.onFilteredResult(result);
+ }
+
+ @Override
+ public void onCancel() {
+ }
+
+ @Override
+ public Bitmap onExecute(ImagePreset param) {
+ if (param == null) {
+ return null;
+ }
+ try {
+ Bitmap bitmap = param.apply(loadMutableBitmap(mContext, mUri));
+ return bitmap;
+ } catch (FileNotFoundException ex) {
+ Log.w(LOGTAG, "Failed to save image!", ex);
+ return null;
+ }
+ }
+ };
+
+ (new BitmapTask<ImagePreset>(cb)).execute(preset);
+ }
+
+ private String getFileExtension(String requestFormat) {
+ String outputFormat = (requestFormat == null)
+ ? "jpg"
+ : requestFormat;
+ outputFormat = outputFormat.toLowerCase();
+ return (outputFormat.equals("png") || outputFormat.equals("gif"))
+ ? "png" // We don't support gif compression.
+ : "jpg";
+ }
+
+ private CompressFormat convertExtensionToCompressFormat(String extension) {
+ return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
+ }
+
+ public void saveToUri(Bitmap bmap, Uri uri, final String outputFormat,
+ final FilterShowActivity filterShowActivity) {
+
+ OutputStream out = null;
+ try {
+ out = filterShowActivity.getContentResolver().openOutputStream(uri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "cannot write output", e);
+ out = null;
+ } finally {
+ if (bmap == null || out == null) {
+ return;
+ }
+ }
+
+ final InterruptableOutputStream ios = new InterruptableOutputStream(out);
+
+ BitmapTask.Callbacks<Bitmap> cb = new BitmapTask.Callbacks<Bitmap>() {
+
+ @Override
+ public void onComplete(Bitmap result) {
+ filterShowActivity.done();
+ }
+
+ @Override
+ public void onCancel() {
+ ios.interrupt();
+ }
+
+ @Override
+ public Bitmap onExecute(Bitmap param) {
+ CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(outputFormat));
+ param.compress(cf, DEFAULT_COMPRESS_QUALITY, ios);
+ Utils.closeSilently(ios);
+ return null;
+ }
+ };
+
+ (new BitmapTask<Bitmap>(cb)).execute(bmap);
+ }
+
public void setAdapter(HistoryAdapter adapter) {
mAdapter = adapter;
}
@@ -475,4 +621,9 @@ public class ImageLoader {
}
}
+ public void addCacheListener(ImageShow imageShow) {
+ mHiresCache.addObserver(imageShow);
+ mCache.addObserver(imageShow);
+ }
+
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
index 7f4d5ed2a..5c24fbf65 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public class ImageFilter implements Cloneable {
@@ -31,22 +32,42 @@ public class ImageFilter implements Cloneable {
protected String mName = "Original";
private final String LOGTAG = "ImageFilter";
- public static final byte TYPE_BORDER =1;
- public static final byte TYPE_FX = 2;
+ public static final byte TYPE_BORDER = 1;
+ public static final byte TYPE_FX = 2;
public static final byte TYPE_WBALANCE = 3;
public static final byte TYPE_VIGNETTE = 4;
public static final byte TYPE_NORMAL = 5;
public static final byte TYPE_TINYPLANET = 6;
private byte filterType = TYPE_NORMAL;
- public byte getFilterType(){
+ public byte getFilterType() {
return filterType;
}
- protected void setFilterType(byte type){
+ protected void setFilterType(byte type) {
filterType = type;
}
+ public int getButtonId() {
+ return 0;
+ }
+
+ public int getTextId() {
+ return 0;
+ }
+
+ public int getOverlayBitmaps() {
+ return 0;
+ }
+
+ public int getEditingViewId() {
+ return R.id.imageShow;
+ }
+
+ public boolean showEditingControls() {
+ return true;
+ }
+
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilter filter = (ImageFilter) super.clone();
@@ -93,7 +114,7 @@ public class ImageFilter implements Cloneable {
* The maximum allowed value (inclusive)
* @return maximum value allowed as input to this filter
*/
- public int getMaxParameter(){
+ public int getMaxParameter() {
return mMaxParameter;
}
@@ -101,7 +122,7 @@ public class ImageFilter implements Cloneable {
* The parameter value to be used in previews.
* @return parameter value to be used to preview the filter
*/
- public int getPreviewParameter(){
+ public int getPreviewParameter() {
return mPreviewParameter;
}
@@ -109,7 +130,7 @@ public class ImageFilter implements Cloneable {
* The minimum allowed value (inclusive)
* @return minimum value allowed as input to this filter
*/
- public int getMinParameter(){
+ public int getMinParameter() {
return mMinParameter;
}
@@ -117,7 +138,7 @@ public class ImageFilter implements Cloneable {
* Returns the default value returned by this filter.
* @return default value
*/
- public int getDefaultParameter(){
+ public int getDefaultParameter() {
return mDefaultParameter;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
index 558abe3c3..1bb5c76ac 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -29,6 +31,16 @@ public class ImageFilterBwFilter extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.bwfilterButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.bwfilter;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterBwFilter filter = (ImageFilterBwFilter) super.clone();
return filter;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
index 0c3bb37ca..70e3d8589 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterContrast extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterContrast extends ImageFilter {
mName = "Contrast";
}
+ @Override
+ public int getButtonId() {
+ return R.id.contrastButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.contrast;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
index 89641d103..46a00f47a 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.ui.Spline;
public class ImageFilterCurves extends ImageFilter {
@@ -31,6 +32,26 @@ public class ImageFilterCurves extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.curvesButtonRGB;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.curvesRGB;
+ }
+
+ @Override
+ public int getOverlayBitmaps() {
+ return R.drawable.filtershow_button_colors_curve;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageCurves;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterCurves filter = (ImageFilterCurves) super.clone();
for (int i = 0; i < 4; i++) {
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
index e38dc8eb5..63f860171 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterExposure extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterExposure extends ImageFilter {
mName = "Exposure";
}
+ @Override
+ public int getButtonId() {
+ return R.id.exposureButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.exposure;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
index d74a6faab..33ecc8ab9 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
@@ -23,6 +23,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.imageshow.GeometryMath;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
@@ -69,22 +70,56 @@ 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?)
+
+ CropExtras extras = mGeometry.getCropExtras();
+ boolean useExtras = mGeometry.getUseCropExtrasFlag();
+ int outputX = 0;
+ int outputY = 0;
+ boolean s = false;
+ if (extras != null && useExtras){
+ outputX = extras.getOutputX();
+ outputY = extras.getOutputY();
+ s = extras.getScaleUp();
+ }
+
+
Rect cropBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF crop = mGeometry.getCropBounds(bitmap);
if (crop.width() > 0 && crop.height() > 0)
cropBounds = GeometryMath.roundNearest(crop);
- Bitmap temp = null;
- if (mGeometry.hasSwitchedWidthHeight()) {
- temp = Bitmap.createBitmap(cropBounds.height(), cropBounds.width(), mConfig);
- } else {
- temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig);
+
+ int width = cropBounds.width();
+ int height = cropBounds.height();
+
+ if (mGeometry.hasSwitchedWidthHeight()){
+ int temp = width;
+ width = height;
+ height = temp;
}
+
+ if(outputX <= 0 || outputY <= 0){
+ outputX = width;
+ outputY = height;
+ }
+
+ float scaleX = 1;
+ float scaleY = 1;
+ if (s){
+ scaleX = (float) outputX / width;
+ scaleY = (float) outputY / height;
+ }
+
+ Bitmap temp = null;
+ temp = Bitmap.createBitmap(outputX, outputY, mConfig);
+
float[] displayCenter = {
temp.getWidth() / 2f, temp.getHeight() / 2f
};
Matrix m1 = mGeometry.buildTotalXform(bitmap.getWidth(), bitmap.getHeight(), displayCenter);
+ m1.postScale(scaleX, scaleY, displayCenter[0], displayCenter[1]);
+
Canvas canvas = new Canvas(temp);
Paint paint = new Paint();
paint.setAntiAlias(true);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
index 279718edb..e2ea388dc 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterHue extends ImageFilter {
@@ -29,6 +31,16 @@ public class ImageFilterHue extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.hueButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.hue;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterHue filter = (ImageFilterHue) super.clone();
filter.cmatrix = new ColorSpaceMatrix(cmatrix);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
index c77de330f..9ae6f511e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
@@ -17,40 +17,167 @@
package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
-public class ImageFilterRedEye extends ImageFilter {
- private static final String TAG = "ImageFilterRedEye";
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
+
+import java.util.Vector;
+public class ImageFilterRedEye extends ImageFilter {
+ private static final String LOGTAG = "ImageFilterRedEye";
+ private Vector<RedEyeCandidate> mCandidates = null;
public ImageFilterRedEye() {
- mName = "Redeye";
+ mName = "Red Eye";
+ }
+ @Override
+ public int getButtonId() {
+ return R.id.redEyeButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.redeye;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageRedEyes;
}
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterRedEye filter = (ImageFilterRedEye) super.clone();
-
+ if (mCandidates != null) {
+ int size = mCandidates.size();
+ filter.mCandidates = new Vector<RedEyeCandidate>();
+ for (int i = 0; i < size; i++) {
+ filter.mCandidates.add(new RedEyeCandidate(mCandidates.elementAt(i)));
+ }
+ }
return filter;
}
- native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, short []matrix);
+ @Override
+ public boolean isNil() {
+ if (mCandidates != null && mCandidates.size() > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean same(ImageFilter filter) {
+ boolean isRedEyeFilter = super.same(filter);
+ if (!isRedEyeFilter) {
+ return false;
+ }
+ ImageFilterRedEye redEyeFilter = (ImageFilterRedEye) filter;
+ if (redEyeFilter.mCandidates == null && mCandidates == null) {
+ return true;
+ }
+ if (redEyeFilter.mCandidates == null || mCandidates == null) {
+ return false;
+ }
+ if (redEyeFilter.mCandidates.size() != mCandidates.size()) {
+ return false;
+ }
+ int size = mCandidates.size();
+ for (int i = 0; i < size; i++) {
+ RedEyeCandidate c1 = mCandidates.elementAt(i);
+ RedEyeCandidate c2 = redEyeFilter.mCandidates.elementAt(i);
+ if (!c1.equals(c2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Vector<RedEyeCandidate> getCandidates() {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ return mCandidates;
+ }
+
+ public void addRect(RectF rect, RectF bounds) {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ Vector<RedEyeCandidate> intersects = new Vector<RedEyeCandidate>();
+ for (int i = 0; i < mCandidates.size(); i++) {
+ RedEyeCandidate r = mCandidates.elementAt(i);
+ if (r.intersect(rect)) {
+ intersects.add(r);
+ }
+ }
+ for (int i = 0; i < intersects.size(); i++) {
+ RedEyeCandidate r = intersects.elementAt(i);
+ rect.union(r.mRect);
+ bounds.union(r.mBounds);
+ mCandidates.remove(r);
+ }
+ mCandidates.add(new RedEyeCandidate(rect, bounds));
+ }
+
+ public void clear() {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ mCandidates.clear();
+ }
+
+ native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, short[] matrix);
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
- float p = mParameter;
- float value = p;
- int box = Math.min(w, h);
- int sizex = Math.min((int)((p+100)*box/400),w/2);
- int sizey = Math.min((int)((p+100)*box/800),h/2);
-
- short [] rect = new short[]{
- (short) (w/2-sizex),(short) (w/2-sizey),
- (short) (2*sizex),(short) (2*sizey)};
+ short[] rect = new short[4];
- nativeApplyFilter(bitmap, w, h, rect);
+ if (mCandidates != null && mCandidates.size() > 0) {
+ for (int i = 0; i < mCandidates.size(); i++) {
+ RectF r = new RectF(mCandidates.elementAt(i).mRect);
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(true,
+ getImagePreset().getImageLoader().getOriginalBounds().width(),
+ getImagePreset().getImageLoader().getOriginalBounds().height(),
+ w, h);
+ originalToScreen.mapRect(r);
+ if (r.left < 0) {
+ r.left = 0;
+ }
+ if (r.left > w) {
+ r.left = w;
+ }
+ if (r.top < 0) {
+ r.top = 0;
+ }
+ if (r.top > h) {
+ r.top = h;
+ }
+ if (r.right < 0) {
+ r.right = 0;
+ }
+ if (r.right > w) {
+ r.right = w;
+ }
+ if (r.bottom < 0) {
+ r.bottom = 0;
+ }
+ if (r.bottom > h) {
+ r.bottom = h;
+ }
+ rect[0] = (short) r.left;
+ rect[1] = (short) r.top;
+ rect[2] = (short) r.width();
+ rect[3] = (short) r.height();
+ nativeApplyFilter(bitmap, w, h, rect);
+ }
+ }
return bitmap;
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
index 1d3459195..129165b3e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterSaturated extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterSaturated extends ImageFilter {
mName = "Saturated";
}
+ @Override
+ public int getButtonId() {
+ return R.id.saturationButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.saturation;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float saturation);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
index 4e6b848ae..de8fcd5ea 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterShadows extends ImageFilter {
@@ -26,6 +28,16 @@ public class ImageFilterShadows extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.shadowRecoveryButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.shadow_recovery;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterShadows filter = (ImageFilterShadows) super.clone();
return filter;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
index a355539c2..1951b9b9e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
@@ -28,6 +28,26 @@ public class ImageFilterSharpen extends ImageFilterRS {
}
@Override
+ public int getButtonId() {
+ return R.id.sharpenButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.sharpness;
+ }
+
+ @Override
+ public int getOverlayBitmaps() {
+ return R.drawable.filtershow_button_colors_sharpen;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageZoom;
+ }
+
+ @Override
public void createFilter(android.content.res.Resources res, float scaleFactor,
boolean highQuality) {
int w = mInPixelsAllocation.getType().getX();
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
index effd89ebe..36bd62630 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
@@ -22,6 +22,7 @@ import android.graphics.RectF;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
+import com.android.gallery3d.R;
import com.android.gallery3d.app.Log;
import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -59,6 +60,16 @@ public class ImageFilterTinyPlanet extends ImageFilter {
mAngle = 0;
}
+ @Override
+ public int getButtonId() {
+ return R.id.tinyplanetButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.tinyplanet;
+ }
+
public void setAngle(float angle) {
mAngle = angle;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
index 34f8b245e..7720d0490 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterVibrance extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterVibrance extends ImageFilter {
mName = "Vibrance";
}
+ @Override
+ public int getButtonId() {
+ return R.id.vibranceButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.vibrance;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
index 7a471e5b9..3c904fa6c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterVignette extends ImageFilter {
@@ -25,6 +27,16 @@ public class ImageFilterVignette extends ImageFilter {
mName = "Vignette";
}
+ @Override
+ public int getButtonId() {
+ return R.id.vignetteButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.vignette;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index b00b867b3..8665dc54c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterWBalance extends ImageFilter {
@@ -26,13 +28,32 @@ public class ImageFilterWBalance extends ImageFilter {
mName = "WBalance";
}
+ @Override
+ public int getButtonId() {
+ return R.id.wbalanceButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.wbalance;
+ }
+
+ public boolean showEditingControls() {
+ return false;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, int locX, int locY);
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
- nativeApplyFilter(bitmap, w, h, -1,-1);
+ nativeApplyFilter(bitmap, w, h, -1, -1);
return bitmap;
}
+
+ @Override
+ public boolean isNil() {
+ return false;
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java
new file mode 100644
index 000000000..58d3afa3b
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java
@@ -0,0 +1,50 @@
+/*
+ * 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.filtershow.filters;
+
+import android.graphics.RectF;
+
+public class RedEyeCandidate {
+ RectF mRect = new RectF();
+ RectF mBounds = new RectF();
+
+ public RedEyeCandidate(RedEyeCandidate candidate) {
+ mRect.set(candidate.mRect);
+ mBounds.set(candidate.mBounds);
+ }
+
+ public RedEyeCandidate(RectF rect, RectF bounds) {
+ mRect.set(rect);
+ mBounds.set(bounds);
+ }
+
+ public boolean equals(RedEyeCandidate candidate) {
+ if (candidate.mRect.equals(mRect)
+ && candidate.mBounds.equals(mBounds)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean intersect(RectF rect) {
+ return mRect.intersect(rect);
+ }
+
+ public RectF getRect() {
+ return mRect;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
new file mode 100644
index 000000000..e94d1ed9e
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
@@ -0,0 +1,340 @@
+/*
+ * 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.filtershow.imageshow;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+/**
+ * Maintains invariant that inner rectangle is constrained to be within the
+ * outer, rotated rectangle.
+ */
+public class BoundedRect {
+ private float rot;
+ private RectF outer;
+ private RectF inner;
+ private float[] innerRotated;
+
+ public BoundedRect() {
+ rot = 0;
+ outer = new RectF();
+ inner = new RectF();
+ innerRotated = new float[8];
+ }
+
+ public BoundedRect(float rotation, RectF outerRect, RectF innerRect) {
+ rot = rotation;
+ outer = new RectF(outerRect);
+ inner = new RectF(innerRect);
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets inner, and re-constrains it to fit within the rotated bounding rect.
+ */
+ public void setInner(RectF newInner) {
+ if (inner.equals(newInner))
+ return;
+ inner = newInner;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets rotation, and re-constrains inner to fit within the rotated bounding rect.
+ */
+ public void setRotation(float rotation) {
+ if (rotation == rot)
+ return;
+ rot = rotation;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ public RectF getInner() {
+ return new RectF(inner);
+ }
+
+ /**
+ * Tries to move the inner rectangle by (dx, dy). If this would cause it to leave
+ * the bounding rectangle, snaps the inner rectangle to the edge of the bounding
+ * rectangle.
+ */
+ public void moveInner(float dx, float dy) {
+ Matrix m0 = getInverseRotMatrix();
+
+ RectF translatedInner = new RectF(inner);
+ translatedInner.offset(dx, dy);
+
+ float[] translatedInnerCorners = CropMath.getCornersFromRect(translatedInner);
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+
+ m0.mapPoints(translatedInnerCorners);
+ float[] correction = {
+ 0, 0
+ };
+
+ // find correction vectors for corners that have moved out of bounds
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] badCorner = {
+ correctedInnerX, correctedInnerY
+ };
+ float[] nearestSide = CropMath.closestSide(badCorner, outerCorners);
+ float[] correctionVec =
+ GeometryMath.shortestVectorFromPointToLine(badCorner, nearestSide);
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] correctionVec = {
+ correctedInnerX, correctedInnerY
+ };
+ CropMath.getEdgePoints(outer, correctionVec);
+ correctionVec[0] -= correctedInnerX;
+ correctionVec[1] -= correctedInnerY;
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ // Set correction
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ // update translated corners with correction vectors
+ translatedInnerCorners[i] = correctedInnerX;
+ translatedInnerCorners[i + 1] = correctedInnerY;
+ }
+
+ innerRotated = translatedInnerCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit.
+ */
+ public void resizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+ m.mapPoints(outerCorners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+ RectF ret = new RectF(newInner);
+
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ float[] outerSide = CropMath.closestSide(c, outerCorners);
+ float[] pathOfCorner = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(pathOfCorner, outerSide);
+ if (p == null) {
+ // lines are parallel or not well defined, so don't resize
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ switch (i) {
+ case 0:
+ case 1:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 2:
+ case 3:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 4:
+ case 5:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ case 6:
+ case 7:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit while maintaining
+ * aspect ratio.
+ */
+ public void fixedAspectResizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float aspectW = inner.width();
+ float aspectH = inner.height();
+ float aspRatio = aspectW / aspectH;
+ float[] corners = CropMath.getCornersFromRect(outer);
+
+ m.mapPoints(corners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+
+ // find fixed corner
+ int fixed = -1;
+ if (inner.top == newInner.top) {
+ if (inner.left == newInner.left)
+ fixed = 0; // top left
+ else if (inner.right == newInner.right)
+ fixed = 2; // top right
+ } else if (inner.bottom == newInner.bottom) {
+ if (inner.right == newInner.right)
+ fixed = 4; // bottom right
+ else if (inner.left == newInner.left)
+ fixed = 6; // bottom left
+ }
+ // no fixed corner, return without update
+ if (fixed == -1)
+ return;
+ float widthSoFar = newInner.width();
+ int moved = -1;
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ moved = i;
+ if (moved == fixed)
+ continue;
+ float[] l2 = CropMath.closestSide(c, corners);
+ float[] l1 = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(l1, l2);
+ if (p == null) {
+ // lines are parallel or not well defined, so set to old
+ // corner
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ float fixed_x = oldInnerCorners[fixed];
+ float fixed_y = oldInnerCorners[fixed + 1];
+ float newWidth = Math.abs(fixed_x - p[0]);
+ float newHeight = Math.abs(fixed_y - p[1]);
+ newWidth = Math.max(newWidth, aspRatio * newHeight);
+ if (newWidth < widthSoFar)
+ widthSoFar = newWidth;
+ }
+ }
+
+ float heightSoFar = widthSoFar / aspRatio;
+ RectF ret = new RectF(inner);
+ if (fixed == 0) {
+ ret.right = ret.left + widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 2) {
+ ret.left = ret.right - widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 4) {
+ ret.left = ret.right - widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ } else if (fixed == 6) {
+ ret.right = ret.left + widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ // internal methods
+
+ private boolean isConstrained() {
+ for (int i = 0; i < 8; i += 2) {
+ if (!CropMath.inclusiveContains(outer, innerRotated[i], innerRotated[i + 1]))
+ return false;
+ }
+ return true;
+ }
+
+ private void reconstrain() {
+ // innerRotated has been changed to have incorrect values
+ CropMath.getEdgePoints(outer, innerRotated);
+ Matrix m = getRotMatrix();
+ float[] unrotated = Arrays.copyOf(innerRotated, 8);
+ m.mapPoints(unrotated);
+ inner = CropMath.trapToRect(unrotated);
+ }
+
+ private void rotateInner() {
+ Matrix m = getInverseRotMatrix();
+ m.mapPoints(innerRotated);
+ }
+
+ private Matrix getRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+
+ private Matrix getInverseRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(-rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/CropMath.java b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
new file mode 100644
index 000000000..9037ca043
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
@@ -0,0 +1,191 @@
+/*
+ * 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.filtershow.imageshow;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+public class CropMath {
+
+ /**
+ * Gets a float array of the 2D coordinates representing a rectangles
+ * corners.
+ * The order of the corners in the float array is:
+ * 0------->1
+ * ^ |
+ * | v
+ * 3<-------2
+ *
+ * @param r the rectangle to get the corners of
+ * @return the float array of corners (8 floats)
+ */
+
+ public static float[] getCornersFromRect(RectF r) {
+ float[] corners = {
+ r.left, r.top,
+ r.right, r.top,
+ r.right, r.bottom,
+ r.left, r.bottom
+ };
+ return corners;
+ }
+
+ /**
+ * Returns true iff point (x, y) is within or on the rectangle's bounds.
+ * RectF's "contains" function treats points on the bottom and right bound
+ * as not being contained.
+ *
+ * @param r the rectangle
+ * @param x the x value of the point
+ * @param y the y value of the point
+ * @return
+ */
+ public static boolean inclusiveContains(RectF r, float x, float y) {
+ return !(x > r.right || x < r.left || y > r.bottom || y < r.top);
+ }
+
+ /**
+ * Takes an array of 2D coordinates representing corners and returns the
+ * smallest rectangle containing those coordinates.
+ *
+ * @param array array of 2D coordinates
+ * @return smallest rectangle containing coordinates
+ */
+ public static RectF trapToRect(float[] array) {
+ RectF r = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ for (int i = 1; i < array.length; i += 2) {
+ float x = array[i - 1];
+ float y = array[i];
+ r.left = (x < r.left) ? x : r.left;
+ r.top = (y < r.top) ? y : r.top;
+ r.right = (x > r.right) ? x : r.right;
+ r.bottom = (y > r.bottom) ? y : r.bottom;
+ }
+ r.sort();
+ return r;
+ }
+
+ /**
+ * 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.
+ *
+ * @param imageBound the rectangle to clamp edge points to.
+ * @param array an array of points to clamp to the rectangle, gets set to
+ * the clamped values.
+ */
+ public static void getEdgePoints(RectF imageBound, float[] array) {
+ if (array.length < 2)
+ return;
+ for (int x = 0; x < array.length; x += 2) {
+ array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
+ array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
+ }
+ }
+
+ /**
+ * Takes a point and the corners of a rectangle and returns the two corners
+ * representing the side of the rectangle closest to the point.
+ *
+ * @param point the point which is being checked
+ * @param corners the corners of the rectangle
+ * @return two corners representing the side of the rectangle
+ */
+ public static float[] closestSide(float[] point, float[] corners) {
+ int len = corners.length;
+ float oldMag = Float.POSITIVE_INFINITY;
+ float[] bestLine = null;
+ for (int i = 0; i < len; i += 2) {
+ float[] line = {
+ corners[i], corners[(i + 1) % len],
+ corners[(i + 2) % len], corners[(i + 3) % len]
+ };
+ float mag = GeometryMath.vectorLength(
+ GeometryMath.shortestVectorFromPointToLine(point, line));
+ if (mag < oldMag) {
+ oldMag = mag;
+ bestLine = line;
+ }
+ }
+ return bestLine;
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param bound rectangle to rotate
+ * @param rot angle of rotation about rectangle center
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, RectF bound, float rot) {
+ Matrix m = new Matrix();
+ float[] p = Arrays.copyOf(point, 2);
+ m.setRotate(rot, bound.centerX(), bound.centerY());
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0))
+ return false;
+ m0.mapPoints(p);
+ return inclusiveContains(bound, p[0], p[1]);
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param rotatedRect corners of a rotated rectangle
+ * @param center center of the rotated rectangle
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, float[] rotatedRect, float[] center) {
+ RectF unrotated = new RectF();
+ float angle = getUnrotated(rotatedRect, center, unrotated);
+ return pointInRotatedRect(point, unrotated, angle);
+ }
+
+ /**
+ * Resizes rectangle to have a certain aspect ratio (center remains
+ * stationary).
+ *
+ * @param r rectangle to resize
+ * @param w new width aspect
+ * @param h new height aspect
+ */
+ public static void fixAspectRatio(RectF r, float w, float h) {
+ float scale = Math.min(r.width() / w, r.height() / 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);
+ }
+
+ private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
+ float dy = rotatedRect[1] - rotatedRect[3];
+ float dx = rotatedRect[0] - rotatedRect[2];
+ float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI);
+ Matrix m = new Matrix();
+ m.setRotate(-angle, center[0], center[1]);
+ float[] unrotatedRect = new float[rotatedRect.length];
+ m.mapPoints(unrotatedRect, rotatedRect);
+ unrotated.set(trapToRect(unrotatedRect));
+ return angle;
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
index 55f791820..568dadfc3 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
@@ -26,11 +26,37 @@ public class GeometryMath {
return Math.max(Math.min(i, high), low);
}
- protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
- float x1 = l1[0];
- float x2 = l2[0];
- float y1 = l1[1];
- float y2 = l2[1];
+ public static float[] lineIntersect(float[] line1, float[] line2) {
+ float a0 = line1[0];
+ float a1 = line1[1];
+ float b0 = line1[2];
+ float b1 = line1[3];
+ float c0 = line2[0];
+ float c1 = line2[1];
+ float d0 = line2[2];
+ float d1 = line2[3];
+ float t0 = a0 - b0;
+ float t1 = a1 - b1;
+ float t2 = b0 - d0;
+ float t3 = d1 - b1;
+ float t4 = c0 - d0;
+ float t5 = c1 - d1;
+
+ float denom = t1 * t4 - t0 * t5;
+ if (denom == 0)
+ return null;
+ float u = (t3 * t4 + t5 * t2) / denom;
+ float[] intersect = {
+ b0 + u * t0, b1 + u * t1
+ };
+ return intersect;
+ }
+
+ public static float[] shortestVectorFromPointToLine(float[] point, float[] line) {
+ float x1 = line[0];
+ float x2 = line[2];
+ float y1 = line[1];
+ float y2 = line[3];
float xdelt = x2 - x1;
float ydelt = y2 - y1;
if (xdelt == 0 && ydelt == 0)
@@ -40,67 +66,75 @@ public class GeometryMath {
float[] ret = {
(x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
};
- float [] vec = {ret[0] - point[0], ret[1] - point[1] };
+ float[] vec = {
+ ret[0] - point[0], ret[1] - point[1]
+ };
return vec;
}
// A . B
- public static float dotProduct(float[] a, float[] b){
+ public static float dotProduct(float[] a, float[] b) {
return a[0] * b[0] + a[1] * b[1];
}
- public static float[] normalize(float[] a){
+ public static float[] normalize(float[] a) {
float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
- float[] b = { a[0] / length, a[1] / length };
+ float[] b = {
+ a[0] / length, a[1] / length
+ };
return b;
}
// A onto B
- public static float scalarProjection(float[] a, float[] b){
+ public static float scalarProjection(float[] a, float[] b) {
float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]);
return dotProduct(a, b) / length;
}
- public static float[] getVectorFromPoints(float [] point1, float [] point2){
- float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ public static float[] getVectorFromPoints(float[] point1, float[] point2) {
+ float[] p = {
+ point2[0] - point1[0], point2[1] - point1[1]
+ };
return p;
}
- public static float[] getUnitVectorFromPoints(float [] point1, float [] point2){
- float [] p = { point2[0] - point1[0], point2[1] - point1[1] };
+ public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) {
+ float[] p = {
+ point2[0] - point1[0], point2[1] - point1[1]
+ };
float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]);
p[0] = p[0] / length;
p[1] = p[1] / length;
return p;
}
- public static RectF scaleRect(RectF r, float scale){
+ public static RectF scaleRect(RectF r, float scale) {
return new RectF(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale);
}
// A - B
- public static float[] vectorSubtract(float [] a, float [] b){
+ public static float[] vectorSubtract(float[] a, float[] b) {
int len = a.length;
if (len != b.length)
return null;
- float [] ret = new float[len];
- for (int i = 0; i < len; i++){
+ float[] ret = new float[len];
+ for (int i = 0; i < len; i++) {
ret[i] = a[i] - b[i];
}
return ret;
}
- public static float vectorLength(float [] a){
+ public static float vectorLength(float[] a) {
return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]);
}
public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) {
if (oldHeight == 0 || oldWidth == 0)
return 1;
- return Math.min(newWidth / oldWidth , newHeight / oldHeight);
+ return Math.min(newWidth / oldWidth, newHeight / oldHeight);
}
- public static Rect roundNearest(RectF r){
+ public static Rect roundNearest(RectF r) {
Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right),
Math.round(r.bottom));
return q;
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index dffdc2449..b53284061 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -21,12 +21,11 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
public class GeometryMetadata {
- // Applied in order: rotate, crop, scale.
- // Do not scale saved image (presumably?).
private static final ImageFilterGeometry mImageFilter = new ImageFilterGeometry();
private static final String LOGTAG = "GeometryMetadata";
private float mScaleFactor = 1.0f;
@@ -36,12 +35,29 @@ public class GeometryMetadata {
private final RectF mPhotoBounds = new RectF();
private FLIP mFlip = FLIP.NONE;
- private RectF mBounds = new RectF();
-
public enum FLIP {
NONE, VERTICAL, HORIZONTAL, BOTH
}
+ // Output format data from intent extras
+ private boolean mUseCropExtras = false;
+ private CropExtras mCropExtras = null;
+ public void setUseCropExtrasFlag(boolean f){
+ mUseCropExtras = f;
+ }
+
+ public boolean getUseCropExtrasFlag(){
+ return mUseCropExtras;
+ }
+
+ public void setCropExtras(CropExtras e){
+ mCropExtras = e;
+ }
+
+ public CropExtras getCropExtras(){
+ return mCropExtras;
+ }
+
public GeometryMetadata() {
}
@@ -86,7 +102,11 @@ public class GeometryMetadata {
mCropBounds.set(g.mCropBounds);
mPhotoBounds.set(g.mPhotoBounds);
mFlip = g.mFlip;
- mBounds = g.mBounds;
+
+ mUseCropExtras = g.mUseCropExtras;
+ if (g.mCropExtras != null){
+ mCropExtras = new CropExtras(g.mCropExtras);
+ }
}
public float getScaleFactor() {
@@ -184,48 +204,16 @@ public class GeometryMetadata {
+ ",photoRect=" + mPhotoBounds.toShortString() + "]";
}
- // TODO: refactor away
- protected static Matrix getHorizontalMatrix(float width) {
- Matrix flipHorizontalMatrix = new Matrix();
- flipHorizontalMatrix.setScale(-1, 1);
- flipHorizontalMatrix.postTranslate(width, 0);
- return flipHorizontalMatrix;
- }
-
protected static void concatHorizontalMatrix(Matrix m, float width) {
m.postScale(-1, 1);
m.postTranslate(width, 0);
}
- // TODO: refactor away
- protected static Matrix getVerticalMatrix(float height) {
- Matrix flipVerticalMatrix = new Matrix();
- flipVerticalMatrix.setScale(1, -1);
- flipVerticalMatrix.postTranslate(0, height);
- return flipVerticalMatrix;
- }
-
protected static void concatVerticalMatrix(Matrix m, float height) {
m.postScale(1, -1);
m.postTranslate(0, height);
}
- // TODO: refactor away
- public static Matrix getFlipMatrix(float width, float height, FLIP type) {
- if (type == FLIP.HORIZONTAL) {
- return getHorizontalMatrix(width);
- } else if (type == FLIP.VERTICAL) {
- return getVerticalMatrix(height);
- } else if (type == FLIP.BOTH) {
- Matrix flipper = getVerticalMatrix(height);
- flipper.postConcat(getHorizontalMatrix(width));
- return flipper;
- } else {
- Matrix m = new Matrix();
- m.reset(); // identity
- return m;
- }
- }
public static void concatMirrorMatrix(Matrix m, float width, float height, FLIP type) {
if (type == FLIP.HORIZONTAL) {
@@ -331,46 +319,10 @@ public class GeometryMetadata {
return m1;
}
- // TODO: refactor away
- public Matrix getFlipMatrix(float width, float height) {
- FLIP type = getFlipType();
- return getFlipMatrix(width, height, type);
- }
-
public boolean hasSwitchedWidthHeight() {
return (((int) (mRotation / 90)) % 2) != 0;
}
- // TODO: refactor away
- 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;
- }
-
- // TODO: refactor away
- 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);
- }
-
- // TODO: refactor away
- 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);
- }
-
public static Matrix buildPhotoMatrix(RectF photo, RectF crop, float rotation,
float straighten, FLIP type) {
Matrix m = new Matrix();
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
index a352a16e7..594e008a2 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -29,22 +29,25 @@ import android.util.AttributeSet;
import android.util.Log;
import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.CropExtras;
public class ImageCrop extends ImageGeometry {
private static final boolean LOGV = false;
+
+ // Sides
private static final int MOVE_LEFT = 1;
private static final int MOVE_TOP = 2;
private static final int MOVE_RIGHT = 4;
private static final int MOVE_BOTTOM = 8;
private static final int MOVE_BLOCK = 16;
- //Corners
+ // Corners
private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
- private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
+ private static int mMinSideSize = 100;
private static int mTouchTolerance = 45;
private boolean mFirstDraw = true;
@@ -53,23 +56,30 @@ public class ImageCrop extends ImageGeometry {
private boolean mFixAspectRatio = false;
private float mLastRot = 0;
- private final Paint borderPaint;
+ private BoundedRect mBounded = null;
private int movingEdges;
private final Drawable cropIndicator;
private final int indicatorSize;
private final int mBorderColor = Color.argb(128, 255, 255, 255);
+ // Offset between crop center and photo center
+ private float[] mOffset = {
+ 0, 0
+ };
+ private CropExtras mCropExtras = null;
+ private boolean mDoingCropIntentAction = false;
+
private static final String LOGTAG = "ImageCrop";
private String mAspect = "";
private int mAspectTextSize = 24;
- public void setAspectTextSize(int textSize){
+ public void setAspectTextSize(int textSize) {
mAspectTextSize = textSize;
}
- public void setAspectString(String a){
+ public void setAspectString(String a) {
mAspect = a;
}
@@ -80,10 +90,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
public ImageCrop(Context context, AttributeSet attrs) {
@@ -91,10 +97,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
@Override
@@ -102,84 +104,46 @@ public class ImageCrop extends ImageGeometry {
return getContext().getString(R.string.crop);
}
- private void swapAspect(){
+ private void swapAspect() {
+ if (mDoingCropIntentAction) {
+ return;
+ }
float temp = mAspectWidth;
mAspectWidth = mAspectHeight;
mAspectHeight = temp;
}
- public static void setTouchTolerance(int tolerance){
+ /**
+ * Set tolerance for crop marker selection (in pixels)
+ */
+ public static void setTouchTolerance(int tolerance) {
mTouchTolerance = tolerance;
}
- private boolean switchCropBounds(int moving_corner, RectF dst) {
- RectF crop = getCropBoundsDisplayed();
- float dx1 = 0;
- float dy1 = 0;
- float dx2 = 0;
- float dy2 = 0;
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx1 = mCurrentX - crop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx1 = mCurrentX - crop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy1 = mCurrentY - crop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy1 = mCurrentY - crop.top;
- }
- RectF newCrop = null;
- //Fix opposite corner in place and move sides
- if (moving_corner == BOTTOM_RIGHT) {
- newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top
- + crop.width());
- } else if (moving_corner == BOTTOM_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top
- + crop.width());
- } else if (moving_corner == TOP_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(),
- crop.right, crop.bottom);
- } else if (moving_corner == TOP_RIGHT) {
- newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left
- + crop.height(), crop.bottom);
- }
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx2 = mCurrentX - newCrop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx2 = mCurrentX - newCrop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy2 = mCurrentY - newCrop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy2 = mCurrentY - newCrop.top;
- }
- if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return false;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return false;
- }
- swapAspect();
- dst.set(newCrop);
- return true;
- }
- return false;
+ /**
+ * Set minimum side length for crop box (in pixels)
+ */
+ public static void setMinCropSize(int minHeightWidth) {
+ mMinSideSize = minHeightWidth;
+ }
+
+ public void setExtras(CropExtras e) {
+ mCropExtras = e;
+ }
+
+ public void setCropActionFlag(boolean f) {
+ mDoingCropIntentAction = f;
}
- public void apply(float w, float h){
+ public void apply(float w, float h) {
mFixAspectRatio = true;
mAspectWidth = w;
mAspectHeight = h;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
@@ -194,202 +158,159 @@ public class ImageCrop extends ImageGeometry {
mAspectHeight = h / scale;
setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds,
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
public void applyClear() {
mFixAspectRatio = false;
+ mAspectWidth = 1;
+ mAspectHeight = 1;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
- private float getScaledMinWidthHeight() {
- RectF disp = new RectF(0, 0, getWidth(), getHeight());
- float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
- / computeScale(getWidth(), getHeight());
- return scaled;
- }
-
- 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;
- }
-
- protected Matrix getCropBoundDisplayMatrix(){
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
- }
- float zoom = computeScale(getWidth(), getHeight());
- m.postTranslate(mXOffset, mYOffset);
- m.postScale(zoom, zoom, mCenterX, mCenterY);
- return m;
- }
-
- protected RectF getCropBoundsDisplayed() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
+ public void clear() {
+ if (mCropExtras != null) {
+ int x = mCropExtras.getAspectX();
+ int y = mCropExtras.getAspectY();
+ if (mDoingCropIntentAction && x > 0 && y > 0) {
+ apply(x, y);
+ }
} else {
- m.mapRect(crop);
+ applyClear();
}
- m = new Matrix();
- float zoom = computeScale(getWidth(), getHeight());
- m.setScale(zoom, zoom, mCenterX, mCenterY);
- m.preTranslate(mXOffset, mYOffset);
- m.mapRect(crop);
- return crop;
}
- private RectF getRotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- return null;
- } else {
- m.mapRect(crop);
- }
- return crop;
+ private Matrix getPhotoBoundDisplayedMatrix() {
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
+ Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop,
+ getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
+ m.preScale(scale, scale);
+ return m;
}
- 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 Matrix getCropBoundDisplayedMatrix() {
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
+ Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop,
+ getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
+ m1.preScale(scale, scale);
+ return m1;
}
- 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;
+ /**
+ * Takes the rotated corners of a rectangle and returns the angle; sets
+ * unrotated to be the unrotated version of the rectangle.
+ */
+ private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
+ float dy = rotatedRect[1] - rotatedRect[3];
+ float dx = rotatedRect[0] - rotatedRect[2];
+ float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI);
+ Matrix m = new Matrix();
+ m.setRotate(-angle, center[0], center[1]);
+ float[] unrotatedRect = new float[rotatedRect.length];
+ m.mapPoints(unrotatedRect, rotatedRect);
+ unrotated.set(CropMath.trapToRect(unrotatedRect));
+ return angle;
}
/**
* Sets cropped bounds; modifies the bounds if it's smaller than the allowed
* dimensions.
*/
- public void setCropBounds(RectF bounds) {
- // Avoid cropping smaller than minimum width or height.
+ public boolean setCropBounds(RectF bounds) {
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;
- }
- }
-
+ Matrix mc = getCropBoundDisplayedMatrix();
+ Matrix mcInv = new Matrix();
+ mc.invert(mcInv);
+ mcInv.mapRect(cbounds);
+ // Avoid cropping smaller than minimum
float newWidth = cbounds.width();
float newHeight = cbounds.height();
- 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;
- }
- }
+ float scale = getTransformState(null, null, null);
+ float minWidthHeight = mMinSideSize / scale;
RectF pbounds = getLocalPhotoBounds();
- if (pbounds.width() < minWidthHeight) {
- newWidth = pbounds.width();
+
+ // if photo is smaller than minimum, refuse to set crop bounds
+ if (pbounds.width() < minWidthHeight || pbounds.height() < minWidthHeight) {
+ return false;
}
- if (pbounds.height() < minWidthHeight) {
- newHeight = pbounds.height();
+
+ // if incoming crop is smaller than minimum, refuse to set crop bounds
+ if (newWidth < minWidthHeight || newHeight < minWidthHeight) {
+ return false;
}
- cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- cbounds.intersect(straightenBounds);
+ float newX = bounds.centerX() - (getWidth() / 2f);
+ float newY = bounds.centerY() - (getHeight() / 2f);
+ mOffset[0] = newX;
+ mOffset[1] = newY;
- if (mFixAspectRatio) {
- fixAspectRatio(cbounds, aw, ah);
- }
setLocalCropBounds(cbounds);
invalidate();
+ return true;
+ }
+
+ private BoundedRect getBoundedCrop(RectF crop) {
+ RectF photo = getLocalPhotoBounds();
+ Matrix mp = getPhotoBoundDisplayedMatrix();
+ float[] photoCorners = CropMath.getCornersFromRect(photo);
+ float[] photoCenter = {
+ photo.centerX(), photo.centerY()
+ };
+ mp.mapPoints(photoCorners);
+ mp.mapPoints(photoCenter);
+ RectF scaledPhoto = new RectF();
+ float angle = getUnrotated(photoCorners, photoCenter, scaledPhoto);
+ return new BoundedRect(angle, scaledPhoto, crop);
}
private void detectMovingEdges(float x, float y) {
- RectF cropped = getCropBoundsDisplayed();
+ Matrix m = getCropBoundDisplayedMatrix();
+ RectF cropped = getLocalCropBounds();
+ m.mapRect(cropped);
+ mBounded = getBoundedCrop(cropped);
movingEdges = 0;
- // Check left or right.
float left = Math.abs(x - cropped.left);
float right = Math.abs(x - cropped.right);
- if ((left <= mTouchTolerance) && (left < right)) {
+ float top = Math.abs(y - cropped.top);
+ float bottom = Math.abs(y - cropped.bottom);
+
+ // Check left or right.
+ if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
+ && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) {
movingEdges |= MOVE_LEFT;
}
- else if (right <= mTouchTolerance) {
+ else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
+ && ((y - mTouchTolerance) <= cropped.bottom)) {
movingEdges |= MOVE_RIGHT;
}
// Check top or bottom.
- float top = Math.abs(y - cropped.top);
- float bottom = Math.abs(y - cropped.bottom);
- if ((top <= mTouchTolerance) & (top < bottom)) {
+ if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
movingEdges |= MOVE_TOP;
}
- else if (bottom <= mTouchTolerance) {
+ else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right)) {
movingEdges |= MOVE_BOTTOM;
}
- // Check inside block.
- if (cropped.contains(x, y) && (movingEdges == 0)) {
+ if (movingEdges == 0) {
movingEdges = MOVE_BLOCK;
}
if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) {
@@ -398,7 +319,7 @@ public class ImageCrop extends ImageGeometry {
invalidate();
}
- private int fixEdgeToCorner(int moving_edges){
+ private int fixEdgeToCorner(int moving_edges) {
if (moving_edges == MOVE_LEFT) {
moving_edges |= MOVE_TOP;
}
@@ -414,9 +335,9 @@ public class ImageCrop extends ImageGeometry {
return moving_edges;
}
- private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){
+ private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
RectF newCrop = null;
- //Fix opposite corner in place and move sides
+ // Fix opposite corner in place and move sides
if (moving_corner == BOTTOM_RIGHT) {
newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
+ dy);
@@ -434,120 +355,90 @@ public class ImageCrop extends ImageGeometry {
}
private void moveEdges(float dX, float dY) {
- RectF cropped = getRotatedCropBounds();
- float minWidthHeight = getScaledMinWidthHeight();
- float scale = computeScale(getWidth(), getHeight());
- float deltaX = dX / scale;
- float deltaY = dY / scale;
- int select = movingEdges;
- if (mFixAspectRatio && (select != MOVE_BLOCK)) {
-
- // TODO: add in orientation change for fixed aspect
- /*if (select == TOP_LEFT || select == TOP_RIGHT ||
- select == BOTTOM_LEFT || select == BOTTOM_RIGHT){
- RectF blank = new RectF();
- if(switchCropBounds(select, blank)){
- setCropBounds(blank);
- return;
- }
- }*/
- if (select == MOVE_LEFT) {
- select |= MOVE_TOP;
- }
- if (select == MOVE_TOP) {
- select |= MOVE_LEFT;
- }
- if (select == MOVE_RIGHT) {
- select |= MOVE_BOTTOM;
- }
- if (select == MOVE_BOTTOM) {
- select |= MOVE_RIGHT;
- }
- }
-
- 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);
+ RectF crop = mBounded.getInner();
+
+ Matrix mc = getCropBoundDisplayedMatrix();
+
+ RectF photo = getLocalPhotoBounds();
+ Matrix mp = getPhotoBoundDisplayedMatrix();
+ float[] photoCorners = CropMath.getCornersFromRect(photo);
+ float[] photoCenter = {
+ photo.centerX(), photo.centerY()
+ };
+ mp.mapPoints(photoCorners);
+ mp.mapPoints(photoCenter);
+
+ float minWidthHeight = mMinSideSize;
+
+ if (movingEdges == MOVE_BLOCK) {
+ mBounded.moveInner(-dX, -dY);
+ RectF r = mBounded.getInner();
+ setCropBounds(r);
+ return;
} else {
float dx = 0;
float dy = 0;
- if ((select & MOVE_LEFT) != 0) {
- dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
}
- if ((select & MOVE_TOP) != 0) {
- dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
}
- if ((select & MOVE_RIGHT) != 0) {
- dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight)
- - cropped.right;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
+ - crop.right;
}
- if ((select & MOVE_BOTTOM) != 0) {
- dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
- - cropped.bottom;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
+ - crop.bottom;
}
if (mFixAspectRatio) {
- RectF crop = getCropBoundsDisplayed();
- float [] l1 = {crop.left, crop.bottom};
- float [] l2 = {crop.right, crop.top};
- if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){
+ float[] l1 = {
+ crop.left, crop.bottom
+ };
+ float[] l2 = {
+ crop.right, crop.top
+ };
+ if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) {
l1[1] = crop.top;
l2[1] = crop.bottom;
}
- float[] b = { l1[0] - l2[0], l1[1] - l2[1] };
- float[] disp = {dx, dy};
+ float[] b = {
+ l1[0] - l2[0], l1[1] - l2[1]
+ };
+ float[] disp = {
+ dx, dy
+ };
float[] bUnit = GeometryMath.normalize(b);
float sp = GeometryMath.scalarProjection(disp, bUnit);
dx = sp * bUnit[0];
dy = sp * bUnit[1];
- RectF newCrop = fixedCornerResize(crop, select, dx * scale, dy * scale);
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return;
- }
+ RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
+
+ mBounded.fixedAspectResizeInner(newCrop);
+ newCrop = mBounded.getInner();
setCropBounds(newCrop);
return;
} else {
- if ((select & MOVE_LEFT) != 0) {
- cropped.left += dx;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ crop.left += dx;
}
- if ((select & MOVE_TOP) != 0) {
- cropped.top += dy;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ crop.top += dy;
}
- if ((select & MOVE_RIGHT) != 0) {
- cropped.right += dx;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ crop.right += dx;
}
- if ((select & MOVE_BOTTOM) != 0) {
- cropped.bottom += dy;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ crop.bottom += dy;
}
}
}
- movingEdges = select;
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- Matrix m0 = new Matrix();
- if (!m.invert(m0)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
- }
- if (!m0.mapRect(cropped)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
- }
- setCropBounds(cropped);
+ mBounded.resizeInner(crop);
+ crop = mBounded.getInner();
+ setCropBounds(crop);
}
private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
@@ -560,7 +451,8 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionDown(float x, float y) {
super.setActionDown(x, y);
- detectMovingEdges(x, y);
+ detectMovingEdges(x + mOffset[0], y + mOffset[1]);
+
}
@Override
@@ -571,20 +463,54 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionMove(float x, float y) {
- if (movingEdges != 0){
+
+ if (movingEdges != 0) {
moveEdges(x - mCurrentX, y - mCurrentY);
}
super.setActionMove(x, y);
+
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ setActionUp();
+ cropSetup();
+ invalidate();
}
private void cropSetup() {
+ RectF crop = getLocalCropBounds();
+ Matrix m = getCropBoundDisplayedMatrix();
+ m.mapRect(crop);
if (mFixAspectRatio) {
- RectF cb = getRotatedCropBounds();
- fixAspectRatio(cb, mAspectWidth, mAspectHeight);
- RectF cb0 = getUnrotatedCropBounds(cb);
- setCropBounds(cb0);
- } else {
- setCropBounds(getLocalCropBounds());
+ CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight);
+ }
+ float dCentX = getWidth() / 2;
+ float dCentY = getHeight() / 2;
+
+ BoundedRect r = getBoundedCrop(crop);
+ crop = r.getInner();
+ if (!setCropBounds(crop)) {
+ float h = mMinSideSize / 2;
+ float wScale = 1;
+ float hScale = mAspectHeight / mAspectWidth;
+ if (hScale < 1) {
+ wScale = mAspectWidth / mAspectHeight;
+ hScale = 1;
+ }
+ crop.set(dCentX - h * wScale, dCentY - h * hScale, dCentX + h * wScale, dCentY + h
+ * hScale);
+ if (mFixAspectRatio) {
+ CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight);
+ }
+ r.setInner(crop);
+ crop = r.getInner();
+ if (!setCropBounds(crop)) {
+ crop.set(dCentX - h, dCentY - h, dCentX + h, dCentY + h);
+ r.setInner(crop);
+ crop = r.getInner();
+ setCropBounds(crop);
+ }
}
}
@@ -592,7 +518,7 @@ public class ImageCrop extends ImageGeometry {
public void imageLoaded() {
super.imageLoaded();
syncLocalToMasterGeometry();
- applyClear();
+ clear();
invalidate();
}
@@ -600,7 +526,7 @@ public class ImageCrop extends ImageGeometry {
protected void gainedVisibility() {
float rot = getLocalRotation();
// if has changed orientation via rotate
- if( ((int) ((rot - mLastRot) / 90)) % 2 != 0 ){
+ if (((int) ((rot - mLastRot) / 90)) % 2 != 0) {
swapAspect();
}
cropSetup();
@@ -610,7 +536,6 @@ public class ImageCrop extends ImageGeometry {
@Override
public void resetParameter() {
super.resetParameter();
- cropSetup();
}
@Override
@@ -635,7 +560,6 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- // TODO: move style to xml
gPaint.setAntiAlias(true);
gPaint.setFilterBitmap(true);
gPaint.setDither(true);
@@ -645,91 +569,102 @@ public class ImageCrop extends ImageGeometry {
cropSetup();
mFirstDraw = false;
}
- float rotation = getLocalRotation();
- RectF crop = drawTransformed(canvas, image, gPaint);
+ RectF crop = drawTransformed(canvas, image, gPaint, mOffset);
gPaint.setColor(mBorderColor);
gPaint.setStrokeWidth(3);
gPaint.setStyle(Paint.Style.STROKE);
- drawRuleOfThird(canvas, crop, gPaint);
-
- if (mFixAspectRatio){
- float w = crop.width();
- float h = crop.height();
- float diag = (float) Math.sqrt(w*w + h*h);
-
- float dash_len = 20;
- int num_intervals = (int) (diag / dash_len);
- float [] tl = { crop.left, crop.top };
- float centX = tl[0] + w/2;
- float centY = tl[1] + h/2 + 5;
- float [] br = { crop.right, crop.bottom };
- float [] vec = GeometryMath.getUnitVectorFromPoints(tl, br);
-
- float [] counter = tl;
- for (int x = 0; x < num_intervals; x++ ){
- float tempX = counter[0] + vec[0] * dash_len;
- float tempY = counter[1] + vec[1] * dash_len;
- if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2){
- canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint);
- }
- counter[0] = tempX;
- counter[1] = tempY;
+
+ boolean doThirds = true;
+
+ if (mFixAspectRatio) {
+ float spotlightX = 0;
+ float spotlightY = 0;
+ if (mCropExtras != null) {
+ spotlightX = mCropExtras.getSpotlightX();
+ spotlightY = mCropExtras.getSpotlightY();
}
+ if (mDoingCropIntentAction && spotlightX > 0 && spotlightY > 0) {
+ float sx = crop.width() * spotlightX;
+ float sy = crop.height() * spotlightY;
+ float cx = crop.centerX();
+ float cy = crop.centerY();
+ RectF r1 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
+ float temp = sx;
+ sx = sy;
+ sy = temp;
+ RectF r2 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
+ canvas.drawRect(r1, gPaint);
+ canvas.drawRect(r2, gPaint);
+ doThirds = false;
+ } else {
+ float w = crop.width();
+ float h = crop.height();
+ float diag = (float) Math.sqrt(w * w + h * h);
+
+ float dash_len = 20;
+ int num_intervals = (int) (diag / dash_len);
+ float[] tl = {
+ crop.left, crop.top
+ };
+ float centX = tl[0] + w / 2;
+ float centY = tl[1] + h / 2 + 5;
+ float[] br = {
+ crop.right, crop.bottom
+ };
+ float[] vec = GeometryMath.getUnitVectorFromPoints(tl, br);
+
+ float[] counter = tl;
+ for (int x = 0; x < num_intervals; x++) {
+ float tempX = counter[0] + vec[0] * dash_len;
+ float tempY = counter[1] + vec[1] * dash_len;
+ if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2) {
+ canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint);
+ }
+ counter[0] = tempX;
+ counter[1] = tempY;
+ }
- gPaint.setTextAlign(Paint.Align.CENTER);
- gPaint.setTextSize(mAspectTextSize);
- canvas.drawText(mAspect, centX, centY, gPaint);
+ gPaint.setTextAlign(Paint.Align.CENTER);
+ gPaint.setTextSize(mAspectTextSize);
+ canvas.drawText(mAspect, centX, centY, gPaint);
+ }
}
- gPaint.setColor(mBorderColor);
- gPaint.setStrokeWidth(3);
- gPaint.setStyle(Paint.Style.STROKE);
- drawStraighten(canvas, gPaint);
-
- int decoded_moving = decoder(movingEdges, rotation);
- canvas.save();
- canvas.rotate(rotation, mCenterX, mCenterY);
- RectF scaledCrop = unrotatedCropBounds();
- boolean notMoving = decoded_moving == 0;
- if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
- }
- if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
- }
- if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
- }
- if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
+ if (doThirds) {
+ drawRuleOfThird(canvas, crop, gPaint);
+
}
- 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;
+
+ RectF scaledCrop = crop;
+ boolean notMoving = (movingEdges == 0);
+ if (mFixAspectRatio) {
+ if ((movingEdges == TOP_LEFT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.top);
+ }
+ if ((movingEdges == TOP_RIGHT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.top);
+ }
+ if ((movingEdges == BOTTOM_LEFT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.bottom);
+ }
+ if ((movingEdges == BOTTOM_RIGHT) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.bottom);
+ }
+ } else {
+ if (((movingEdges & MOVE_TOP) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
+ }
+ if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
+ }
+ if (((movingEdges & MOVE_LEFT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
+ }
+ if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) {
+ drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
+ }
}
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
index 42dd139bc..c8ae444da 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -33,7 +33,7 @@ import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public abstract class ImageGeometry extends ImageSlave {
- private boolean mVisibilityGained = false;
+ protected boolean mVisibilityGained = false;
private boolean mHasDrawn = false;
protected static final float MAX_STRAIGHTEN_ANGLE = 45;
@@ -191,8 +191,8 @@ public abstract class ImageGeometry extends ImageSlave {
return r * 90;
}
- protected Matrix getLocalGeoFlipMatrix(float width, float height) {
- return mLocalGeometry.getFlipMatrix(width, height);
+ protected boolean isHeightWidthSwapped() {
+ return ((int) (getLocalRotation() / 90)) % 2 != 0;
}
protected void setLocalStraighten(float r) {
@@ -217,32 +217,6 @@ public abstract class ImageGeometry extends ImageSlave {
return getLocalRotation() + getLocalStraighten();
}
- protected static float[] getCornersFromRect(RectF r) {
- // Order is:
- // 0------->1
- // ^ |
- // | 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
- };
- return corners;
- }
-
- // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
- // image bound rectangle, clamps it to the edge of the rectangle.
- protected static void getEdgePoints(RectF imageBound, float[] array) {
- if (array.length < 2)
- return;
- for (int x = 0; x < array.length; x += 2) {
- array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right);
- array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom);
- }
- }
-
protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
Path crop = new Path();
crop.moveTo(points[0], points[1]);
@@ -254,16 +228,6 @@ public abstract class ImageGeometry extends ImageSlave {
return crop;
}
- protected static void fixAspectRatio(RectF r, float w, float h) {
- float scale = Math.min(r.width() / w, r.height() / 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) {
return width * h / w;
}
@@ -290,11 +254,11 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected void gainedVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
protected void lostVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
@Override
@@ -327,7 +291,7 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected int getLocalValue() {
- return 0; // TODO: Override this
+ return 0; // Override this
}
protected void setActionDown(float x, float y) {
@@ -402,110 +366,19 @@ public abstract class ImageGeometry extends ImageSlave {
return new RectF(left, top, right, bottom);
}
- protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) {
- RectF pbounds = getLocalPhotoBounds();
- float scale = GeometryMath
- .scale(pbounds.width(), pbounds.height(), getWidth(), getHeight());
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
- scale = GeometryMath.scale(pbounds.width(), pbounds.height(), getHeight(), getWidth());
- }
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- float w = r.left * 2 + r.width();
- float h = r.top * 2 + r.height();
- return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate);
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) {
- canvas.save();
- canvas.drawBitmap(bitmap, m, paint);
- canvas.restore();
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) {
- float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff);
- drawImageBitmap(canvas, bitmap, paint, m);
- }
-
protected RectF straightenBounds() {
RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten());
- Matrix m = getGeoMatrix(bounds, true);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected void drawStraighten(Canvas canvas, Paint paint) {
- RectF bounds = straightenBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected RectF unrotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF pbounds = getLocalPhotoBounds();
float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale,
- xoff, yoff, 0);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected RectF cropBounds() {
- RectF bounds = getLocalCropBounds();
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- m.mapRect(bounds);
+ bounds = GeometryMath.scaleRect(bounds, scale);
+ float dx = (getWidth() / 2) - bounds.centerX();
+ float dy = (getHeight() / 2) - bounds.centerY();
+ bounds.offset(dx, dy);
return bounds;
}
- // Fails for non-90 degree
- protected void drawCrop(Canvas canvas, Paint paint) {
- RectF bounds = cropBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected void drawCropSafe(Canvas canvas, Paint paint) {
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- RectF crop = getLocalCropBounds();
- if (!m.rectStaysRect()) {
- float[] corners = getCornersFromRect(crop);
- m.mapPoints(corners);
- drawClosedPath(canvas, paint, corners);
- } else {
- m.mapRect(crop);
- Path path = new Path();
- path.addRect(crop, Path.Direction.CCW);
- canvas.drawPath(path, paint);
- }
- }
-
- protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
- paint.setARGB(255, 0, 0, 0);
- drawImageBitmap(canvas, bitmap, paint);
- paint.setColor(Color.WHITE);
- paint.setStyle(Style.STROKE);
- paint.setStrokeWidth(2);
- drawCropSafe(canvas, paint);
- paint.setColor(getDefaultBackgroundColor());
- paint.setStyle(Paint.Style.FILL);
- drawShadows(canvas, paint, unrotatedCropBounds());
- }
-
- protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
- RectF display = new RectF(0, 0, getWidth(), getHeight());
- drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
- getHeight() / 2);
- }
-
- protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
+ protected static void drawRotatedShadows(Canvas canvas, Paint p, RectF innerBounds,
+ RectF outerBounds,
float rotation, float centerX, float centerY) {
canvas.save();
canvas.rotate(rotation, centerX, centerY);
@@ -527,6 +400,15 @@ public abstract class ImageGeometry extends ImageSlave {
canvas.restore();
}
+ protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
+ float w = getWidth();
+ float h = getHeight();
+ canvas.drawRect(0f, 0f, w, innerBounds.top, p);
+ canvas.drawRect(0f, innerBounds.top, innerBounds.left, innerBounds.bottom, p);
+ canvas.drawRect(innerBounds.right, innerBounds.top, w, innerBounds.bottom, p);
+ canvas.drawRect(0f, innerBounds.bottom, w, h, p);
+ }
+
@Override
public void onDraw(Canvas canvas) {
if (getDirtyGeometryFlag()) {
@@ -547,21 +429,38 @@ public abstract class ImageGeometry extends ImageSlave {
// TODO: Override this stub.
}
- protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p) {
- p.setARGB(255, 0, 0, 0);
+ /**
+ * Sets up inputs for buildCenteredPhotoMatrix and buildWanderingCropMatrix
+ * and returns the scale factor.
+ */
+ protected float getTransformState(RectF photo, RectF crop, float[] displayCenter) {
RectF photoBounds = getLocalPhotoBounds();
RectF cropBounds = getLocalCropBounds();
float scale = computeScale(getWidth(), getHeight());
// checks if local rotation is an odd multiple of 90.
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = computeScale(getHeight(), getWidth());
}
// put in screen coordinates
- RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale);
- RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale);
- float[] displayCenter = {
- getWidth() / 2f, getHeight() / 2f
- };
+ if (crop != null) {
+ crop.set(GeometryMath.scaleRect(cropBounds, scale));
+ }
+ if (photo != null) {
+ photo.set(GeometryMath.scaleRect(photoBounds, scale));
+ }
+ if (displayCenter != null && displayCenter.length >= 2) {
+ displayCenter[0] = getWidth() / 2f;
+ displayCenter[1] = getHeight() / 2f;
+ }
+ return scale;
+ }
+
+ protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p, float[] offset) {
+ p.setARGB(255, 0, 0, 0);
+ float[] displayCenter = new float[2];
+ RectF scaledCrop = new RectF();
+ RectF scaledPhoto = new RectF();
+ float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter);
Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop,
getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
@@ -569,9 +468,11 @@ public abstract class ImageGeometry extends ImageSlave {
getLocalRotation(), getLocalStraighten(), getLocalFlip(), displayCenter);
m1.mapRect(scaledCrop);
Path path = new Path();
+ scaledCrop.offset(-offset[0], -offset[1]);
path.addRect(scaledCrop, Path.Direction.CCW);
m.preScale(scale, scale);
+ m.postTranslate(-offset[0], -offset[1]);
canvas.save();
canvas.drawBitmap(photo, m, p);
canvas.restore();
@@ -580,6 +481,11 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Style.STROKE);
p.setStrokeWidth(2);
canvas.drawPath(path, p);
+
+ p.setColor(getDefaultBackgroundColor());
+ p.setAlpha(128);
+ p.setStyle(Paint.Style.FILL);
+ drawShadows(canvas, p, scaledCrop);
return scaledCrop;
}
@@ -590,7 +496,7 @@ public abstract class ImageGeometry extends ImageSlave {
float imageHeight = cropBounds.height();
float scale = GeometryMath.scale(imageWidth, imageHeight, getWidth(), getHeight());
// checks if local rotation is an odd multiple of 90.
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = GeometryMath.scale(imageWidth, imageHeight, getHeight(), getWidth());
}
// put in screen coordinates
@@ -618,6 +524,8 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Paint.Style.FILL);
scaledCrop.offset(displayCenter[0] - scaledCrop.centerX(), displayCenter[1]
- scaledCrop.centerY());
- drawShadows(canvas, p, scaledCrop);
+ RectF display = new RectF(0, 0, getWidth(), getHeight());
+ drawRotatedShadows(canvas, p, scaledCrop, display, getLocalRotation(), getWidth() / 2,
+ getHeight() / 2);
}
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
new file mode 100644
index 000000000..5119dff3c
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
@@ -0,0 +1,148 @@
+
+package com.android.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.gallery3d.filtershow.filters.ImageFilterRedEye;
+import com.android.gallery3d.filtershow.filters.RedEyeCandidate;
+
+public class ImageRedEyes extends ImageSlave {
+
+ private static final String LOGTAG = "ImageRedEyes";
+ private RectF mCurrentRect = null;
+ private static float mTouchPadding = 80;
+
+ public static void setTouchPadding(float padding) {
+ mTouchPadding = padding;
+ }
+
+ public ImageRedEyes(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageRedEyes(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void resetParameter() {
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ if (filter != null) {
+ filter.clear();
+ }
+ mCurrentRect = null;
+ invalidate();
+ }
+
+ @Override
+ public void updateImage() {
+ super.updateImage();
+ invalidate();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ float ex = event.getX();
+ float ey = event.getY();
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+
+ // let's transform (ex, ey) to displayed image coordinates
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentRect = new RectF();
+ mCurrentRect.left = ex - mTouchPadding;
+ mCurrentRect.top = ey - mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ mCurrentRect.right = ex + mTouchPadding;
+ mCurrentRect.bottom = ey + mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (mCurrentRect != null) {
+ // transform to original coordinates
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+ Matrix originalNoRotateToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+
+ Matrix invert = new Matrix();
+ originalToScreen.invert(invert);
+ RectF r = new RectF(mCurrentRect);
+ invert.mapRect(r);
+ RectF r2 = new RectF(mCurrentRect);
+ invert.reset();
+ originalNoRotateToScreen.invert(invert);
+ invert.mapRect(r2);
+ filter.addRect(r, r2);
+ this.resetImageCaches(this);
+ }
+ mCurrentRect = null;
+ }
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Paint paint = new Paint();
+ paint.setStyle(Style.STROKE);
+ paint.setColor(Color.RED);
+ paint.setStrokeWidth(2);
+ if (mCurrentRect != null) {
+ paint.setColor(Color.RED);
+ RectF drawRect = new RectF(mCurrentRect);
+ canvas.drawRect(drawRect, paint);
+ }
+
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+ Matrix originalRotateToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ for (RedEyeCandidate candidate : filter.getCandidates()) {
+ RectF rect = candidate.getRect();
+ RectF drawRect = new RectF();
+ originalToScreen.mapRect(drawRect, rect);
+ RectF fullRect = new RectF();
+ originalRotateToScreen.mapRect(fullRect, rect);
+ paint.setColor(Color.BLUE);
+ canvas.drawRect(fullRect, paint);
+ canvas.drawLine(fullRect.centerX(), fullRect.top,
+ fullRect.centerX(), fullRect.bottom, paint);
+ canvas.drawLine(fullRect.left, fullRect.centerY(),
+ fullRect.right, fullRect.centerY(), paint);
+ paint.setColor(Color.GREEN);
+ float dw = drawRect.width();
+ float dh = drawRect.height();
+ float dx = fullRect.centerX() - dw/2;
+ float dy = fullRect.centerY() - dh/2;
+ drawRect.set(dx, dy, dx + dw, dy + dh);
+ canvas.drawRect(drawRect, paint);
+ canvas.drawLine(drawRect.centerX(), drawRect.top,
+ drawRect.centerX(), drawRect.bottom, paint);
+ canvas.drawLine(drawRect.left, drawRect.centerY(),
+ drawRect.right, drawRect.centerY(), paint);
+ canvas.drawCircle(drawRect.centerX(), drawRect.centerY(),
+ mTouchPadding, paint);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 0145c24dc..d9df7b7fc 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -23,6 +23,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.net.Uri;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
@@ -83,7 +84,7 @@ public class ImageShow extends View implements OnGestureListener,
private HistoryAdapter mHistoryAdapter = null;
private ImageStateAdapter mImageStateAdapter = null;
- private Rect mImageBounds = new Rect();
+ protected Rect mImageBounds = new Rect();
private boolean mTouchShowOriginal = false;
private long mTouchShowOriginalDate = 0;
@@ -230,6 +231,7 @@ public class ImageShow extends View implements OnGestureListener,
}
updateSeekBar(parameter, minp, maxp);
invalidate();
+ mActivity.enableSave(hasModifications());
}
@Override
@@ -323,6 +325,10 @@ public class ImageShow extends View implements OnGestureListener,
return dst;
}
+ public Rect getImageCropBounds() {
+ return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds());
+ }
+
public Rect getDisplayedImageBounds() {
return mImageBounds;
}
@@ -396,6 +402,7 @@ public class ImageShow extends View implements OnGestureListener,
public void updateImagePresets(boolean force) {
ImagePreset preset = getImagePreset();
if (preset == null) {
+ mActivity.enableSave(false);
return;
}
if (force) {
@@ -419,6 +426,7 @@ public class ImageShow extends View implements OnGestureListener,
mFiltersOnlyImage = null;
}
}
+ mActivity.enableSave(hasModifications());
}
public void requestFilteredImages() {
@@ -643,13 +651,13 @@ public class ImageShow extends View implements OnGestureListener,
}
public void updateImage() {
+ invalidate();
if (!updateGeometryFlags()) {
return;
}
Bitmap bitmap = mImageLoader.getOriginalBitmapLarge();
if (bitmap != null) {
imageSizeChanged(bitmap);
- invalidate();
}
}
@@ -666,6 +674,14 @@ public class ImageShow extends View implements OnGestureListener,
mImageLoader.saveImage(getImagePreset(), filterShowActivity, file);
}
+ public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) {
+ mImageLoader.saveToUri(f, u, m, filterShowActivity);
+ }
+
+ public void returnFilteredResult(FilterShowActivity filterShowActivity) {
+ mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
index 6a79e18a1..2a3ee2856 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
@@ -47,6 +47,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
protected final int mTextColor = Color.WHITE;
private ImageSmallFilter mNullFilter;
+ private Bitmap mOverlayBitmap = null;
+ private final int mOverlayTint = Color.argb(100, 0, 0, 0);
+
+ public void setOverlayBitmap(Bitmap bitmap){
+ mOverlayBitmap = bitmap;
+ }
+
public static void setMargin(int value) {
mMargin = value;
}
@@ -77,6 +84,10 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mImagePreset.add(mImageFilter);
}
+ public ImageFilter getImageFilter() {
+ return mImageFilter;
+ }
+
@Override
public void setSelected(boolean value) {
if (mIsSelected != value) {
@@ -188,6 +199,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
canvas.drawText(mImageFilter.getName(), x, y - mTextMargin, mPaint);
+ if (mOverlayBitmap != null) {
+ mPaint.setColor(mOverlayTint);
+ canvas.drawRect(0, mMargin, getWidth(), getWidth() + mMargin, mPaint);
+ Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
+ mPaint.setColor(Color.BLACK);
+ drawImage(canvas, mOverlayBitmap, d);
+ }
}
public void drawImage(Canvas canvas, Bitmap image, Rect destination) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index 57a22aab3..7a539da8f 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
@@ -105,10 +106,10 @@ public class ImageStraighten extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- drawTransformed(canvas, image, gPaint);
+ float [] o = {0, 0};
+ RectF bounds = drawTransformed(canvas, image, gPaint, o);
// Draw the grid
- 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/imageshow/ImageWithIcon.java b/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
deleted file mode 100644
index a332fa72a..000000000
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.filtershow.imageshow;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-
-/**
- * TODO: Insert description here. (generated by hoford)
- */
-public class ImageWithIcon extends ImageSmallFilter {
- /**
- * @param context
- */
- public ImageWithIcon(Context context) {
- super(context);
- // TODO(hoford): Auto-generated constructor stub
- }
-
- private Bitmap bitmap;
-
- public void setIcon(Bitmap bitmap){
- this.bitmap = bitmap;
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (bitmap != null) {
- Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
- drawImage(canvas, bitmap, d);
- }
- }
-}
diff --git a/src/com/android/gallery3d/filtershow/tools/BitmapTask.java b/src/com/android/gallery3d/filtershow/tools/BitmapTask.java
new file mode 100644
index 000000000..62801c1f2
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/tools/BitmapTask.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.tools;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+
+/**
+ * Asynchronous task filtering or doign I/O with bitmaps.
+ */
+public class BitmapTask <T> extends AsyncTask<T, Void, Bitmap> {
+
+ private Callbacks<T> mCallbacks;
+ private static final String LOGTAG = "BitmapTask";
+
+ public BitmapTask(Callbacks<T> callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ protected Bitmap doInBackground(T... params) {
+ if (params == null || mCallbacks == null) {
+ return null;
+ }
+ return mCallbacks.onExecute(params[0]);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.onComplete(result);
+ }
+
+ @Override
+ protected void onCancelled() {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.onCancel();
+ }
+
+ /**
+ * Callbacks for the asynchronous task.
+ */
+ public interface Callbacks<P> {
+ void onComplete(Bitmap result);
+
+ void onCancel();
+
+ Bitmap onExecute(P param);
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
index 9c55623d1..30659e677 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
@@ -52,9 +52,6 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
private static final String LOGTAG = "SaveCopyTask";
- private static final int DEFAULT_COMPRESS_QUALITY = 95;
- private static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
-
/**
* Saves the bitmap in the final destination
*/
@@ -62,7 +59,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
OutputStream os = null;
try {
os = new FileOutputStream(destination);
- bitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, os);
+ bitmap.compress(CompressFormat.JPEG, ImageLoader.DEFAULT_COMPRESS_QUALITY, os);
} catch (FileNotFoundException e) {
Log.v(LOGTAG,"Error in writing "+destination.getAbsolutePath());
} finally {
@@ -123,7 +120,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
File saveDirectory = getSaveDirectory(context, sourceUri);
if ((saveDirectory == null) || !saveDirectory.canWrite()) {
saveDirectory = new File(Environment.getExternalStorageDirectory(),
- DEFAULT_SAVE_DIRECTORY);
+ ImageLoader.DEFAULT_SAVE_DIRECTORY);
}
// Create the directory if it doesn't exist
if (!saveDirectory.exists()) saveDirectory.mkdirs();
@@ -137,19 +134,6 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
return new File(saveDirectory, filename + ".JPG");
}
- private Bitmap loadMutableBitmap() throws FileNotFoundException {
- BitmapFactory.Options options = new BitmapFactory.Options();
- // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
- // exist)
- options.inMutable = true;
-
- InputStream is = context.getContentResolver().openInputStream(sourceUri);
- Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
- int orientation = ImageLoader.getOrientation(context, sourceUri);
- bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
- return bitmap;
- }
-
private static final String[] COPY_EXIF_ATTRIBUTES = new String[] {
ExifInterface.TAG_APERTURE,
ExifInterface.TAG_DATETIME,
@@ -228,7 +212,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
ImagePreset preset = params[0];
try {
- Bitmap bitmap = preset.apply(loadMutableBitmap());
+ Bitmap bitmap = preset.apply(ImageLoader.loadMutableBitmap(context, sourceUri));
Object xmp = null;
InputStream is = null;
diff --git a/src/com/android/gallery3d/gadget/WidgetConfigure.java b/src/com/android/gallery3d/gadget/WidgetConfigure.java
index 331e7d2c4..3a1b52bdc 100644
--- a/src/com/android/gallery3d/gadget/WidgetConfigure.java
+++ b/src/com/android/gallery3d/gadget/WidgetConfigure.java
@@ -27,9 +27,10 @@ import android.widget.RemoteViews;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AlbumPicker;
-import com.android.gallery3d.app.CropImage;
import com.android.gallery3d.app.DialogPicker;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.CropExtras;
public class WidgetConfigure extends Activity {
@SuppressWarnings("unused")
@@ -142,14 +143,14 @@ public class WidgetConfigure extends Activity {
int widgetHeight = Math.round(height * scale);
mPickedItem = data.getData();
- Intent request = new Intent(CropImage.ACTION_CROP, mPickedItem)
- .putExtra(CropImage.KEY_OUTPUT_X, widgetWidth)
- .putExtra(CropImage.KEY_OUTPUT_Y, widgetHeight)
- .putExtra(CropImage.KEY_ASPECT_X, widgetWidth)
- .putExtra(CropImage.KEY_ASPECT_Y, widgetHeight)
- .putExtra(CropImage.KEY_SCALE_UP_IF_NEEDED, true)
- .putExtra(CropImage.KEY_SCALE, true)
- .putExtra(CropImage.KEY_RETURN_DATA, true);
+ Intent request = new Intent(FilterShowActivity.CROP_ACTION, mPickedItem)
+ .putExtra(CropExtras.KEY_OUTPUT_X, widgetWidth)
+ .putExtra(CropExtras.KEY_OUTPUT_Y, widgetHeight)
+ .putExtra(CropExtras.KEY_ASPECT_X, widgetWidth)
+ .putExtra(CropExtras.KEY_ASPECT_Y, widgetHeight)
+ .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true)
+ .putExtra(CropExtras.KEY_SCALE, true)
+ .putExtra(CropExtras.KEY_RETURN_DATA, true);
startActivityForResult(request, REQUEST_CROP_IMAGE);
}
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index 7191599ad..d90fc20d0 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -17,21 +17,21 @@
package com.android.gallery3d.ui;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Handler;
-import android.view.ActionMode;
-import android.view.ActionMode.Callback;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
-import android.widget.ShareActionProvider;
-import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.ActionMode;
+import com.actionbarsherlock.view.ActionMode.Callback;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
+import com.actionbarsherlock.widget.ShareActionProvider.OnShareTargetSelectedListener;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.common.ApiHelper;
@@ -129,7 +129,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
}
public void startActionMode() {
- Activity a = mActivity;
+ SherlockActivity a = mActivity;
mActionMode = a.startActionMode(this);
View customView = LayoutInflater.from(a).inflate(
R.layout.action_mode, null);
@@ -408,6 +408,15 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
// Pass1: Deal with unexpanded media object list for menu operation.
ArrayList<MediaObject> selected = getSelectedMediaObjects(jc);
if (selected == null) {
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mMenuTask = null;
+ if (jc.isCancelled()) return;
+ // Disable all the operations when no item is selected
+ MenuExecutor.updateMenuOperation(mMenu, 0);
+ }
+ });
return null;
}
final int operation = computeMenuOptions(selected);
@@ -466,7 +475,12 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi
mMenuExecutor.pause();
}
+ public void destroy() {
+ mMenuExecutor.destroy();
+ }
+
public void resume() {
if (mSelectionManager.inSelectionMode()) updateSupportedOperation();
+ mMenuExecutor.resume();
}
}
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 99cf0571c..38686d59f 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -36,7 +36,7 @@ abstract class BasicTexture implements Texture {
// Log a warning if a texture is larger along a dimension
private static final int MAX_TEXTURE_SIZE = 4096;
- protected int mId;
+ protected int mId = -1;
protected int mState;
protected int mWidth = UNSPECIFIED;
@@ -165,8 +165,9 @@ abstract class BasicTexture implements Texture {
private void freeResource() {
GLCanvas canvas = mCanvasRef;
- if (canvas != null && isLoaded()) {
+ if (canvas != null && mId != -1) {
canvas.unloadTexture(this);
+ mId = -1; // Don't free it again.
}
mState = STATE_UNLOADED;
setAssociatedCanvas(null);
diff --git a/src/com/android/gallery3d/ui/CropView.java b/src/com/android/gallery3d/ui/CropView.java
deleted file mode 100644
index 1890c7630..000000000
--- a/src/com/android/gallery3d/ui/CropView.java
+++ /dev/null
@@ -1,801 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.ui;
-
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.media.FaceDetector;
-import android.os.Handler;
-import android.os.Message;
-import android.util.FloatMath;
-import android.view.MotionEvent;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Toast;
-
-import com.android.gallery3d.R;
-import com.android.gallery3d.anim.Animation;
-import com.android.gallery3d.app.AbstractGalleryActivity;
-import com.android.gallery3d.common.Utils;
-
-import java.util.ArrayList;
-
-import javax.microedition.khronos.opengles.GL11;
-
-/**
- * The activity can crop specific region of interest from an image.
- */
-public class CropView extends GLView {
- @SuppressWarnings("unused")
- private static final String TAG = "CropView";
-
- private static final int FACE_PIXEL_COUNT = 120000; // around 400x300
-
- private static final int COLOR_OUTLINE = 0xFF008AFF;
- private static final int COLOR_FACE_OUTLINE = 0xFF000000;
-
- private static final float OUTLINE_WIDTH = 3f;
-
- private static final int SIZE_UNKNOWN = -1;
- private static final int TOUCH_TOLERANCE = 30;
-
- private static final float MIN_SELECTION_LENGTH = 16f;
- public static final float UNSPECIFIED = -1f;
-
- private static final int MAX_FACE_COUNT = 3;
- private static final float FACE_EYE_RATIO = 2f;
-
- private static final int ANIMATION_DURATION = 1250;
-
- private static final int MOVE_LEFT = 1;
- private static final int MOVE_TOP = 2;
- private static final int MOVE_RIGHT = 4;
- private static final int MOVE_BOTTOM = 8;
- private static final int MOVE_BLOCK = 16;
-
- private static final float MAX_SELECTION_RATIO = 0.8f;
- private static final float MIN_SELECTION_RATIO = 0.4f;
- private static final float SELECTION_RATIO = 0.60f;
- private static final int ANIMATION_TRIGGER = 64;
-
- private static final int MSG_UPDATE_FACES = 1;
-
- private float mAspectRatio = UNSPECIFIED;
- private float mSpotlightRatioX = 0;
- private float mSpotlightRatioY = 0;
-
- private Handler mMainHandler;
-
- private FaceHighlightView mFaceDetectionView;
- private HighlightRectangle mHighlightRectangle;
- private TileImageView mImageView;
- private AnimationController mAnimation = new AnimationController();
-
- private int mImageWidth = SIZE_UNKNOWN;
- private int mImageHeight = SIZE_UNKNOWN;
-
- private AbstractGalleryActivity mActivity;
-
- private GLPaint mPaint = new GLPaint();
- private GLPaint mFacePaint = new GLPaint();
-
- private int mImageRotation;
-
- public CropView(AbstractGalleryActivity activity) {
- mActivity = activity;
- mImageView = new TileImageView(activity);
- mFaceDetectionView = new FaceHighlightView();
- mHighlightRectangle = new HighlightRectangle();
-
- addComponent(mImageView);
- addComponent(mFaceDetectionView);
- addComponent(mHighlightRectangle);
-
- mHighlightRectangle.setVisibility(GLView.INVISIBLE);
-
- mPaint.setColor(COLOR_OUTLINE);
- mPaint.setLineWidth(OUTLINE_WIDTH);
-
- mFacePaint.setColor(COLOR_FACE_OUTLINE);
- mFacePaint.setLineWidth(OUTLINE_WIDTH);
-
- mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
- @Override
- public void handleMessage(Message message) {
- Utils.assertTrue(message.what == MSG_UPDATE_FACES);
- ((DetectFaceTask) message.obj).updateFaces();
- }
- };
- }
-
- public void setAspectRatio(float ratio) {
- mAspectRatio = ratio;
- }
-
- public void setSpotlightRatio(float ratioX, float ratioY) {
- mSpotlightRatioX = ratioX;
- mSpotlightRatioY = ratioY;
- }
-
- @Override
- public void onLayout(boolean changed, int l, int t, int r, int b) {
- int width = r - l;
- int height = b - t;
-
- mFaceDetectionView.layout(0, 0, width, height);
- mHighlightRectangle.layout(0, 0, width, height);
- mImageView.layout(0, 0, width, height);
- if (mImageHeight != SIZE_UNKNOWN) {
- mAnimation.initialize();
- if (mHighlightRectangle.getVisibility() == GLView.VISIBLE) {
- mAnimation.parkNow(
- mHighlightRectangle.mHighlightRect);
- }
- }
- }
-
- private boolean setImageViewPosition(int centerX, int centerY, float scale) {
- int inverseX = mImageWidth - centerX;
- int inverseY = mImageHeight - centerY;
- TileImageView t = mImageView;
- int rotation = mImageRotation;
- switch (rotation) {
- case 0: return t.setPosition(centerX, centerY, scale, 0);
- case 90: return t.setPosition(centerY, inverseX, scale, 90);
- case 180: return t.setPosition(inverseX, inverseY, scale, 180);
- case 270: return t.setPosition(inverseY, centerX, scale, 270);
- default: throw new IllegalArgumentException(String.valueOf(rotation));
- }
- }
-
- @Override
- public void render(GLCanvas canvas) {
- AnimationController a = mAnimation;
- if (a.calculate(AnimationTime.get())) invalidate();
- setImageViewPosition(a.getCenterX(), a.getCenterY(), a.getScale());
- super.render(canvas);
- }
-
- @Override
- public void renderBackground(GLCanvas canvas) {
- canvas.clearBuffer();
- }
-
- public RectF getCropRectangle() {
- if (mHighlightRectangle.getVisibility() == GLView.INVISIBLE) return null;
- RectF rect = mHighlightRectangle.mHighlightRect;
- RectF result = new RectF(rect.left * mImageWidth, rect.top * mImageHeight,
- rect.right * mImageWidth, rect.bottom * mImageHeight);
- return result;
- }
-
- public int getImageWidth() {
- return mImageWidth;
- }
-
- public int getImageHeight() {
- return mImageHeight;
- }
-
- private class FaceHighlightView extends GLView {
- private static final int INDEX_NONE = -1;
- private ArrayList<RectF> mFaces = new ArrayList<RectF>();
- private RectF mRect = new RectF();
- private int mPressedFaceIndex = INDEX_NONE;
-
- public void addFace(RectF faceRect) {
- mFaces.add(faceRect);
- invalidate();
- }
-
- private void renderFace(GLCanvas canvas, RectF face, boolean pressed) {
- GL11 gl = canvas.getGLInstance();
- if (pressed) {
- gl.glEnable(GL11.GL_STENCIL_TEST);
- gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
- gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
- }
-
- RectF r = mAnimation.mapRect(face, mRect);
- canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mFacePaint);
-
- if (pressed) {
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
- }
- }
-
- @Override
- protected void renderBackground(GLCanvas canvas) {
- ArrayList<RectF> faces = mFaces;
- for (int i = 0, n = faces.size(); i < n; ++i) {
- renderFace(canvas, faces.get(i), i == mPressedFaceIndex);
- }
-
- GL11 gl = canvas.getGLInstance();
- if (mPressedFaceIndex != INDEX_NONE) {
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- canvas.fillRect(0, 0, getWidth(), getHeight(), 0x66000000);
- gl.glDisable(GL11.GL_STENCIL_TEST);
- }
- }
-
- private void setPressedFace(int index) {
- if (mPressedFaceIndex == index) return;
- mPressedFaceIndex = index;
- invalidate();
- }
-
- private int getFaceIndexByPosition(float x, float y) {
- ArrayList<RectF> faces = mFaces;
- for (int i = 0, n = faces.size(); i < n; ++i) {
- RectF r = mAnimation.mapRect(faces.get(i), mRect);
- if (r.contains(x, y)) return i;
- }
- return INDEX_NONE;
- }
-
- @Override
- protected boolean onTouch(MotionEvent event) {
- float x = event.getX();
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE: {
- setPressedFace(getFaceIndexByPosition(x, y));
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- int index = mPressedFaceIndex;
- setPressedFace(INDEX_NONE);
- if (index != INDEX_NONE) {
- mHighlightRectangle.setRectangle(mFaces.get(index));
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- setVisibility(GLView.INVISIBLE);
- }
- }
- }
- return true;
- }
- }
-
- private class AnimationController extends Animation {
- private int mCurrentX;
- private int mCurrentY;
- private float mCurrentScale;
- private int mStartX;
- private int mStartY;
- private float mStartScale;
- private int mTargetX;
- private int mTargetY;
- private float mTargetScale;
-
- public AnimationController() {
- setDuration(ANIMATION_DURATION);
- setInterpolator(new DecelerateInterpolator(4));
- }
-
- public void initialize() {
- mCurrentX = mImageWidth / 2;
- mCurrentY = mImageHeight / 2;
- mCurrentScale = Math.min(2, Math.min(
- (float) getWidth() / mImageWidth,
- (float) getHeight() / mImageHeight));
- }
-
- public void startParkingAnimation(RectF highlight) {
- RectF r = mAnimation.mapRect(highlight, new RectF());
- int width = getWidth();
- int height = getHeight();
-
- float wr = r.width() / width;
- float hr = r.height() / height;
- final int d = ANIMATION_TRIGGER;
- if (wr >= MIN_SELECTION_RATIO && wr < MAX_SELECTION_RATIO
- && hr >= MIN_SELECTION_RATIO && hr < MAX_SELECTION_RATIO
- && r.left >= d && r.right < width - d
- && r.top >= d && r.bottom < height - d) return;
-
- mStartX = mCurrentX;
- mStartY = mCurrentY;
- mStartScale = mCurrentScale;
- calculateTarget(highlight);
- start();
- }
-
- public void parkNow(RectF highlight) {
- calculateTarget(highlight);
- forceStop();
- mStartX = mCurrentX = mTargetX;
- mStartY = mCurrentY = mTargetY;
- mStartScale = mCurrentScale = mTargetScale;
- }
-
- public void inverseMapPoint(PointF point) {
- float s = mCurrentScale;
- point.x = Utils.clamp(((point.x - getWidth() * 0.5f) / s
- + mCurrentX) / mImageWidth, 0, 1);
- point.y = Utils.clamp(((point.y - getHeight() * 0.5f) / s
- + mCurrentY) / mImageHeight, 0, 1);
- }
-
- public RectF mapRect(RectF input, RectF output) {
- float offsetX = getWidth() * 0.5f;
- float offsetY = getHeight() * 0.5f;
- int x = mCurrentX;
- int y = mCurrentY;
- float s = mCurrentScale;
- output.set(
- offsetX + (input.left * mImageWidth - x) * s,
- offsetY + (input.top * mImageHeight - y) * s,
- offsetX + (input.right * mImageWidth - x) * s,
- offsetY + (input.bottom * mImageHeight - y) * s);
- return output;
- }
-
- @Override
- protected void onCalculate(float progress) {
- mCurrentX = Math.round(mStartX + (mTargetX - mStartX) * progress);
- mCurrentY = Math.round(mStartY + (mTargetY - mStartY) * progress);
- mCurrentScale = mStartScale + (mTargetScale - mStartScale) * progress;
-
- if (mCurrentX == mTargetX && mCurrentY == mTargetY
- && mCurrentScale == mTargetScale) forceStop();
- }
-
- public int getCenterX() {
- return mCurrentX;
- }
-
- public int getCenterY() {
- return mCurrentY;
- }
-
- public float getScale() {
- return mCurrentScale;
- }
-
- private void calculateTarget(RectF highlight) {
- float width = getWidth();
- float height = getHeight();
-
- if (mImageWidth != SIZE_UNKNOWN) {
- float minScale = Math.min(width / mImageWidth, height / mImageHeight);
- float scale = Utils.clamp(SELECTION_RATIO * Math.min(
- width / (highlight.width() * mImageWidth),
- height / (highlight.height() * mImageHeight)), minScale, 2f);
- int centerX = Math.round(
- mImageWidth * (highlight.left + highlight.right) * 0.5f);
- int centerY = Math.round(
- mImageHeight * (highlight.top + highlight.bottom) * 0.5f);
-
- if (Math.round(mImageWidth * scale) > width) {
- int limitX = Math.round(width * 0.5f / scale);
- centerX = Math.round(
- (highlight.left + highlight.right) * mImageWidth / 2);
- centerX = Utils.clamp(centerX, limitX, mImageWidth - limitX);
- } else {
- centerX = mImageWidth / 2;
- }
- if (Math.round(mImageHeight * scale) > height) {
- int limitY = Math.round(height * 0.5f / scale);
- centerY = Math.round(
- (highlight.top + highlight.bottom) * mImageHeight / 2);
- centerY = Utils.clamp(centerY, limitY, mImageHeight - limitY);
- } else {
- centerY = mImageHeight / 2;
- }
- mTargetX = centerX;
- mTargetY = centerY;
- mTargetScale = scale;
- }
- }
-
- }
-
- private class HighlightRectangle extends GLView {
- private RectF mHighlightRect = new RectF(0.25f, 0.25f, 0.75f, 0.75f);
- private RectF mTempRect = new RectF();
- private PointF mTempPoint = new PointF();
-
- private ResourceTexture mArrow;
-
- private int mMovingEdges = 0;
- private float mReferenceX;
- private float mReferenceY;
-
- public HighlightRectangle() {
- mArrow = new ResourceTexture(mActivity.getAndroidContext(),
- R.drawable.camera_crop_holo);
- }
-
- public void setInitRectangle() {
- float targetRatio = mAspectRatio == UNSPECIFIED
- ? 1f
- : mAspectRatio * mImageHeight / mImageWidth;
- float w = SELECTION_RATIO / 2f;
- float h = SELECTION_RATIO / 2f;
- if (targetRatio > 1) {
- h = w / targetRatio;
- } else {
- w = h * targetRatio;
- }
- mHighlightRect.set(0.5f - w, 0.5f - h, 0.5f + w, 0.5f + h);
- }
-
- public void setRectangle(RectF faceRect) {
- mHighlightRect.set(faceRect);
- mAnimation.startParkingAnimation(faceRect);
- invalidate();
- }
-
- private void moveEdges(MotionEvent event) {
- float scale = mAnimation.getScale();
- float dx = (event.getX() - mReferenceX) / scale / mImageWidth;
- float dy = (event.getY() - mReferenceY) / scale / mImageHeight;
- mReferenceX = event.getX();
- mReferenceY = event.getY();
- RectF r = mHighlightRect;
-
- if ((mMovingEdges & MOVE_BLOCK) != 0) {
- dx = Utils.clamp(dx, -r.left, 1 - r.right);
- dy = Utils.clamp(dy, -r.top , 1 - r.bottom);
- r.top += dy;
- r.bottom += dy;
- r.left += dx;
- r.right += dx;
- } else {
- PointF point = mTempPoint;
- point.set(mReferenceX, mReferenceY);
- mAnimation.inverseMapPoint(point);
- float left = r.left + MIN_SELECTION_LENGTH / mImageWidth;
- float right = r.right - MIN_SELECTION_LENGTH / mImageWidth;
- float top = r.top + MIN_SELECTION_LENGTH / mImageHeight;
- float bottom = r.bottom - MIN_SELECTION_LENGTH / mImageHeight;
- if ((mMovingEdges & MOVE_RIGHT) != 0) {
- r.right = Utils.clamp(point.x, left, 1f);
- }
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(point.x, 0, right);
- }
- if ((mMovingEdges & MOVE_TOP) != 0) {
- r.top = Utils.clamp(point.y, 0, bottom);
- }
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(point.y, top, 1f);
- }
- if (mAspectRatio != UNSPECIFIED) {
- float targetRatio = mAspectRatio * mImageHeight / mImageWidth;
- if (r.width() / r.height() > targetRatio) {
- float height = r.width() / targetRatio;
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(r.top + height, top, 1f);
- } else {
- r.top = Utils.clamp(r.bottom - height, 0, bottom);
- }
- } else {
- float width = r.height() * targetRatio;
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(r.right - width, 0, right);
- } else {
- r.right = Utils.clamp(r.left + width, left, 1f);
- }
- }
- if (r.width() / r.height() > targetRatio) {
- float width = r.height() * targetRatio;
- if ((mMovingEdges & MOVE_LEFT) != 0) {
- r.left = Utils.clamp(r.right - width, 0, right);
- } else {
- r.right = Utils.clamp(r.left + width, left, 1f);
- }
- } else {
- float height = r.width() / targetRatio;
- if ((mMovingEdges & MOVE_BOTTOM) != 0) {
- r.bottom = Utils.clamp(r.top + height, top, 1f);
- } else {
- r.top = Utils.clamp(r.bottom - height, 0, bottom);
- }
- }
- }
- }
- invalidate();
- }
-
- private void setMovingEdges(MotionEvent event) {
- RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
- float x = event.getX();
- float y = event.getY();
-
- if (x > r.left + TOUCH_TOLERANCE && x < r.right - TOUCH_TOLERANCE
- && y > r.top + TOUCH_TOLERANCE && y < r.bottom - TOUCH_TOLERANCE) {
- mMovingEdges = MOVE_BLOCK;
- return;
- }
-
- boolean inVerticalRange = (r.top - TOUCH_TOLERANCE) <= y
- && y <= (r.bottom + TOUCH_TOLERANCE);
- boolean inHorizontalRange = (r.left - TOUCH_TOLERANCE) <= x
- && x <= (r.right + TOUCH_TOLERANCE);
-
- if (inVerticalRange) {
- boolean left = Math.abs(x - r.left) <= TOUCH_TOLERANCE;
- boolean right = Math.abs(x - r.right) <= TOUCH_TOLERANCE;
- if (left && right) {
- left = Math.abs(x - r.left) < Math.abs(x - r.right);
- right = !left;
- }
- if (left) mMovingEdges |= MOVE_LEFT;
- if (right) mMovingEdges |= MOVE_RIGHT;
- if (mAspectRatio != UNSPECIFIED && inHorizontalRange) {
- mMovingEdges |= (y >
- (r.top + r.bottom) / 2) ? MOVE_BOTTOM : MOVE_TOP;
- }
- }
- if (inHorizontalRange) {
- boolean top = Math.abs(y - r.top) <= TOUCH_TOLERANCE;
- boolean bottom = Math.abs(y - r.bottom) <= TOUCH_TOLERANCE;
- if (top && bottom) {
- top = Math.abs(y - r.top) < Math.abs(y - r.bottom);
- bottom = !top;
- }
- if (top) mMovingEdges |= MOVE_TOP;
- if (bottom) mMovingEdges |= MOVE_BOTTOM;
- if (mAspectRatio != UNSPECIFIED && inVerticalRange) {
- mMovingEdges |= (x >
- (r.left + r.right) / 2) ? MOVE_RIGHT : MOVE_LEFT;
- }
- }
- }
-
- @Override
- protected boolean onTouch(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- mReferenceX = event.getX();
- mReferenceY = event.getY();
- setMovingEdges(event);
- invalidate();
- return true;
- }
- case MotionEvent.ACTION_MOVE:
- moveEdges(event);
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- mMovingEdges = 0;
- mAnimation.startParkingAnimation(mHighlightRect);
- invalidate();
- return true;
- }
- }
- return true;
- }
-
- @Override
- protected void renderBackground(GLCanvas canvas) {
- RectF r = mAnimation.mapRect(mHighlightRect, mTempRect);
- drawHighlightRectangle(canvas, r);
-
- float centerY = (r.top + r.bottom) / 2;
- float centerX = (r.left + r.right) / 2;
- boolean notMoving = mMovingEdges == 0;
- if ((mMovingEdges & MOVE_RIGHT) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(r.right - mArrow.getWidth() / 2),
- Math.round(centerY - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_LEFT) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(r.left - mArrow.getWidth() / 2),
- Math.round(centerY - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_TOP) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(centerX - mArrow.getWidth() / 2),
- Math.round(r.top - mArrow.getHeight() / 2));
- }
- if ((mMovingEdges & MOVE_BOTTOM) != 0 || notMoving) {
- mArrow.draw(canvas,
- Math.round(centerX - mArrow.getWidth() / 2),
- Math.round(r.bottom - mArrow.getHeight() / 2));
- }
- }
-
- private void drawHighlightRectangle(GLCanvas canvas, RectF r) {
- GL11 gl = canvas.getGLInstance();
- gl.glLineWidth(3.0f);
- gl.glEnable(GL11.GL_LINE_SMOOTH);
-
- gl.glEnable(GL11.GL_STENCIL_TEST);
- gl.glClear(GL11.GL_STENCIL_BUFFER_BIT);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
- gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
-
- if (mSpotlightRatioX == 0 || mSpotlightRatioY == 0) {
- canvas.fillRect(r.left, r.top, r.width(), r.height(), Color.TRANSPARENT);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
- } else {
- float sx = r.width() * mSpotlightRatioX;
- float sy = r.height() * mSpotlightRatioY;
- float cx = r.centerX();
- float cy = r.centerY();
-
- canvas.fillRect(cx - sx / 2, cy - sy / 2, sx, sy, Color.TRANSPARENT);
- canvas.drawRect(cx - sx / 2, cy - sy / 2, sx, sy, mPaint);
- canvas.drawRect(r.left, r.top, r.width(), r.height(), mPaint);
-
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
-
- canvas.drawRect(cx - sy / 2, cy - sx / 2, sy, sx, mPaint);
- canvas.fillRect(cx - sy / 2, cy - sx / 2, sy, sx, Color.TRANSPARENT);
- canvas.fillRect(r.left, r.top, r.width(), r.height(), 0x80000000);
- }
-
- gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1);
- gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
-
- canvas.fillRect(0, 0, getWidth(), getHeight(), 0xA0000000);
-
- gl.glDisable(GL11.GL_STENCIL_TEST);
- }
- }
-
- private class DetectFaceTask extends Thread {
- private final FaceDetector.Face[] mFaces = new FaceDetector.Face[MAX_FACE_COUNT];
- private final Bitmap mFaceBitmap;
- private int mFaceCount;
-
- public DetectFaceTask(Bitmap bitmap) {
- mFaceBitmap = bitmap;
- setName("face-detect");
- }
-
- @Override
- public void run() {
- Bitmap bitmap = mFaceBitmap;
- FaceDetector detector = new FaceDetector(
- bitmap.getWidth(), bitmap.getHeight(), MAX_FACE_COUNT);
- mFaceCount = detector.findFaces(bitmap, mFaces);
- mMainHandler.sendMessage(
- mMainHandler.obtainMessage(MSG_UPDATE_FACES, this));
- }
-
- private RectF getFaceRect(FaceDetector.Face face) {
- PointF point = new PointF();
- face.getMidPoint(point);
-
- int width = mFaceBitmap.getWidth();
- int height = mFaceBitmap.getHeight();
- float rx = face.eyesDistance() * FACE_EYE_RATIO;
- float ry = rx;
- float aspect = mAspectRatio;
- if (aspect != UNSPECIFIED) {
- if (aspect > 1) {
- rx = ry * aspect;
- } else {
- ry = rx / aspect;
- }
- }
-
- RectF r = new RectF(
- point.x - rx, point.y - ry, point.x + rx, point.y + ry);
- r.intersect(0, 0, width, height);
-
- if (aspect != UNSPECIFIED) {
- if (r.width() / r.height() > aspect) {
- float w = r.height() * aspect;
- r.left = (r.left + r.right - w) * 0.5f;
- r.right = r.left + w;
- } else {
- float h = r.width() / aspect;
- r.top = (r.top + r.bottom - h) * 0.5f;
- r.bottom = r.top + h;
- }
- }
-
- r.left /= width;
- r.right /= width;
- r.top /= height;
- r.bottom /= height;
- return r;
- }
-
- public void updateFaces() {
- if (mFaceCount > 1) {
- for (int i = 0, n = mFaceCount; i < n; ++i) {
- mFaceDetectionView.addFace(getFaceRect(mFaces[i]));
- }
- mFaceDetectionView.setVisibility(GLView.VISIBLE);
- Toast.makeText(mActivity.getAndroidContext(),
- R.string.multiface_crop_help, Toast.LENGTH_SHORT).show();
- } else if (mFaceCount == 1) {
- mFaceDetectionView.setVisibility(GLView.INVISIBLE);
- mHighlightRectangle.setRectangle(getFaceRect(mFaces[0]));
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- } else /*mFaceCount == 0*/ {
- mHighlightRectangle.setInitRectangle();
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- }
- }
- }
-
- public void setDataModel(TileImageView.Model dataModel, int rotation) {
- if (((rotation / 90) & 0x01) != 0) {
- mImageWidth = dataModel.getImageHeight();
- mImageHeight = dataModel.getImageWidth();
- } else {
- mImageWidth = dataModel.getImageWidth();
- mImageHeight = dataModel.getImageHeight();
- }
-
- mImageRotation = rotation;
-
- mImageView.setModel(dataModel);
- mAnimation.initialize();
- }
-
- public void detectFaces(Bitmap bitmap) {
- int rotation = mImageRotation;
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- float scale = FloatMath.sqrt((float) FACE_PIXEL_COUNT / (width * height));
-
- // faceBitmap is a correctly rotated bitmap, as viewed by a user.
- Bitmap faceBitmap;
- if (((rotation / 90) & 1) == 0) {
- int w = (Math.round(width * scale) & ~1); // must be even
- int h = Math.round(height * scale);
- faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
- Canvas canvas = new Canvas(faceBitmap);
- canvas.rotate(rotation, w / 2, h / 2);
- canvas.scale((float) w / width, (float) h / height);
- canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
- } else {
- int w = (Math.round(height * scale) & ~1); // must be even
- int h = Math.round(width * scale);
- faceBitmap = Bitmap.createBitmap(w, h, Config.RGB_565);
- Canvas canvas = new Canvas(faceBitmap);
- canvas.translate(w / 2, h / 2);
- canvas.rotate(rotation);
- canvas.translate(-h / 2, -w / 2);
- canvas.scale((float) w / height, (float) h / width);
- canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG));
- }
- new DetectFaceTask(faceBitmap).start();
- }
-
- public void initializeHighlightRectangle() {
- mHighlightRectangle.setInitRectangle();
- mHighlightRectangle.setVisibility(GLView.VISIBLE);
- }
-
- public void resume() {
- mImageView.prepareTextures();
- }
-
- public void pause() {
- mImageView.freeTextures();
- }
-}
-
diff --git a/src/com/android/gallery3d/ui/DetailsHelper.java b/src/com/android/gallery3d/ui/DetailsHelper.java
index 301601156..47296f655 100644
--- a/src/com/android/gallery3d/ui/DetailsHelper.java
+++ b/src/com/android/gallery3d/ui/DetailsHelper.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.ui;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.view.View.MeasureSpec;
import com.android.gallery3d.R;
@@ -44,6 +46,10 @@ public class DetailsHelper {
public void hide();
}
+ public interface ResolutionResolvingListener {
+ public void onResolutionAvailable(int width, int height);
+ }
+
public DetailsHelper(AbstractGalleryActivity activity, GLView rootPane, DetailsSource source) {
mContainer = new DialogDetailsView(activity, source);
}
@@ -75,6 +81,12 @@ public class DetailsHelper {
return sAddressResolver.resolveAddress(latlng, listener);
}
+ public static void resolveResolution(String path, ResolutionResolvingListener listener) {
+ Bitmap bitmap = BitmapFactory.decodeFile(path);
+ if (bitmap == null) return;
+ listener.onResolutionAvailable(bitmap.getWidth(), bitmap.getHeight());
+ }
+
public static void pause() {
if (sAddressResolver != null) sAddressResolver.cancel();
}
diff --git a/src/com/android/gallery3d/ui/DialogDetailsView.java b/src/com/android/gallery3d/ui/DialogDetailsView.java
index 8d96b821a..058c03654 100644
--- a/src/com/android/gallery3d/ui/DialogDetailsView.java
+++ b/src/com/android/gallery3d/ui/DialogDetailsView.java
@@ -37,6 +37,7 @@ import com.android.gallery3d.ui.DetailsAddressResolver.AddressResolvingListener;
import com.android.gallery3d.ui.DetailsHelper.CloseListener;
import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
import com.android.gallery3d.ui.DetailsHelper.DetailsViewContainer;
+import com.android.gallery3d.ui.DetailsHelper.ResolutionResolvingListener;
import java.util.ArrayList;
import java.util.Map.Entry;
@@ -111,9 +112,13 @@ public class DialogDetailsView implements DetailsViewContainer {
});
}
- private class DetailsAdapter extends BaseAdapter implements AddressResolvingListener {
+
+ private class DetailsAdapter extends BaseAdapter
+ implements AddressResolvingListener, ResolutionResolvingListener {
private final ArrayList<String> mItems;
private int mLocationIndex;
+ private int mWidthIndex = -1;
+ private int mHeightIndex = -1;
public DetailsAdapter(MediaDetails details) {
Context context = mActivity.getAndroidContext();
@@ -123,6 +128,8 @@ public class DialogDetailsView implements DetailsViewContainer {
}
private void setDetails(Context context, MediaDetails details) {
+ boolean resolutionIsValid = true;
+ String path = null;
for (Entry<Integer, Object> detail : details) {
String value;
switch (detail.getKey()) {
@@ -170,6 +177,26 @@ public class DialogDetailsView implements DetailsViewContainer {
}
break;
}
+ case MediaDetails.INDEX_WIDTH:
+ mWidthIndex = mItems.size();
+ value = detail.getValue().toString();
+ if (value.equalsIgnoreCase("0")) {
+ value = context.getString(R.string.unknown);
+ resolutionIsValid = false;
+ }
+ break;
+ case MediaDetails.INDEX_HEIGHT: {
+ mHeightIndex = mItems.size();
+ value = detail.getValue().toString();
+ if (value.equalsIgnoreCase("0")) {
+ value = context.getString(R.string.unknown);
+ resolutionIsValid = false;
+ }
+ break;
+ }
+ case MediaDetails.INDEX_PATH:
+ // Get the path and then fall through to the default case
+ path = detail.getValue().toString();
default: {
Object valueObj = detail.getValue();
// This shouldn't happen, log its key to help us diagnose the problem.
@@ -189,6 +216,9 @@ public class DialogDetailsView implements DetailsViewContainer {
context, key), value);
}
mItems.add(value);
+ if (!resolutionIsValid) {
+ DetailsHelper.resolveResolution(path, this);
+ }
}
}
@@ -235,6 +265,20 @@ public class DialogDetailsView implements DetailsViewContainer {
mItems.set(mLocationIndex, address);
notifyDataSetChanged();
}
+
+ @Override
+ public void onResolutionAvailable(int width, int height) {
+ if (width == 0 || height == 0) return;
+ // Update the resolution with the new width and height
+ Context context = mActivity.getAndroidContext();
+ String widthString = String.format("%s: %d", DetailsHelper.getDetailsName(
+ context, MediaDetails.INDEX_WIDTH), width);
+ String heightString = String.format("%s: %d", DetailsHelper.getDetailsName(
+ context, MediaDetails.INDEX_HEIGHT), height);
+ mItems.set(mWidthIndex, String.valueOf(widthString));
+ mItems.set(mHeightIndex, String.valueOf(heightString));
+ notifyDataSetChanged();
+ }
}
@Override
diff --git a/src/com/android/gallery3d/ui/ExtTexture.java b/src/com/android/gallery3d/ui/ExtTexture.java
index eac504fe5..180a89dce 100644
--- a/src/com/android/gallery3d/ui/ExtTexture.java
+++ b/src/com/android/gallery3d/ui/ExtTexture.java
@@ -16,49 +16,21 @@
package com.android.gallery3d.ui;
-import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
// ExtTexture is a texture whose content comes from a external texture.
// Before drawing, setSize() should be called.
public class ExtTexture extends BasicTexture {
- private static int[] sTextureId = new int[1];
- private static float[] sCropRect = new float[4];
private int mTarget;
public ExtTexture(int target) {
- GLId.glGenTextures(1, sTextureId, 0);
- mId = sTextureId[0];
+ GLId glId = GLCanvas.getGLId();
+ mId = glId.generateTexture();
mTarget = target;
}
private void uploadToCanvas(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
-
- int width = getWidth();
- int height = getHeight();
- // Define a vertically flipped crop rectangle for OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = 0;
- sCropRect[1] = height;
- sCropRect[2] = width;
- sCropRect[3] = -height;
-
- // Set texture parameters.
- gl.glBindTexture(mTarget, mId);
- gl.glTexParameterfv(mTarget,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(mTarget,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(mTarget,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(mTarget,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(mTarget,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
-
+ canvas.setTextureParameters(this);
setAssociatedCanvas(canvas);
mState = STATE_LOADED;
}
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index 6f8baef7e..1dbee5dd9 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -16,8 +16,11 @@
package com.android.gallery3d.ui;
+import android.graphics.Bitmap;
import android.graphics.RectF;
+import com.android.gallery3d.common.ApiHelper;
+
import javax.microedition.khronos.opengles.GL11;
//
@@ -26,107 +29,236 @@ import javax.microedition.khronos.opengles.GL11;
// When a rectangle is specified in this interface, it means the region
// [x, x+width) * [y, y+height)
//
-public interface GLCanvas {
+public abstract class GLCanvas {
+ public enum Blending {
+ Additive, Mix,
+ }
+
+ private static GLCanvas sInstance = instantiateCanvas();
+ private static GLId sGLId = instantiateGLId();
+
+ public static GLId getGLId() {
+ return sGLId;
+ }
+
+ public static GLCanvas getInstance() {
+ return sInstance;
+ }
+
+ private static GLId instantiateGLId() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? (GLES20Canvas) sInstance : new GLIdImpl();
+ }
+
+ private static GLCanvas instantiateCanvas() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? new GLES20Canvas() : new GLCanvasImpl();
+ }
+
+ public static int getEGLContextClientVersion() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? 2 : 1;
+ }
+
+ public abstract void initialize(GL11 gl);
+
// Tells GLCanvas the size of the underlying GL surface. This should be
// called before first drawing and when the size of GL surface is changed.
// This is called by GLRoot and should not be called by the clients
// who only want to draw on the GLCanvas. Both width and height must be
// nonnegative.
- public void setSize(int width, int height);
+ public abstract void setSize(int width, int height);
// Clear the drawing buffers. This should only be used by GLRoot.
- public void clearBuffer();
- public void clearBuffer(float[] argb);
+ public abstract void clearBuffer();
+
+ public abstract void clearBuffer(float[] argb);
// Sets and gets the current alpha, alpha must be in [0, 1].
- public void setAlpha(float alpha);
- public float getAlpha();
+ public abstract void setAlpha(float alpha);
+
+ public abstract float getAlpha();
// (current alpha) = (current alpha) * alpha
- public void multiplyAlpha(float alpha);
+ public abstract void multiplyAlpha(float alpha);
// Change the current transform matrix.
- public void translate(float x, float y, float z);
- public void translate(float x, float y);
- public void scale(float sx, float sy, float sz);
- public void rotate(float angle, float x, float y, float z);
- public void multiplyMatrix(float[] mMatrix, int offset);
+ public abstract void translate(float x, float y, float z);
+
+ public abstract void translate(float x, float y);
+
+ public abstract void scale(float sx, float sy, float sz);
+
+ public abstract void rotate(float angle, float x, float y, float z);
+
+ public abstract void multiplyMatrix(float[] mMatrix, int offset);
// Pushes the configuration state (matrix, and alpha) onto
// a private stack.
- public void save();
+ public abstract void save();
// Same as save(), but only save those specified in saveFlags.
- public void save(int saveFlags);
+ public abstract void save(int saveFlags);
public static final int SAVE_FLAG_ALL = 0xFFFFFFFF;
public static final int SAVE_FLAG_ALPHA = 0x01;
public static final int SAVE_FLAG_MATRIX = 0x02;
+ public static final int SAVE_FLAG_BLEND = 0x04;
// Pops from the top of the stack as current configuration state (matrix,
// alpha, and clip). This call balances a previous call to save(), and is
// used to remove all modifications to the configuration state since the
// last save call.
- public void restore();
+ public abstract void restore();
// Draws a line using the specified paint from (x1, y1) to (x2, y2).
// (Both end points are included).
- public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
+ public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
// Draws a rectangle using the specified paint from (x1, y1) to (x2, y2).
// (Both end points are included).
- public void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
+ public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
// Fills the specified rectangle with the specified color.
- public void fillRect(float x, float y, float width, float height, int color);
+ public abstract void fillRect(float x, float y, float width, float height, int color);
// Draws a texture to the specified rectangle.
- public void drawTexture(
+ public abstract void drawTexture(
BasicTexture texture, int x, int y, int width, int height);
- public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
+
+ public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
int uvBuffer, int indexBuffer, int indexCount);
// Draws the source rectangle part of the texture to the target rectangle.
- public void drawTexture(BasicTexture texture, RectF source, RectF target);
+ public abstract void drawTexture(BasicTexture texture, RectF source, RectF target);
// Draw a texture with a specified texture transform.
- public void drawTexture(BasicTexture texture, float[] mTextureTransform,
+ public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform,
int x, int y, int w, int h);
// Draw two textures to the specified rectangle. The actual texture used is
// from * (1 - ratio) + to * ratio
// The two textures must have the same size.
- public void drawMixed(BasicTexture from, int toColor,
+ public abstract void drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int w, int h);
// Draw a region of a texture and a specified color to the specified
// rectangle. The actual color used is from * (1 - ratio) + to * ratio.
// The region of the texture is defined by parameter "src". The target
// rectangle is specified by parameter "target".
- public void drawMixed(BasicTexture from, int toColor,
+ public abstract void drawMixed(BasicTexture from, int toColor,
float ratio, RectF src, RectF target);
- // Gets the underlying GL instance. This is used only when direct access to
- // GL is needed.
- public GL11 getGLInstance();
-
// Unloads the specified texture from the canvas. The resource allocated
// to draw the texture will be released. The specified texture will return
// to the unloaded state. This function should be called only from
// BasicTexture or its descendant
- public boolean unloadTexture(BasicTexture texture);
+ public abstract boolean unloadTexture(BasicTexture texture);
// Delete the specified buffer object, similar to unloadTexture.
- public void deleteBuffer(int bufferId);
+ public abstract void deleteBuffer(int bufferId);
// Delete the textures and buffers in GL side. This function should only be
// called in the GL thread.
- public void deleteRecycledResources();
+ public abstract void deleteRecycledResources();
// Dump statistics information and clear the counters. For debug only.
- public void dumpStatisticsAndClear();
+ public abstract void dumpStatisticsAndClear();
+
+ public abstract void beginRenderTarget(RawTexture texture);
+
+ public abstract void endRenderTarget();
+
+ /**
+ * Sets texture parameters to use GL_CLAMP_TO_EDGE for both
+ * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be
+ * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER.
+ * bindTexture() must be called prior to this.
+ *
+ * @param texture The texture to set parameters on.
+ */
+ public abstract void setTextureParameters(BasicTexture texture);
+
+ /**
+ * Initializes the texture to a size by calling texImage2D on it.
+ *
+ * @param texture The texture to initialize the size.
+ * @param format The texture format (e.g. GL_RGBA)
+ * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
+ */
+ public abstract void initializeTextureSize(BasicTexture texture, int format, int type);
+
+ /**
+ * Initializes the texture to a size by calling texImage2D on it.
+ *
+ * @param texture The texture to initialize the size.
+ * @param bitmap The bitmap to initialize the bitmap with.
+ */
+ public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap);
+
+ /**
+ * Calls glTexSubImage2D to upload a bitmap to the texture.
+ *
+ * @param texture The target texture to write to.
+ * @param xOffset Specifies a texel offset in the x direction within the
+ * texture array.
+ * @param yOffset Specifies a texel offset in the y direction within the
+ * texture array.
+ * @param format The texture format (e.g. GL_RGBA)
+ * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
+ */
+ public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset,
+ Bitmap bitmap,
+ int format, int type);
+
+ /**
+ * Generates buffers and uploads the buffer data.
+ *
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
+ */
+ public abstract int uploadBuffer(java.nio.FloatBuffer buffer);
+
+ /**
+ * Generates buffers and uploads the element array buffer data.
+ *
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
+ */
+ public abstract int uploadBuffer(java.nio.ByteBuffer buffer);
+
+ /**
+ * Sets the blending algorithm if a texture is not opaque.
+ *
+ * @param blending Either mixing (overlay) or adding a texture.
+ */
+ public abstract void setBlending(Blending blending);
+
+ /**
+ * Enable stencil test
+ */
+ public abstract void enableStencil();
+
+ /**
+ * Disable stencil.
+ */
+ public abstract void disableStencil();
+
+ /**
+ * Clears the stencil so that a new stencil can be generated.
+ */
+ public abstract void clearStencilBuffer();
- public void beginRenderTarget(RawTexture texture);
+ /**
+ * Start/stop updating the stencil buffer.
+ *
+ * @param update True if the stencil should be updated, false otherwise.
+ */
+ public abstract void updateStencil(boolean update);
- public void endRenderTarget();
+ /**
+ * Changes how the stencil buffer is used.
+ *
+ * @param onlyOutside If true, only the area outside the stencil can be
+ * changed. If false, the area inside the stencil can be drawn to
+ * as well.
+ */
+ public abstract void drawOnlyOutsideStencil(boolean onlyOutside);
}
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index 45903b3cd..54c231c3b 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -16,13 +16,16 @@
package com.android.gallery3d.ui;
+import android.graphics.Bitmap;
import android.graphics.RectF;
import android.opengl.GLU;
+import android.opengl.GLUtils;
import android.opengl.Matrix;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.util.IntArray;
+import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
@@ -33,7 +36,7 @@ import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
-public class GLCanvasImpl implements GLCanvas {
+public class GLCanvasImpl extends GLCanvas {
@SuppressWarnings("unused")
private static final String TAG = "GLCanvasImp";
@@ -47,7 +50,7 @@ public class GLCanvasImpl implements GLCanvas {
0, 0, 1, 1, // used for drawing a line
0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
- private final GL11 mGL;
+ private GL11 mGL;
private final float mMatrixValues[] = new float[16];
private final float mTextureMatrixValues[] = new float[16];
@@ -60,7 +63,7 @@ public class GLCanvasImpl implements GLCanvas {
private int mBoxCoords;
- private final GLState mGLState;
+ private GLState mGLState;
private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
private float mAlpha;
@@ -76,8 +79,10 @@ public class GLCanvasImpl implements GLCanvas {
private int mScreenHeight;
private boolean mBlendEnabled = true;
private int mFrameBuffer[] = new int[1];
+ private static float[] sCropRect = new float[4];
private RawTexture mTargetTexture;
+ private Blending mBlending = Blending.Mix;
// Drawing statistics
int mCountDrawLine;
@@ -86,10 +91,7 @@ public class GLCanvasImpl implements GLCanvas {
int mCountTextureRect;
int mCountTextureOES;
- GLCanvasImpl(GL11 gl) {
- mGL = gl;
- mGLState = new GLState(gl);
- initialize();
+ GLCanvasImpl() {
}
@Override
@@ -141,16 +143,18 @@ public class GLCanvasImpl implements GLCanvas {
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
}
- private void initialize() {
- GL11 gl = mGL;
-
+ @Override
+ public void initialize(GL11 gl) {
+ mGL = gl;
+ mGLState = new GLState(gl);
// First create an nio buffer, then create a VBO from it.
int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
int[] name = new int[1];
- GLId.glGenBuffers(1, name, 0);
+ GLId glId = getGLId();
+ glId.glGenBuffers(1, name, 0);
mBoxCoords = name[0];
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
@@ -684,11 +688,6 @@ public class GLCanvasImpl implements GLCanvas {
}
@Override
- public GL11 getGLInstance() {
- return mGL;
- }
-
- @Override
public void clearBuffer(float[] argb) {
if(argb != null && argb.length == 4) {
mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]);
@@ -748,14 +747,15 @@ public class GLCanvasImpl implements GLCanvas {
public void deleteRecycledResources() {
synchronized (mUnboundTextures) {
IntArray ids = mUnboundTextures;
+ GLId glId = getGLId();
if (ids.size() > 0) {
- GLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
+ glId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
ids = mDeleteBuffers;
if (ids.size() > 0) {
- GLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
+ glId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
}
@@ -776,6 +776,11 @@ public class GLCanvasImpl implements GLCanvas {
config.mAlpha = -1;
}
+ if ((saveFlags & SAVE_FLAG_BLEND) != 0) {
+ config.mBlending = mBlending;
+ } else {
+ config.mBlending = null;
+ }
if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
@@ -811,6 +816,7 @@ public class GLCanvasImpl implements GLCanvas {
private static class ConfigState {
float mAlpha;
float mMatrix[] = new float[16];
+ Blending mBlending;
ConfigState mNextFree;
public void restore(GLCanvasImpl canvas) {
@@ -818,6 +824,9 @@ public class GLCanvasImpl implements GLCanvas {
if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
}
+ if (mBlending != null) {
+ canvas.setBlending(mBlending);
+ }
}
}
@@ -847,7 +856,8 @@ public class GLCanvasImpl implements GLCanvas {
GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL;
if (mTargetTexture == null && texture != null) {
- GLId.glGenBuffers(1, mFrameBuffer, 0);
+ GLId glId = getGLId();
+ glId.glGenBuffers(1, mFrameBuffer, 0);
gl11ep.glBindFramebufferOES(
GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]);
}
@@ -917,4 +927,110 @@ public class GLCanvasImpl implements GLCanvas {
throw new RuntimeException(msg + ":" + Integer.toHexString(status));
}
}
+
+ @Override
+ public void setTextureParameters(BasicTexture texture) {
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ // Define a vertically flipped crop rectangle for OES_draw_texture.
+ // The four values in sCropRect are: left, bottom, width, and
+ // height. Negative value of width or height means flip.
+ sCropRect[0] = 0;
+ sCropRect[1] = height;
+ sCropRect[2] = width;
+ sCropRect[3] = -height;
+
+ // Set texture parameters.
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
+ mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ }
+
+ @Override
+ public void initializeTextureSize(BasicTexture texture, int format, int type) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ int width = texture.getTextureWidth();
+ int height = texture.getTextureHeight();
+ mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ }
+
+ @Override
+ public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ }
+
+ @Override
+ public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
+ int format, int type) {
+ int target = texture.getTarget();
+ mGL.glBindTexture(target, texture.getId());
+ GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ }
+
+ @Override
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
+ }
+
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
+
+ private int uploadBuffer(Buffer buf, int elementSize) {
+ int[] bufferIds = new int[1];
+ GLId glId = getGLId();
+ glId.glGenBuffers(bufferIds.length, bufferIds, 0);
+ int bufferId = bufferIds[0];
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
+ mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
+ GL11.GL_STATIC_DRAW);
+ return bufferId;
+ }
+
+ @Override
+ public void setBlending(Blending blending) {
+ if (mBlending == blending) {
+ return;
+ }
+ Utils.assertTrue(blending == Blending.Additive || blending == Blending.Mix);
+ mBlending = blending;
+ int srcFunc = GL11.GL_ONE;
+ int dstFunc = (blending == Blending.Additive) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA;
+ mGL.glBlendFunc(srcFunc, dstFunc);
+ }
+
+ @Override
+ public void enableStencil() {
+ mGL.glEnable(GL11.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void disableStencil() {
+ mGL.glDisable(GL11.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void clearStencilBuffer() {
+ mGL.glClear(GL11.GL_STENCIL_BUFFER_BIT);
+ }
+
+ @Override
+ public void updateStencil(boolean update) {
+ int passOp = update ? GL11.GL_REPLACE : GL11.GL_KEEP;
+ mGL.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, passOp);
+ }
+
+ @Override
+ public void drawOnlyOutsideStencil(boolean onlyOutside) {
+ int func = onlyOutside ? GL11.GL_NOTEQUAL : GL11.GL_ALWAYS;
+ mGL.glStencilFunc(func, 1, 1);
+ }
}
diff --git a/src/com/android/gallery3d/ui/GLES20Canvas.java b/src/com/android/gallery3d/ui/GLES20Canvas.java
new file mode 100644
index 000000000..b720a773b
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLES20Canvas.java
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.util.IntArray;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+public class GLES20Canvas extends GLCanvas implements GLId {
+ // ************** Constants **********************
+ private static final String TAG = GLES20Canvas.class.getSimpleName();
+ private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
+ private static final float OPAQUE_ALPHA = 0.95f;
+
+ private static final int COORDS_PER_VERTEX = 2;
+ private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
+
+ private static final int COUNT_FILL_VERTEX = 4;
+ private static final int COUNT_LINE_VERTEX = 2;
+ private static final int COUNT_RECT_VERTEX = 4;
+ private static final int OFFSET_FILL_RECT = 0;
+ private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
+ private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
+
+ private static final float[] BOX_COORDINATES = {
+ 0, 0, // Fill rectangle
+ 1, 0,
+ 0, 1,
+ 1, 1,
+ 0, 0, // Draw line
+ 1, 1,
+ 0, 0, // Draw rectangle outline
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ };
+
+ private static final String POSITION_ATTRIBUTE = "aPosition";
+ private static final String COLOR_UNIFORM = "uColor";
+ private static final String MATRIX_UNIFORM = "uMatrix";
+ private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
+ private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
+ private static final String ALPHA_UNIFORM = "uAlpha";
+ private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
+
+ private static final String DRAW_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + "}\n";
+
+ private static final String DRAW_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "uniform vec4 " + COLOR_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = " + COLOR_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
+ + "}\n";
+
+ private static final String MESH_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
+ + "#extension GL_OES_EGL_image_external : require\n"
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final int INITIAL_RESTORE_STATE_SIZE = 8;
+ private static final int MATRIX_SIZE = 16;
+
+ // Keep track of restore state
+ private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
+ private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
+ private IntArray mSaveFlags = new IntArray();
+ private ArrayList<Blending> mBlendings = new ArrayList<Blending>();
+
+ private int mCurrentAlphaIndex = 0;
+ private int mCurrentMatrixIndex = 0;
+
+ // Viewport size
+ private int mWidth;
+ private int mHeight;
+
+ // Projection matrix
+ private float[] mProjectionMatrix = new float[MATRIX_SIZE];
+
+ // Screen size for when we aren't bound to a texture
+ private int mScreenWidth;
+ private int mScreenHeight;
+
+ // GL programs
+ private int mDrawProgram;
+ private int mTextureProgram;
+ private int mOesTextureProgram;
+ private int mMeshProgram;
+
+ // GL buffer containing BOX_COORDINATES
+ private int mBoxCoordinates;
+
+ // Handle indices -- common
+ private static final int INDEX_POSITION = 0;
+ private static final int INDEX_MATRIX = 1;
+
+ // Handle indices -- draw
+ private static final int INDEX_COLOR = 2;
+
+ // Handle indices -- texture
+ private static final int INDEX_TEXTURE_MATRIX = 2;
+ private static final int INDEX_TEXTURE_SAMPLER = 3;
+ private static final int INDEX_ALPHA = 4;
+
+ // Handle indices -- mesh
+ private static final int INDEX_TEXTURE_COORD = 2;
+
+ private abstract static class ShaderParameter {
+ public int handle;
+ protected final String mName;
+
+ public ShaderParameter(String name) {
+ mName = name;
+ }
+
+ public abstract void loadHandle(int program);
+ }
+
+ private static class UniformShaderParameter extends ShaderParameter {
+ public UniformShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetUniformLocation(program, mName);
+ checkError();
+ }
+ }
+
+ private static class AttributeShaderParameter extends ShaderParameter {
+ public AttributeShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetAttribLocation(program, mName);
+ checkError();
+ }
+ }
+
+ ShaderParameter[] mDrawParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
+ };
+ ShaderParameter[] mTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mOesTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mMeshParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+
+ private final IntArray mUnboundTextures = new IntArray();
+ private final IntArray mDeleteBuffers = new IntArray();
+
+ // Keep track of statistics for debugging
+ private int mCountDrawMesh = 0;
+ private int mCountTextureRect = 0;
+ private int mCountFillRect = 0;
+ private int mCountDrawLine = 0;
+
+ private int mNextTextureId = 1;
+
+ // Buffer for framebuffer IDs -- we keep track so we can switch the attached
+ // texture.
+ private int[] mFrameBuffer = new int[1];
+
+ // Bound textures.
+ private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
+
+ // Temporary variables used within calculations
+ private final float[] mTempMatrix = new float[32];
+ private final float[] mTempColor = new float[4];
+ private final RectF mTempSourceRect = new RectF();
+ private final RectF mTempTargetRect = new RectF();
+ private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
+ private final int[] mTempIntArray = new int[1];
+
+ public GLES20Canvas() {
+ Matrix.setIdentityM(mTempTextureMatrix, 0);
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ mAlphas[mCurrentAlphaIndex] = 1f;
+ mTargetTextures.add(null);
+ }
+
+ @Override
+ public void initialize(GL11 gl) {
+ FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
+ mBoxCoordinates = uploadBuffer(boxBuffer);
+
+ int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
+ int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
+ int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
+ int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
+ int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
+ int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
+ OES_TEXTURE_FRAGMENT_SHADER);
+
+ mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
+ mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
+ mTextureParameters);
+ mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
+ mOesTextureParameters);
+ mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
+
+ mBlendings.clear();
+ mBlendings.add(null);
+ setBlending(Blending.Mix);
+ }
+
+ private static FloatBuffer createBuffer(float[] values) {
+ // First create an nio buffer, then create a VBO from it.
+ int size = values.length * FLOAT_SIZE;
+ FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ buffer.put(values, 0, values.length).position(0);
+ return buffer;
+ }
+
+ private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
+ int program = GLES20.glCreateProgram();
+ checkError();
+ if (program == 0) {
+ throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
+ }
+ GLES20.glAttachShader(program, vertexShader);
+ checkError();
+ GLES20.glAttachShader(program, fragmentShader);
+ checkError();
+ GLES20.glLinkProgram(program);
+ checkError();
+ int[] mLinkStatus = mTempIntArray;
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
+ if (mLinkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ for (int i = 0; i < params.length; i++) {
+ params[i].loadHandle(program);
+ }
+ return program;
+ }
+
+ private static int loadShader(int type, String shaderCode) {
+ // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
+ // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
+ int shader = GLES20.glCreateShader(type);
+
+ // add the source code to the shader and compile it
+ GLES20.glShaderSource(shader, shaderCode);
+ checkError();
+ GLES20.glCompileShader(shader);
+ checkError();
+
+ return shader;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ GLES20.glViewport(0, 0, mWidth, mHeight);
+ checkError();
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
+ if (getTargetTexture() == null) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
+ }
+ }
+
+ @Override
+ public void clearBuffer() {
+ GLES20.glClearColor(0f, 0f, 0f, 1f);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public void clearBuffer(float[] argb) {
+ GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public float getAlpha() {
+ return mAlphas[mCurrentAlphaIndex];
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ mAlphas[mCurrentAlphaIndex] = alpha;
+ }
+
+ @Override
+ public void multiplyAlpha(float alpha) {
+ setAlpha(getAlpha() * alpha);
+ }
+
+ @Override
+ public void translate(float x, float y, float z) {
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
+ }
+
+ // This is a faster version of translate(x, y, z) because
+ // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
+ // (3) we unroll the loop
+ @Override
+ public void translate(float x, float y) {
+ int index = mCurrentMatrixIndex;
+ float[] m = mMatrices;
+ m[index + 12] += m[index + 0] * x + m[index + 4] * y;
+ m[index + 13] += m[index + 1] * x + m[index + 5] * y;
+ m[index + 14] += m[index + 2] * x + m[index + 6] * y;
+ m[index + 15] += m[index + 3] * x + m[index + 7] * y;
+ }
+
+ @Override
+ public void scale(float sx, float sy, float sz) {
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
+ }
+
+ @Override
+ public void rotate(float angle, float x, float y, float z) {
+ if (angle == 0f) {
+ return;
+ }
+ float[] temp = mTempMatrix;
+ Matrix.setRotateM(temp, 0, angle, x, y, z);
+ float[] matrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
+ System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
+ }
+
+ @Override
+ public void multiplyMatrix(float[] matrix, int offset) {
+ float[] temp = mTempMatrix;
+ float[] currentMatrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
+ System.arraycopy(temp, 0, currentMatrix, index, 16);
+ }
+
+ @Override
+ public void save() {
+ save(SAVE_FLAG_ALL);
+ }
+
+ @Override
+ public void save(int saveFlags) {
+ boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (saveAlpha) {
+ float currentAlpha = getAlpha();
+ mCurrentAlphaIndex++;
+ if (mAlphas.length <= mCurrentAlphaIndex) {
+ mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
+ }
+ mAlphas[mCurrentAlphaIndex] = currentAlpha;
+ }
+ boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (saveMatrix) {
+ int currentIndex = mCurrentMatrixIndex;
+ mCurrentMatrixIndex += MATRIX_SIZE;
+ if (mMatrices.length <= mCurrentMatrixIndex) {
+ mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
+ }
+ System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
+ }
+ boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (saveBlending) {
+ mBlendings.add(mBlendings.get(mBlendings.size() - 1));
+ }
+ mSaveFlags.add(saveFlags);
+ }
+
+ @Override
+ public void restore() {
+ int restoreFlags = mSaveFlags.removeLast();
+ boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (restoreAlpha) {
+ mCurrentAlphaIndex--;
+ }
+ boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (restoreMatrix) {
+ mCurrentMatrixIndex -= MATRIX_SIZE;
+ }
+ boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (restoreBlending) {
+ setBlending(mBlendings.get(mBlendings.size() - 2));
+ mBlendings.remove(mBlendings.size() - 1);
+ }
+ }
+
+ @Override
+ public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
+ draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
+ paint);
+ mCountDrawLine++;
+ }
+
+ @Override
+ public void drawRect(float x, float y, float width, float height, GLPaint paint) {
+ draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
+ mCountDrawLine++;
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ GLPaint paint) {
+ draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ int color, float lineWidth) {
+ prepareDraw(offset, color, lineWidth);
+ draw(mDrawParameters, type, count, x, y, width, height);
+ }
+
+ private void prepareDraw(int offset, int color, float lineWidth) {
+ GLES20.glUseProgram(mDrawProgram);
+ checkError();
+ if (lineWidth > 0) {
+ GLES20.glLineWidth(lineWidth);
+ checkError();
+ }
+ float[] colorArray = getColor(color);
+ boolean blendingEnabled = (colorArray[3] < 1f);
+ enableBlending(blendingEnabled);
+ if (blendingEnabled) {
+ GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
+ checkError();
+ }
+
+ GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
+ setPosition(mDrawParameters, offset);
+ checkError();
+ }
+
+ private float[] getColor(int color) {
+ float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
+ float red = ((color >>> 16) & 0xFF) / 255f * alpha;
+ float green = ((color >>> 8) & 0xFF) / 255f * alpha;
+ float blue = (color & 0xFF) / 255f * alpha;
+ mTempColor[0] = red;
+ mTempColor[1] = green;
+ mTempColor[2] = blue;
+ mTempColor[3] = alpha;
+ return mTempColor;
+ }
+
+ private void enableBlending(boolean enableBlending) {
+ if (enableBlending) {
+ GLES20.glEnable(GLES20.GL_BLEND);
+ checkError();
+ } else {
+ GLES20.glDisable(GLES20.GL_BLEND);
+ checkError();
+ }
+ }
+
+ private void setPosition(ShaderParameter[] params, int offset) {
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
+ checkError();
+ GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
+ GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
+ checkError();
+ }
+
+ private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
+ float height) {
+ setMatrix(params, x, y, width, height);
+ int positionHandle = params[INDEX_POSITION].handle;
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDrawArrays(type, 0, count);
+ checkError();
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ }
+
+ private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
+ Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
+ Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
+ Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
+ GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
+ checkError();
+ }
+
+ @Override
+ public void fillRect(float x, float y, float width, float height, int color) {
+ draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
+ color, 0f);
+ mCountFillRect++;
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + width, y + height);
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
+ int left = 0;
+ int top = 0;
+ int right = texture.getWidth();
+ int bottom = texture.getHeight();
+ if (texture.hasBorder()) {
+ left = 1;
+ top = 1;
+ right -= 1;
+ bottom -= 1;
+ }
+ outRect.set(left, top, right, bottom);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ mTempSourceRect.set(source);
+ mTempTargetRect.set(target);
+
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
+ int h) {
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawTextureRect(texture, textureTransform, mTempTargetRect);
+ }
+
+ private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
+ setTextureMatrix(source);
+ drawTextureRect(texture, mTempTextureMatrix, target);
+ }
+
+ private void setTextureMatrix(RectF source) {
+ mTempTextureMatrix[0] = source.width();
+ mTempTextureMatrix[5] = source.height();
+ mTempTextureMatrix[12] = source.left;
+ mTempTextureMatrix[13] = source.top;
+ }
+
+ // This function changes the source coordinate to the texture coordinates.
+ // It also clips the source and target coordinates if it is beyond the
+ // bound of the texture.
+ private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ int texWidth = texture.getTextureWidth();
+ int texHeight = texture.getTextureHeight();
+ // Convert to texture coordinates
+ source.left /= texWidth;
+ source.right /= texWidth;
+ source.top /= texHeight;
+ source.bottom /= texHeight;
+
+ // Clip if the rendering range is beyond the bound of the texture.
+ float xBound = (float) width / texWidth;
+ if (source.right > xBound) {
+ target.right = target.left + target.width() * (xBound - source.left) / source.width();
+ source.right = xBound;
+ }
+ float yBound = (float) height / texHeight;
+ if (source.bottom > yBound) {
+ target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
+ source.bottom = yBound;
+ }
+ }
+
+ private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
+ ShaderParameter[] params = prepareTexture(texture);
+ setPosition(params, OFFSET_FILL_RECT);
+ GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
+ checkError();
+ draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
+ target.width(), target.height());
+ mCountTextureRect++;
+ }
+
+ private ShaderParameter[] prepareTexture(BasicTexture texture) {
+ ShaderParameter[] params;
+ int program;
+ if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
+ params = mTextureParameters;
+ program = mTextureProgram;
+ } else {
+ params = mOesTextureParameters;
+ program = mOesTextureProgram;
+ }
+ prepareTexture(texture, program, params);
+ return params;
+ }
+
+ private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
+ GLES20.glUseProgram(program);
+ checkError();
+ enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ checkError();
+ texture.onBind(this);
+ GLES20.glBindTexture(texture.getTarget(), texture.getId());
+ checkError();
+ GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
+ checkError();
+ GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
+ checkError();
+ }
+
+ @Override
+ public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
+ int indexBuffer, int indexCount) {
+ prepareTexture(texture, mMeshProgram, mMeshParameters);
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
+ checkError();
+ int positionHandle = mMeshParameters[INDEX_POSITION].handle;
+ GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
+ VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
+ checkError();
+ int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
+ GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
+ false, VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glEnableVertexAttribArray(texCoordHandle);
+ checkError();
+
+ setMatrix(mMeshParameters, x, y, 1, 1);
+ GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
+ checkError();
+
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDisableVertexAttribArray(texCoordHandle);
+ checkError();
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
+ checkError();
+ mCountDrawMesh++;
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ save(SAVE_FLAG_ALPHA);
+
+ float currentAlpha = getAlpha();
+ float cappedRatio = Math.min(1f, Math.max(0f, ratio));
+
+ float textureAlpha = (1f - cappedRatio) * currentAlpha;
+ setAlpha(textureAlpha);
+ drawTexture(texture, source, target);
+
+ float colorAlpha = cappedRatio * currentAlpha;
+ setAlpha(colorAlpha);
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+
+ restore();
+ }
+
+ @Override
+ public boolean unloadTexture(BasicTexture texture) {
+ boolean unload = texture.isLoaded();
+ if (unload) {
+ synchronized (mUnboundTextures) {
+ mUnboundTextures.add(texture.getId());
+ }
+ }
+ return unload;
+ }
+
+ @Override
+ public void deleteBuffer(int bufferId) {
+ synchronized (mUnboundTextures) {
+ mDeleteBuffers.add(bufferId);
+ }
+ }
+
+ @Override
+ public void deleteRecycledResources() {
+ synchronized (mUnboundTextures) {
+ IntArray ids = mUnboundTextures;
+ if (mUnboundTextures.size() > 0) {
+ glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+
+ ids = mDeleteBuffers;
+ if (ids.size() > 0) {
+ glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+ }
+ }
+
+ @Override
+ public void dumpStatisticsAndClear() {
+ String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
+ mCountTextureRect, mCountFillRect, mCountDrawLine);
+ mCountDrawMesh = 0;
+ mCountTextureRect = 0;
+ mCountFillRect = 0;
+ mCountDrawLine = 0;
+ Log.d(TAG, line);
+ }
+
+ @Override
+ public void endRenderTarget() {
+ RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
+ RawTexture texture = getTargetTexture();
+ setRenderTarget(oldTexture, texture);
+ restore(); // restore matrix and alpha
+ }
+
+ @Override
+ public void beginRenderTarget(RawTexture texture) {
+ save(); // save matrix and alpha and blending
+ RawTexture oldTexture = getTargetTexture();
+ mTargetTextures.add(texture);
+ setRenderTarget(oldTexture, texture);
+ }
+
+ private RawTexture getTargetTexture() {
+ return mTargetTextures.get(mTargetTextures.size() - 1);
+ }
+
+ private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
+ if (oldTexture == null && texture != null) {
+ GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
+ checkError();
+ } else if (oldTexture != null && texture == null) {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ checkError();
+ GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ }
+
+ if (texture == null) {
+ setSize(mScreenWidth, mScreenHeight);
+ } else {
+ setSize(texture.getWidth(), texture.getHeight());
+
+ if (!texture.isLoaded()) {
+ texture.prepare(this);
+ }
+
+ GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
+ texture.getTarget(), texture.getId(), 0);
+ checkError();
+
+ checkFramebufferStatus();
+ }
+ }
+
+ private static void checkFramebufferStatus() {
+ int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
+ if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
+ String msg = "";
+ switch (status) {
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
+ msg = "GL_FRAMEBUFFER_UNSUPPORTED";
+ break;
+ }
+ throw new RuntimeException(msg + ":" + Integer.toHexString(status));
+ }
+ }
+
+ @Override
+ public void setTextureParameters(BasicTexture texture) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ }
+
+ @Override
+ public void initializeTextureSize(BasicTexture texture, int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ int width = texture.getTextureWidth();
+ int height = texture.getTextureHeight();
+ GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ }
+
+ @Override
+ public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ }
+
+ @Override
+ public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
+ int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ }
+
+ @Override
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, FLOAT_SIZE);
+ }
+
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
+
+ private int uploadBuffer(Buffer buffer, int elementSize) {
+ glGenBuffers(1, mTempIntArray, 0);
+ checkError();
+ int bufferId = mTempIntArray[0];
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
+ checkError();
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
+ GLES20.GL_STATIC_DRAW);
+ checkError();
+ return bufferId;
+ }
+
+ @Override
+ public void setBlending(Blending blending) {
+ Blending currentBlending = mBlendings.get(mBlendings.size() - 1);
+ if (currentBlending == blending) {
+ return; // nothing to change
+ }
+ mBlendings.set(mBlendings.size() - 1, blending);
+ int srcFunc = GLES20.GL_ONE;
+ int dstFunc;
+ switch (blending) {
+ case Additive:
+ dstFunc = GLES20.GL_ONE;
+ break;
+ case Mix:
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ default:
+ Utils.fail("Unknown blend: " + blending);
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ GLES20.glBlendFunc(srcFunc, dstFunc);
+ checkError();
+ }
+
+ @Override
+ public int generateTexture() {
+ // Can use anything as a lock. No need to create a new object.
+ synchronized (mTempIntArray) {
+ return mNextTextureId++;
+ }
+ }
+
+ @Override
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ GLES20.glGenBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
+ GLES20.glDeleteTextures(n, textures, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
+ GLES20.glDeleteBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
+ GLES20.glDeleteFramebuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void enableStencil() {
+ GLES20.glEnable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void disableStencil() {
+ GLES20.glDisable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void clearStencilBuffer() {
+ GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);
+ }
+
+ @Override
+ public void updateStencil(boolean update) {
+ int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP;
+ GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp);
+ }
+
+ @Override
+ public void drawOnlyOutsideStencil(boolean onlyOutside) {
+ int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS;
+ GLES20.glStencilFunc(func, 1, 1);
+ }
+
+ private static void checkError() {
+ int error = GLES20.glGetError();
+ if (error != 0) {
+ Throwable t = new Throwable();
+ Log.e(TAG, "GL error: " + error, t);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void printMatrix(String message, float[] m, int offset) {
+ StringBuilder b = new StringBuilder(message);
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ b.append(' ');
+ if (i % 4 == 0) {
+ b.append('\n');
+ }
+ b.append(m[offset + i]);
+ }
+ Log.v(TAG, b.toString());
+ }
+
+}
diff --git a/src/com/android/gallery3d/ui/GLId.java b/src/com/android/gallery3d/ui/GLId.java
index 689cf192e..04977c337 100644
--- a/src/com/android/gallery3d/ui/GLId.java
+++ b/src/com/android/gallery3d/ui/GLId.java
@@ -20,31 +20,14 @@ import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
// This mimics corresponding GL functions.
-public class GLId {
- static int sNextId = 1;
+public interface GLId {
+ public int generateTexture();
- public synchronized static void glGenTextures(int n, int[] textures, int offset) {
- while (n-- > 0) {
- textures[offset + n] = sNextId++;
- }
- }
+ public void glGenBuffers(int n, int[] buffers, int offset);
- public synchronized static void glGenBuffers(int n, int[] buffers, int offset) {
- while (n-- > 0) {
- buffers[offset + n] = sNextId++;
- }
- }
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset);
- public synchronized static void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
- gl.glDeleteTextures(n, textures, offset);
- }
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset);
- public synchronized static void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
- gl.glDeleteBuffers(n, buffers, offset);
- }
-
- public synchronized static void glDeleteFramebuffers(
- GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
- gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
- }
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset);
}
diff --git a/src/com/android/gallery3d/ui/GLIdImpl.java b/src/com/android/gallery3d/ui/GLIdImpl.java
new file mode 100644
index 000000000..7a0232bf0
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLIdImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.gallery3d.ui;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+/**
+ * Open GL ES 1.1 implementation for generating and destroying texture IDs and
+ * buffer IDs
+ */
+public class GLIdImpl implements GLId {
+ private static int sNextId = 1;
+ // Mutex for sNextId
+ private static Object sLock = new Object();
+
+ @Override
+ public int generateTexture() {
+ synchronized (sLock) {
+ return sNextId++;
+ }
+ }
+
+ @Override
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ while (n-- > 0) {
+ buffers[offset + n] = sNextId++;
+ }
+ }
+ }
+
+ @Override
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
+ synchronized (sLock) {
+ gl.glDeleteTextures(n, textures, offset);
+ }
+ }
+
+ @Override
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ gl.glDeleteBuffers(n, buffers, offset);
+ }
+ }
+
+ @Override
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
+ synchronized (sLock) {
+ gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
+ }
+ }
+
+
+}
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index b7c48bf2e..6b76999b6 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -117,6 +117,7 @@ public class GLRootView extends GLSurfaceView
super(context, attrs);
mFlags |= FLAG_INITIALIZED;
setBackgroundDrawable(null);
+ setEGLContextClientVersion(GLCanvas.getEGLContextClientVersion());
setEGLConfigChooser(mEglConfigChooser);
setRenderer(this);
if (ApiHelper.USE_888_PIXEL_FORMAT) {
@@ -283,7 +284,8 @@ public class GLRootView extends GLSurfaceView
mRenderLock.lock();
try {
mGL = gl;
- mCanvas = new GLCanvasImpl(gl);
+ mCanvas = GLCanvas.getInstance();
+ mCanvas.initialize(gl);
BasicTexture.invalidateAllTextures();
} finally {
mRenderLock.unlock();
diff --git a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
index deeb3b76d..f57a312cb 100644
--- a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
+++ b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
@@ -49,12 +49,35 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGL10.EGL_NONE
};
+ private final int mConfig2Spec565[] = new int[] {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
+ private final int mConfig2Spec888[] = new int[] {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] numConfig = new int[1];
- int mConfigSpec[] = ApiHelper.USE_888_PIXEL_FORMAT
- ? mConfigSpec888 : mConfigSpec565;
- if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, numConfig)) {
+
+ int configSpec[];
+ if (GLCanvas.getEGLContextClientVersion() == 2) {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfig2Spec888 : mConfig2Spec565;
+ } else {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfigSpec888 : mConfigSpec565;
+ }
+ if (!egl.eglChooseConfig(display, configSpec, null, 0, numConfig)) {
throw new RuntimeException("eglChooseConfig failed");
}
@@ -64,7 +87,7 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGLConfig[] configs = new EGLConfig[numConfig[0]];
if (!egl.eglChooseConfig(display,
- mConfigSpec, configs, configs.length, numConfig)) {
+ configSpec, configs, configs.length, numConfig)) {
throw new RuntimeException();
}
diff --git a/src/com/android/gallery3d/ui/GestureRecognizer.java b/src/com/android/gallery3d/ui/GestureRecognizer.java
index e4e0c49f5..1e5250b9b 100644
--- a/src/com/android/gallery3d/ui/GestureRecognizer.java
+++ b/src/com/android/gallery3d/ui/GestureRecognizer.java
@@ -32,7 +32,7 @@ public class GestureRecognizer {
boolean onSingleTapUp(float x, float y);
boolean onDoubleTap(float x, float y);
boolean onScroll(float dx, float dy, float totalX, float totalY);
- boolean onFling(float velocityX, float velocityY);
+ boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
boolean onScaleBegin(float focusX, float focusY);
boolean onScale(float focusX, float focusY, float scale);
void onScaleEnd();
@@ -94,7 +94,7 @@ public class GestureRecognizer {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
- return mListener.onFling(velocityX, velocityY);
+ return mListener.onFling(e1, e2, velocityX, velocityY);
}
}
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index f432333ce..a9eeaf89c 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -26,12 +26,12 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
-import android.view.Menu;
-import android.view.MenuItem;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
-import com.android.gallery3d.app.CropImage;
+import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
@@ -62,6 +62,7 @@ public class MenuExecutor {
private Future<?> mTask;
// wait the operation to finish when we want to stop it.
private boolean mWaitOnStop;
+ private boolean mPaused;
private final AbstractGalleryActivity mActivity;
private final SelectionManager mSelectionManager;
@@ -113,7 +114,7 @@ public class MenuExecutor {
break;
}
case MSG_TASK_UPDATE: {
- if (mDialog != null) mDialog.setProgress(message.arg1);
+ if (mDialog != null && !mPaused) mDialog.setProgress(message.arg1);
if (message.obj != null) {
ProgressListener listener = (ProgressListener) message.obj;
listener.onProgressUpdate(message.arg1);
@@ -133,13 +134,23 @@ public class MenuExecutor {
if (mTask != null) {
if (!mWaitOnStop) mTask.cancel();
mTask.waitDone();
- mDialog.dismiss();
+ if (mDialog != null && mDialog.isShowing()) mDialog.dismiss();
mDialog = null;
mTask = null;
}
}
+ public void resume() {
+ mPaused = false;
+ if (mDialog != null) mDialog.show();
+ }
+
public void pause() {
+ mPaused = true;
+ if (mDialog != null && mDialog.isShowing()) mDialog.hide();
+ }
+
+ public void destroy() {
stopTaskAndDismissDialog();
}
@@ -161,6 +172,7 @@ public class MenuExecutor {
boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
+ boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0;
boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
@@ -174,6 +186,7 @@ public class MenuExecutor {
setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate);
setMenuItemVisible(menu, R.id.action_crop, supportCrop);
setMenuItemVisible(menu, R.id.action_trim, supportTrim);
+ setMenuItemVisible(menu, R.id.action_mute, supportMute);
// Hide panorama until call to updateMenuForPanorama corrects it
setMenuItemVisible(menu, R.id.action_share_panorama, false);
setMenuItemVisible(menu, R.id.action_share, supportShare);
@@ -332,7 +345,7 @@ public class MenuExecutor {
mDialog.show();
}
MediaOperation operation = new MediaOperation(action, ids, listener);
- mTask = mActivity.getThreadPool().submit(operation, null);
+ mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null);
mWaitOnStop = waitOnStop;
}
diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java
index fa0e9cdc3..f5c614554 100644
--- a/src/com/android/gallery3d/ui/NinePatchTexture.java
+++ b/src/com/android/gallery3d/ui/NinePatchTexture.java
@@ -27,8 +27,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
-import javax.microedition.khronos.opengles.GL11;
-
// NinePatchTexture is a texture backed by a NinePatch resource.
//
// getPaddings() returns paddings specified in the NinePatch.
@@ -199,7 +197,9 @@ class NinePatchInstance {
private ByteBuffer mIndexBuffer;
// Names for buffer names: xy, uv, index.
- private int[] mBufferNames;
+ private int mXyBufferName = -1;
+ private int mUvBufferName;
+ private int mIndexBufferName;
private int mIdxCount;
@@ -396,24 +396,9 @@ class NinePatchInstance {
}
private void prepareBuffers(GLCanvas canvas) {
- mBufferNames = new int[3];
- GL11 gl = canvas.getGLInstance();
- GLId.glGenBuffers(3, mBufferNames, 0);
-
- gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[0]);
- gl.glBufferData(GL11.GL_ARRAY_BUFFER,
- mXyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
- mXyBuffer, GL11.GL_STATIC_DRAW);
-
- gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBufferNames[1]);
- gl.glBufferData(GL11.GL_ARRAY_BUFFER,
- mUvBuffer.capacity() * (Float.SIZE / Byte.SIZE),
- mUvBuffer, GL11.GL_STATIC_DRAW);
-
- gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mBufferNames[2]);
- gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER,
- mIndexBuffer.capacity(),
- mIndexBuffer, GL11.GL_STATIC_DRAW);
+ mXyBufferName = canvas.uploadBuffer(mXyBuffer);
+ mUvBufferName = canvas.uploadBuffer(mUvBuffer);
+ mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
// These buffers are never used again.
mXyBuffer = null;
@@ -422,19 +407,18 @@ class NinePatchInstance {
}
public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
- if (mBufferNames == null) {
+ if (mXyBufferName == -1) {
prepareBuffers(canvas);
}
- canvas.drawMesh(tex, x, y, mBufferNames[0], mBufferNames[1],
- mBufferNames[2], mIdxCount);
+ canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
}
public void recycle(GLCanvas canvas) {
- if (mBufferNames != null) {
- canvas.deleteBuffer(mBufferNames[0]);
- canvas.deleteBuffer(mBufferNames[1]);
- canvas.deleteBuffer(mBufferNames[2]);
- mBufferNames = null;
+ if (mXyBuffer == null) {
+ canvas.deleteBuffer(mXyBufferName);
+ canvas.deleteBuffer(mUvBufferName);
+ canvas.deleteBuffer(mIndexBufferName);
+ mXyBufferName = -1;
}
}
}
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 6dcae4ca4..58389e4ae 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -174,8 +174,9 @@ public class PhotoView extends GLView {
public static final int SCREEN_NAIL_MAX = 3;
// These are constants for the delete gesture.
- private static final int SWIPE_ESCAPE_VELOCITY = 2500; // dp/sec
- private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
+ private static final int SWIPE_ESCAPE_VELOCITY = 500; // dp/sec
+ private static final int MAX_DISMISS_VELOCITY = 2500; // dp/sec
+ private static final int SWIPE_ESCAPE_DISTANCE = 150; // dp
// The picture entries, the valid index is from -SCREEN_NAIL_MAX to
// SCREEN_NAIL_MAX.
@@ -1070,19 +1071,19 @@ public class PhotoView extends GLView {
}
@Override
- public boolean onFling(float velocityX, float velocityY) {
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIgnoreSwipingGesture) return true;
if (mModeChanged) return true;
if (swipeImages(velocityX, velocityY)) {
mIgnoreUpEvent = true;
} else {
- flingImages(velocityX, velocityY);
+ flingImages(velocityX, velocityY, Math.abs(e2.getY() - e1.getY()));
}
mHadFling = true;
return true;
}
- private boolean flingImages(float velocityX, float velocityY) {
+ private boolean flingImages(float velocityX, float velocityY, float dY) {
int vx = (int) (velocityX + 0.5f);
int vy = (int) (velocityY + 0.5f);
if (!mFilmMode) {
@@ -1099,11 +1100,13 @@ public class PhotoView extends GLView {
}
int maxVelocity = GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY);
int escapeVelocity = GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY);
+ int escapeDistance = GalleryUtils.dpToPixel(SWIPE_ESCAPE_DISTANCE);
int centerY = mPositionController.getPosition(mTouchBoxIndex)
.centerY();
boolean fastEnough = (Math.abs(vy) > escapeVelocity)
&& (Math.abs(vy) > Math.abs(vx))
- && ((vy > 0) == (centerY > getHeight() / 2));
+ && ((vy > 0) == (centerY > getHeight() / 2))
+ && dY >= escapeDistance;
if (fastEnough) {
vy = Math.min(vy, maxVelocity);
int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy);
@@ -1237,7 +1240,10 @@ public class PhotoView extends GLView {
if (mFilmMode) {
int xi = (int) (x + 0.5f);
int yi = (int) (y + 0.5f);
- mTouchBoxIndex = mPositionController.hitTest(xi, yi);
+ // We only care about being within the x bounds, necessary for
+ // handling very wide images which are otherwise very hard to fling
+ mTouchBoxIndex = mPositionController.hitTest(xi, getHeight() / 2);
+
if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) {
mTouchBoxIndex = Integer.MAX_VALUE;
} else {
diff --git a/src/com/android/gallery3d/ui/PopupList.java b/src/com/android/gallery3d/ui/PopupList.java
index 248f50b25..dd6269380 100644
--- a/src/com/android/gallery3d/ui/PopupList.java
+++ b/src/com/android/gallery3d/ui/PopupList.java
@@ -159,7 +159,7 @@ public class PopupList {
R.drawable.menu_dropdown_panel_holo_dark));
mContentList = new ListView(mContext, null,
- android.R.attr.dropDownListViewStyle);
+ com.actionbarsherlock.R.attr.dropDownListViewStyle);
mContentList.setAdapter(new ItemDataAdapter());
mContentList.setOnItemClickListener(mOnItemClickListener);
popup.setContentView(mContentList);
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index 6a4bcea87..9069d5da2 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -18,10 +18,10 @@ package com.android.gallery3d.ui;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Build;
import android.util.Log;
import android.widget.Scroller;
-import com.android.gallery3d.app.PhotoPage;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.ui.PhotoView.Size;
import com.android.gallery3d.util.GalleryUtils;
@@ -211,7 +211,11 @@ class PositionController {
public PositionController(Context context, Listener listener) {
mListener = listener;
mPageScroller = new FlingScroller();
- mFilmScroller = new Scroller(context, null, false);
+ if (Build.VERSION.SDK_INT >= 11) {
+ mFilmScroller = new Scroller(context, null, false);
+ } else {
+ mFilmScroller = new Scroller(context, null);
+ }
// Initialize the areas.
initPlatform();
diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java
index 4c0d9d365..53aef9edc 100644
--- a/src/com/android/gallery3d/ui/RawTexture.java
+++ b/src/com/android/gallery3d/ui/RawTexture.java
@@ -16,15 +16,13 @@
package com.android.gallery3d.ui;
+import android.opengl.GLES20;
+
import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
public class RawTexture extends BasicTexture {
private static final String TAG = "RawTexture";
- private final static int[] sTextureId = new int[1];
- private final static float[] sCropRect = new float[4];
-
private final boolean mOpaque;
public RawTexture(int width, int height, boolean opaque) {
@@ -38,36 +36,10 @@ public class RawTexture extends BasicTexture {
}
protected void prepare(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
-
- // Define a vertically flipped crop rectangle for
- // OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = 0;
- sCropRect[1] = mHeight;
- sCropRect[2] = mWidth;
- sCropRect[3] = -mHeight;
-
- // Upload the bitmap to a new texture.
- GLId.glGenTextures(1, sTextureId, 0);
- gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]);
- gl.glTexParameterfv(GL11.GL_TEXTURE_2D,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
-
- gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
- getTextureWidth(), getTextureHeight(),
- 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, null);
-
- mId = sTextureId[0];
+ GLId glId = GLCanvas.getGLId();
+ mId = glId.generateTexture();
+ canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
+ canvas.setTextureParameters(this);
mState = STATE_LOADED;
setAssociatedCanvas(canvas);
}
diff --git a/src/com/android/gallery3d/ui/SlideshowView.java b/src/com/android/gallery3d/ui/SlideshowView.java
index bb36c47e9..7734eb236 100644
--- a/src/com/android/gallery3d/ui/SlideshowView.java
+++ b/src/com/android/gallery3d/ui/SlideshowView.java
@@ -21,11 +21,10 @@ import android.graphics.PointF;
import com.android.gallery3d.anim.CanvasAnimation;
import com.android.gallery3d.anim.FloatAnimation;
+import com.android.gallery3d.ui.GLCanvas.Blending;
import java.util.Random;
-import javax.microedition.khronos.opengles.GL11;
-
public class SlideshowView extends GLView {
@SuppressWarnings("unused")
private static final String TAG = "SlideshowView";
@@ -93,8 +92,8 @@ public class SlideshowView extends GLView {
protected void render(GLCanvas canvas) {
long animTime = AnimationTime.get();
boolean requestRender = mTransitionAnimation.calculate(animTime);
- GL11 gl = canvas.getGLInstance();
- gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
+ canvas.save(GLCanvas.SAVE_FLAG_BLEND);
+ canvas.setBlending(Blending.Additive);
float alpha = mPrevTexture == null ? 1f : mTransitionAnimation.get();
if (mPrevTexture != null && alpha != 1f) {
@@ -118,7 +117,7 @@ public class SlideshowView extends GLView {
canvas.restore();
}
if (requestRender) invalidate();
- gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ canvas.restore();
}
private class SlideshowAnimation extends CanvasAnimation {
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java
index bb86d05ef..470ee6a98 100644
--- a/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -25,7 +25,6 @@ import com.android.gallery3d.common.Utils;
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
// UploadedTextures use a Bitmap for the content of the texture.
//
@@ -194,9 +193,7 @@ abstract class UploadedTexture extends BasicTexture {
Bitmap bitmap = getBitmap();
int format = GLUtils.getInternalFormat(bitmap);
int type = GLUtils.getType(bitmap);
- canvas.getGLInstance().glBindTexture(GL11.GL_TEXTURE_2D, mId);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0, mBorder, mBorder,
- bitmap, format, type);
+ canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
freeBitmap();
mContentValid = true;
}
@@ -210,11 +207,7 @@ abstract class UploadedTexture extends BasicTexture {
return sUploadedCount > UPLOAD_LIMIT;
}
- static int[] sTextureId = new int[1];
- static float[] sCropRect = new float[4];
-
private void uploadToCanvas(GLCanvas canvas) {
- GL11 gl = canvas.getGLInstance();
Bitmap bitmap = getBitmap();
if (bitmap != null) {
@@ -228,65 +221,40 @@ abstract class UploadedTexture extends BasicTexture {
Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
- // Define a vertically flipped crop rectangle for
- // OES_draw_texture.
- // The four values in sCropRect are: left, bottom, width, and
- // height. Negative value of width or height means flip.
- sCropRect[0] = mBorder;
- sCropRect[1] = mBorder + bHeight;
- sCropRect[2] = bWidth;
- sCropRect[3] = -bHeight;
-
// Upload the bitmap to a new texture.
- GLId.glGenTextures(1, sTextureId, 0);
- gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]);
- gl.glTexParameterfv(GL11.GL_TEXTURE_2D,
- GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameteri(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
- gl.glTexParameterf(GL11.GL_TEXTURE_2D,
- GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ mId = GLCanvas.getGLId().generateTexture();
+ canvas.setTextureParameters(this);
if (bWidth == texWidth && bHeight == texHeight) {
- GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
+ canvas.initializeTexture(this, bitmap);
} else {
int format = GLUtils.getInternalFormat(bitmap);
int type = GLUtils.getType(bitmap);
Config config = bitmap.getConfig();
- gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format,
- texWidth, texHeight, 0, format, type, null);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- mBorder, mBorder, bitmap, format, type);
+ canvas.initializeTextureSize(this, format, type);
+ canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
if (mBorder > 0) {
// Left border
Bitmap line = getBorderLine(true, config, texHeight);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, 0, line, format, type);
+ canvas.texSubImage2D(this, 0, 0, line, format, type);
// Top border
line = getBorderLine(false, config, texWidth);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, 0, line, format, type);
+ canvas.texSubImage2D(this, 0, 0, line, format, type);
}
// Right border
if (mBorder + bWidth < texWidth) {
Bitmap line = getBorderLine(true, config, texHeight);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- mBorder + bWidth, 0, line, format, type);
+ canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
}
// Bottom border
if (mBorder + bHeight < texHeight) {
Bitmap line = getBorderLine(false, config, texWidth);
- GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
- 0, mBorder + bHeight, line, format, type);
+ canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
}
}
} finally {
@@ -294,7 +262,6 @@ abstract class UploadedTexture extends BasicTexture {
}
// Update texture state.
setAssociatedCanvas(canvas);
- mId = sTextureId[0];
mState = STATE_LOADED;
mContentValid = true;
} else {
diff --git a/src/com/android/gallery3d/util/AccessibilityUtils.java b/src/com/android/gallery3d/util/AccessibilityUtils.java
new file mode 100644
index 000000000..9df8e4ece
--- /dev/null
+++ b/src/com/android/gallery3d/util/AccessibilityUtils.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.gallery3d.common.ApiHelper;
+
+/**
+ * AccessibilityUtils provides functions needed in accessibility mode. All the functions
+ * in this class are made compatible with gingerbread and later API's
+*/
+public class AccessibilityUtils {
+ public static void makeAnnouncement(View view, CharSequence announcement) {
+ if (view == null)
+ return;
+ if (ApiHelper.HAS_ANNOUNCE_FOR_ACCESSIBILITY) {
+ view.announceForAccessibility(announcement);
+ } else {
+ // For API 15 and earlier, we need to construct an accessibility event
+ Context ctx = view.getContext();
+ AccessibilityManager am = (AccessibilityManager) ctx.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ if (!am.isEnabled()) return;
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ AccessibilityRecordCompat arc = new AccessibilityRecordCompat(event);
+ arc.setSource(view);
+ event.setClassName(view.getClass().getName());
+ event.setPackageName(view.getContext().getPackageName());
+ event.setEnabled(view.isEnabled());
+ event.getText().add(announcement);
+ am.sendAccessibilityEvent(event);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/gallery3d/util/IntArray.java b/src/com/android/gallery3d/util/IntArray.java
index 082089a65..2c4dc2c83 100644
--- a/src/com/android/gallery3d/util/IntArray.java
+++ b/src/com/android/gallery3d/util/IntArray.java
@@ -31,6 +31,11 @@ public class IntArray {
mData[mSize++] = value;
}
+ public int removeLast() {
+ mSize--;
+ return mData[mSize];
+ }
+
public int size() {
return mSize;
}
diff --git a/src/com/android/gallery3d/util/SaveVideoFileInfo.java b/src/com/android/gallery3d/util/SaveVideoFileInfo.java
new file mode 100644
index 000000000..c7e5e8568
--- /dev/null
+++ b/src/com/android/gallery3d/util/SaveVideoFileInfo.java
@@ -0,0 +1,29 @@
+/*
+ * 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.util;
+
+import java.io.File;
+
+public class SaveVideoFileInfo {
+ public File mFile = null;
+ public String mFileName = null;
+ // This the full directory path.
+ public File mDirectory = null;
+ // This is just the folder's name.
+ public String mFolderName = null;
+
+}
diff --git a/src/com/android/gallery3d/util/SaveVideoFileUtils.java b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
new file mode 100644
index 000000000..c281dd3e7
--- /dev/null
+++ b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
@@ -0,0 +1,141 @@
+/*
+ * 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.util;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore.Video;
+import android.provider.MediaStore.Video.VideoColumns;
+
+import java.io.File;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+
+public class SaveVideoFileUtils {
+ // Copy from SaveCopyTask.java in terms of how to handle the destination
+ // path and filename : querySource() and getSaveDirectory().
+ public interface ContentResolverQueryCallback {
+ void onCursorResult(Cursor cursor);
+ }
+
+ // This function can decide which folder to save the video file, and generate
+ // the needed information for the video file including filename.
+ public static SaveVideoFileInfo getDstMp4FileInfo(String fileNameFormat,
+ ContentResolver contentResolver, Uri uri, String defaultFolderName) {
+ SaveVideoFileInfo dstFileInfo = new SaveVideoFileInfo();
+ // Use the default save directory if the source directory cannot be
+ // saved.
+ dstFileInfo.mDirectory = getSaveDirectory(contentResolver, uri);
+ if ((dstFileInfo.mDirectory == null) || !dstFileInfo.mDirectory.canWrite()) {
+ dstFileInfo.mDirectory = new File(Environment.getExternalStorageDirectory(),
+ BucketNames.DOWNLOAD);
+ dstFileInfo.mFolderName = defaultFolderName;
+ } else {
+ dstFileInfo.mFolderName = dstFileInfo.mDirectory.getName();
+ }
+ dstFileInfo.mFileName = new SimpleDateFormat(fileNameFormat).format(
+ new Date(System.currentTimeMillis()));
+
+ dstFileInfo.mFile = new File(dstFileInfo.mDirectory, dstFileInfo.mFileName + ".mp4");
+ return dstFileInfo;
+ }
+
+ private static void querySource(ContentResolver contentResolver, Uri uri,
+ String[] projection, ContentResolverQueryCallback callback) {
+ Cursor cursor = null;
+ try {
+ cursor = contentResolver.query(uri, projection, null, null, null);
+ if ((cursor != null) && cursor.moveToNext()) {
+ callback.onCursorResult(cursor);
+ }
+ } catch (Exception e) {
+ // Ignore error for lacking the data column from the source.
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private static File getSaveDirectory(ContentResolver contentResolver, Uri uri) {
+ final File[] dir = new File[1];
+ querySource(contentResolver, uri,
+ new String[] { VideoColumns.DATA },
+ new ContentResolverQueryCallback() {
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ dir[0] = new File(cursor.getString(0)).getParentFile();
+ }
+ });
+ return dir[0];
+ }
+
+
+ /**
+ * Insert the content (saved file) with proper video properties.
+ */
+ public static Uri insertContent(SaveVideoFileInfo mDstFileInfo,
+ ContentResolver contentResolver, Uri uri ) {
+ long nowInMs = System.currentTimeMillis();
+ long nowInSec = nowInMs / 1000;
+ final ContentValues values = new ContentValues(12);
+ values.put(Video.Media.TITLE, mDstFileInfo.mFileName);
+ values.put(Video.Media.DISPLAY_NAME, mDstFileInfo.mFile.getName());
+ values.put(Video.Media.MIME_TYPE, "video/mp4");
+ values.put(Video.Media.DATE_TAKEN, nowInMs);
+ values.put(Video.Media.DATE_MODIFIED, nowInSec);
+ values.put(Video.Media.DATE_ADDED, nowInSec);
+ values.put(Video.Media.DATA, mDstFileInfo.mFile.getAbsolutePath());
+ values.put(Video.Media.SIZE, mDstFileInfo.mFile.length());
+ // Copy the data taken and location info from src.
+ String[] projection = new String[] {
+ VideoColumns.DATE_TAKEN,
+ VideoColumns.LATITUDE,
+ VideoColumns.LONGITUDE,
+ VideoColumns.RESOLUTION,
+ };
+
+ // Copy some info from the source file.
+ querySource(contentResolver, uri, projection,
+ new ContentResolverQueryCallback() {
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ long timeTaken = cursor.getLong(0);
+ if (timeTaken > 0) {
+ values.put(Video.Media.DATE_TAKEN, timeTaken);
+ }
+ double latitude = cursor.getDouble(1);
+ double longitude = cursor.getDouble(2);
+ // TODO: Change || to && after the default location
+ // issue is
+ // fixed.
+ if ((latitude != 0f) || (longitude != 0f)) {
+ values.put(Video.Media.LATITUDE, latitude);
+ values.put(Video.Media.LONGITUDE, longitude);
+ }
+ values.put(Video.Media.RESOLUTION, cursor.getString(3));
+
+ }
+ });
+
+ return contentResolver.insert(Video.Media.EXTERNAL_CONTENT_URI, values);
+ }
+
+}