summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/app
diff options
context:
space:
mode:
authorjinwu <jinwu@codeaurora.org>2017-04-07 13:27:48 +0800
committerjinwu <jinwu@codeaurora.org>2017-04-10 11:24:45 +0800
commit4fafa7a04ab78f91bbbf59c5b722a84a9b62f85c (patch)
treefddbf3ccafbd5a8a4be79775a0b22baa30ad848e /src/com/android/gallery3d/app
parent348658059575aa7a3bd722a1f9b06d0683225e9c (diff)
downloadandroid_packages_apps_Gallery2-4fafa7a04ab78f91bbbf59c5b722a84a9b62f85c.tar.gz
android_packages_apps_Gallery2-4fafa7a04ab78f91bbbf59c5b722a84a9b62f85c.tar.bz2
android_packages_apps_Gallery2-4fafa7a04ab78f91bbbf59c5b722a84a9b62f85c.zip
SnapdragonGallery: update source code
Update the newest source code from N to O. Change-Id: I4404638c3111c40bb5d182c770383d9d4760eec2 CRs-Fixed: 2021069
Diffstat (limited to 'src/com/android/gallery3d/app')
-rw-r--r--src/com/android/gallery3d/app/AbstractGalleryActivity.java11
-rwxr-xr-xsrc/com/android/gallery3d/app/GalleryActivity.java28
-rw-r--r--src/com/android/gallery3d/app/MovieActivity.java11
-rw-r--r--src/com/android/gallery3d/app/MoviePlayer.java30
-rwxr-xr-xsrc/com/android/gallery3d/app/PhotoPage.java120
-rw-r--r--src/com/android/gallery3d/app/ThreeDButton.java109
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/Effect.java211
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/GLView.java186
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/ThreeDimensionalActivity.java190
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Error.java44
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Mesh.java149
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Renderer.java178
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Settings.java47
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Shader.java105
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/gl/Texture.java65
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/mpo/Task.java231
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/threed/Controller.java182
-rw-r--r--src/com/android/gallery3d/app/dualcam3d/threed/Gyro.java115
18 files changed, 1976 insertions, 36 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index 709151a1a..c8898ee92 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -61,6 +61,7 @@ public abstract class AbstractGalleryActivity extends AbstractPermissionActivity
private TransitionStore mTransitionStore = new TransitionStore();
private PanoramaViewHelper mPanoramaViewHelper;
private Toolbar mToolbar;
+ public boolean isTopMenuShow = false;
private AlertDialog mAlertDialog = null;
private BroadcastReceiver mMountReceiver = new BroadcastReceiver() {
@@ -204,6 +205,12 @@ public abstract class AbstractGalleryActivity extends AbstractPermissionActivity
@Override
protected void onResume() {
super.onResume();
+ // If top menu shows, GLView is active,
+ // so don't need to resume it.
+ if (isTopMenuShow) {
+ isTopMenuShow = false;
+ return;
+ }
mGLRootView.lockRenderThread();
try {
getStateManager().resume();
@@ -218,6 +225,10 @@ public abstract class AbstractGalleryActivity extends AbstractPermissionActivity
@Override
protected void onPause() {
super.onPause();
+ // If top menu shows, don't pause GLView,
+ // so it can redraw when rotating.
+ if (isTopMenuShow)
+ return;
mOrientationManager.pause();
mGLRootView.onPause();
mGLRootView.lockRenderThread();
diff --git a/src/com/android/gallery3d/app/GalleryActivity.java b/src/com/android/gallery3d/app/GalleryActivity.java
index e18f1c2c6..95883afd5 100755
--- a/src/com/android/gallery3d/app/GalleryActivity.java
+++ b/src/com/android/gallery3d/app/GalleryActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
* Not a Contribution
*
* Copyright (C) 2009 The Android Open Source Project
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
+import android.content.UriMatcher;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -91,6 +92,17 @@ public final class GalleryActivity extends AbstractGalleryActivity implements On
public static final String KEY_FROM_SNAPCAM = "from-snapcam";
public static final String KEY_TOTAL_NUMBER = "total-number";
+ private static final int ALL_DOWNLOADS = 1;
+ private static final int ALL_DOWNLOADS_ID = 2;
+ private static final UriMatcher sURIMatcher =
+ new UriMatcher(UriMatcher.NO_MATCH);
+ public static final String PERMISSION_ACCESS_ALL =
+ "android.permission.ACCESS_ALL_DOWNLOADS";
+ static {
+ sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS);
+ sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
+ }
+
private static final String TAG = "GalleryActivity";
private Dialog mVersionCheckDialog;
private ListView mDrawerListView;
@@ -366,6 +378,17 @@ public final class GalleryActivity extends AbstractGalleryActivity implements On
} else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
|| ACTION_REVIEW.equalsIgnoreCase(action)){
mDrawerLayoutSupported = false;
+ Uri uri = intent.getData();
+ int flag = intent.getFlags();
+ int match = sURIMatcher.match(uri);
+ if ((match == ALL_DOWNLOADS || match == ALL_DOWNLOADS_ID) &&
+ (flag & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) {
+ if (checkCallingOrSelfPermission(
+ PERMISSION_ACCESS_ALL) != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "no permission to view: " + uri);
+ return;
+ }
+ }
startViewAction(intent);
} else {
mDrawerLayoutSupported = true;
@@ -750,7 +773,8 @@ public final class GalleryActivity extends AbstractGalleryActivity implements On
DualCameraNativeEngine.getInstance().initDepthMap(
primaryBm, auxiliaryBm, mpoFilepath,
- DualCameraNativeEngine.getInstance().getCalibFilepath(context));
+ DualCameraNativeEngine.getInstance().getCalibFilepath(context),
+ DualCameraNativeEngine.DEFAULT_BRIGHTNESS_INTENSITY);
primaryBm.recycle();
primaryBm = null;
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 150b3807f..811cdca77 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -141,9 +141,11 @@ public class MovieActivity extends AbstractPermissionActivity {
|| audioManager.isBluetoothA2dpOn();
} else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
|| action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
- final int deviceClass = ((BluetoothDevice)
+ final BluetoothClass bc = ((BluetoothDevice)
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))
- .getBluetoothClass().getDeviceClass();
+ .getBluetoothClass();
+ if (bc == null) return;
+ final int deviceClass = bc.getDeviceClass();
if ((deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES)
|| (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
mIsHeadsetOn = action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)
@@ -195,7 +197,7 @@ public class MovieActivity extends AbstractPermissionActivity {
if (isPermissionGranted()) {
init(intent, rootView, savedInstanceState);
}
-
+ registerScreenReceiver();
// DRM validation
// Uri original = intent.getData();
// String mimeType = intent.getType();
@@ -584,7 +586,6 @@ public class MovieActivity extends AbstractPermissionActivity {
mPlayer.requestAudioFocus();
super.onStart();
mMovieHooker.onStart();
- registerScreenReceiver();
}
@Override
@@ -600,7 +601,6 @@ public class MovieActivity extends AbstractPermissionActivity {
mControlResumed = false;
}
mMovieHooker.onStop();
- unregisterScreenReceiver();
}
@Override
@@ -687,6 +687,7 @@ public class MovieActivity extends AbstractPermissionActivity {
mPlayer.onDestroy();
super.onDestroy();
mMovieHooker.onDestroy();
+ unregisterScreenReceiver();
}
@Override
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 162c0731c..5477a145f 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -100,7 +100,6 @@ public class MoviePlayer implements
private static final String KEY_VIDEO_STATE = "video_state";
private static final String VIRTUALIZE_EXTRA = "virtualize";
- private static final long BLACK_TIMEOUT = 500;
public static final int SERVER_TIMEOUT = 8801;
// If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
@@ -115,6 +114,7 @@ public class MoviePlayer implements
private Context mContext;
private final CodeauroraVideoView mVideoView;
+ private final View mCoverView;
private final View mRootView;
private final Bookmarker mBookmarker;
private final Handler mHandler = new Handler();
@@ -194,16 +194,6 @@ public class MoviePlayer implements
}
};
- private Runnable mDelayVideoRunnable = new Runnable() {
- @Override
- public void run() {
- if (LOG) {
- Log.v(TAG, "mDelayVideoRunnable.run()");
- }
- mVideoView.setVisibility(View.VISIBLE);
- }
- };
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -227,6 +217,7 @@ public class MoviePlayer implements
mContext = movieActivity.getApplicationContext();
mRootView = rootView;
mVideoView = (CodeauroraVideoView) rootView.findViewById(R.id.surface_view);
+ mCoverView = rootView.findViewById(R.id.surface_view_cover);
mBookmarker = new Bookmarker(movieActivity);
mController = new MovieControllerOverlayNew(movieActivity);
@@ -279,18 +270,6 @@ public class MoviePlayer implements
}
});
- // The SurfaceView is transparent before drawing the first frame.
- // This makes the UI flashing when open a video. (black -> old screen
- // -> video) However, we have no way to know the timing of the first
- // frame. So, we hide the VideoView for a while to make sure the
- // video has been drawn on it.
- mVideoView.postDelayed(new Runnable() {
- @Override
- public void run() {
- mVideoView.setVisibility(View.VISIBLE);
- }
- }, BLACK_TIMEOUT);
-
setOnSystemUiVisibilityChangeListener();
// Hide system UI by default
showSystemUi(false);
@@ -506,10 +485,6 @@ public class MoviePlayer implements
public void onResume() {
mDragging = false;// clear drag info
if (mHasPaused) {
- //M: same as launch case to delay transparent.
- mVideoView.removeCallbacks(mDelayVideoRunnable);
- mVideoView.postDelayed(mDelayVideoRunnable, BLACK_TIMEOUT);
-
if (mServerTimeoutExt.handleOnResume() || mIsShowResumingDialog) {
mHasPaused = false;
return;
@@ -815,6 +790,7 @@ public class MoviePlayer implements
if (LOG) {
Log.v(TAG, "onPrepared(" + mp + ")");
}
+ mCoverView.setVisibility(View.GONE);
if (!isLocalFile()) {
mOverlayExt.setPlayingInfo(isLiveStreaming());
}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 161d729da..a665b1427 100755
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -30,6 +30,7 @@ import android.net.Uri;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateBeamUrisCallback;
import android.nfc.NfcEvent;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -47,6 +48,8 @@ import android.widget.Toast;
import android.widget.Toolbar;
import org.codeaurora.gallery.R;
+
+import com.android.gallery3d.app.dualcam3d.ThreeDimensionalActivity;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.ComboAlbum;
import com.android.gallery3d.data.DataManager;
@@ -64,6 +67,9 @@ import com.android.gallery3d.data.SnailAlbum;
import com.android.gallery3d.data.SnailItem;
import com.android.gallery3d.data.SnailSource;
import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+import com.android.gallery3d.mpo.MpoParser;
import com.android.gallery3d.ui.DetailsHelper;
import com.android.gallery3d.ui.DetailsHelper.CloseListener;
import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
@@ -83,7 +89,7 @@ import java.util.Locale;
public abstract class PhotoPage extends ActivityState implements
PhotoView.Listener, AppBridge.Server, ShareActionProvider.OnShareTargetSelectedListener,
- PhotoPageBottomControls.Delegate {
+ PhotoPageBottomControls.Delegate, ThreeDButton.Delegate{
private static final String TAG = "PhotoPage";
private static final int MSG_HIDE_BARS = 1;
@@ -111,6 +117,7 @@ public abstract class PhotoPage extends ActivityState implements
public static final String KEY_MEDIA_SET_PATH = "media-set-path";
public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
public static final String KEY_INDEX_HINT = "index-hint";
+ public static final String KEY_CURRENT_PHOTO_HINT = "currtent_photo_path";
public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
public static final String KEY_APP_BRIDGE = "app-bridge";
public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up";
@@ -208,6 +215,11 @@ public abstract class PhotoPage extends ActivityState implements
private int originalHeight = 0;
private int originalPadding = 0;
+ private ThreeDButton m3DButton;
+ private boolean bShow3DButton;
+ private LoadMpoDataTask mLoadMpoTask = new LoadMpoDataTask();;
+ private ParseMpoDataTask mParseMpoDateTask = new ParseMpoDataTask();
+
private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() {
@Override
public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
@@ -302,6 +314,9 @@ public abstract class PhotoPage extends ActivityState implements
mIsPanorama = message.arg1 == 1;
mIsPanorama360 = message.arg2 == 1;
mBottomControls.refresh();
+ if (null != m3DButton) {
+ m3DButton.refresh();
+ }
}
break;
}
@@ -421,6 +436,12 @@ public abstract class PhotoPage extends ActivityState implements
//we only save index in onSaveState, set itemPath to null to get the right path later
itemPath = null;
}
+ if ((mCurrentPhoto == null) && (restoreState != null)) {
+ String curPath = restoreState.getString(KEY_CURRENT_PHOTO_HINT, null);
+ if (curPath != null)
+ mCurrentPhoto = (MediaItem)
+ mActivity.getDataManager().getMediaObject(curPath);
+ }
if (mSetPathString != null) {
mShowSpinner = true;
mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
@@ -591,6 +612,7 @@ public abstract class PhotoPage extends ActivityState implements
if (galleryRoot != null) {
if (mSecureAlbum == null) {
mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot);
+ m3DButton = new ThreeDButton(this, mActivity, galleryRoot);
}
}
@@ -611,6 +633,7 @@ public abstract class PhotoPage extends ActivityState implements
@Override
protected void onSaveState(Bundle outState) {
outState.putInt(KEY_INDEX_HINT,mCurrentIndex);
+ outState.putString(KEY_CURRENT_PHOTO_HINT, mCurrentPhoto.getFilePath());
super.onSaveState(outState);
}
@@ -662,6 +685,7 @@ public abstract class PhotoPage extends ActivityState implements
case R.id.photopage_bottom_control_share:
if (mModel != null && mModel.getMediaItem(0) != null) {
Uri uri = mActivity.getDataManager().getContentUri(mModel.getMediaItem(0).getPath());
+ mActivity.isTopMenuShow = true;
mShareIntent.setType(MenuExecutor.getMimeType(mModel
.getMediaItem(0).getMediaType()));
mShareIntent.putExtra(Intent.EXTRA_STREAM, uri);
@@ -690,6 +714,24 @@ public abstract class PhotoPage extends ActivityState implements
}
}
+ @Override
+ public boolean canDisplay3DButton() {
+ return bShow3DButton && mShowBars
+ && (mPhotoView == null ? false : !mPhotoView.getFilmMode());
+ }
+
+ @Override
+ public void on3DButtonClicked() {
+ if (mCurrentPhoto != null) {
+ Path p = mCurrentPhoto.getPath();
+ Uri uri = mActivity.getDataManager().getContentUri(p);
+ Intent intent = new Intent();
+ intent.setClass(mActivity, ThreeDimensionalActivity.class);
+ intent.setData(uri);
+ mActivity.startActivity(intent);
+ }
+ }
+
@TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
private void setupNfcBeamPush() {
if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) return;
@@ -828,6 +870,7 @@ public abstract class PhotoPage extends ActivityState implements
updateMenuOperations();
refreshBottomControlsWhenReady();
+ parseMpoData();
if (mShowDetails) {
mDetailsHelper.reloadDetails();
}
@@ -840,6 +883,9 @@ public abstract class PhotoPage extends ActivityState implements
private void updateCurrentPhoto(MediaItem photo) {
if (mCurrentPhoto == photo) return;
mCurrentPhoto = photo;
+ if (mPhotoView == null) {
+ return;
+ }
if (mPhotoView.getFilmMode()) {
requestDeferredUpdate();
} else {
@@ -934,7 +980,7 @@ public abstract class PhotoPage extends ActivityState implements
private void refreshHidingMessage() {
mHandler.removeMessages(MSG_HIDE_BARS);
if (mPhotoView == null) {
- mPhotoView = (PhotoView) mRootPane.getComponent(0);
+ return;
}
if (!mIsMenuVisible && !mPhotoView.getFilmMode()) {
mHandler.sendEmptyMessageDelayed(MSG_HIDE_BARS, HIDE_BARS_TIMEOUT);
@@ -1419,6 +1465,7 @@ public abstract class PhotoPage extends ActivityState implements
}
if (null != mModel) {
mModel.setCurrentPhoto(path, mCurrentIndex);
+ parseMpoData();
}
}
}
@@ -1464,11 +1511,23 @@ public abstract class PhotoPage extends ActivityState implements
}
if (path != null) {
mModel.setCurrentPhoto(Path.fromString(path), index);
+ parseMpoData();
}
}
}
}
+ private void parseMpoData() {
+ bShow3DButton = false;
+ if (DualCameraNativeEngine.getInstance().isLibLoaded()) {
+ if (mParseMpoDateTask.getStatus() != AsyncTask.Status.FINISHED) {
+ boolean r = mParseMpoDateTask.cancel(true);
+ }
+ mParseMpoDateTask = new ParseMpoDataTask();
+ mParseMpoDateTask.execute();
+ }
+ }
+
@Override
public void onPause() {
super.onPause();
@@ -1663,6 +1722,7 @@ public abstract class PhotoPage extends ActivityState implements
}
mActivity.getGLRoot().setOrientationSource(null);
if (mBottomControls != null) mBottomControls.cleanup();
+ if (m3DButton != null) m3DButton.cleanup();
mPhotoView.destroy();
mPhotoView = null;
// Remove all pending messages.
@@ -1761,4 +1821,60 @@ public abstract class PhotoPage extends ActivityState implements
}
}
+ public class ParseMpoDataTask extends AsyncTask<Void, Void, Void> {
+ private byte[] mPrimaryImgData = null;
+ private byte[] mAuxImgData = null;
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (mCurrentPhoto == null)return null;
+ MpoParser parser = MpoParser.parse(mActivity, mCurrentPhoto.getContentUri());
+ mPrimaryImgData = parser.readImgData(true);
+ mAuxImgData = parser.readImgData(false);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (isCancelled()){
+ bShow3DButton = false;
+ m3DButton.refresh();
+ return;
+ }
+ if(mPrimaryImgData == null ||
+ mAuxImgData == null) {
+ // parse failed
+ bShow3DButton = false;
+ } else {
+ if (mLoadMpoTask.getStatus() != Status.FINISHED) {
+ boolean r = mLoadMpoTask.cancel(true);
+ }
+ bShow3DButton = true;
+ mLoadMpoTask = new LoadMpoDataTask();
+ mLoadMpoTask.execute(mPrimaryImgData, mAuxImgData);
+ }
+ m3DButton.refresh();
+ }
+ }
+
+ private class LoadMpoDataTask extends AsyncTask<byte[], Void, Boolean> {
+
+ @Override
+ protected Boolean doInBackground(byte[]... params) {
+ return MasterImage.getImage().loadMpo(mActivity, params[0], params[1], mCurrentPhoto.getContentUri());
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (isCancelled()){
+ bShow3DButton = false;
+ m3DButton.refresh();
+ return;
+ }
+ if (!result) {
+ bShow3DButton = result;
+ m3DButton.refresh();
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/app/ThreeDButton.java b/src/com/android/gallery3d/app/ThreeDButton.java
new file mode 100644
index 000000000..4cc146ab9
--- /dev/null
+++ b/src/com/android/gallery3d/app/ThreeDButton.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.RelativeLayout;
+
+import org.codeaurora.gallery.R;
+
+public class ThreeDButton implements OnClickListener {
+ interface Delegate {
+ boolean canDisplay3DButton();
+
+ void on3DButtonClicked();
+ }
+
+ private Delegate mDelegate;
+ private ViewGroup root;
+ private ViewGroup mContainer;
+ private boolean mContainerVisible = false;
+
+ private Animation mAnimIn = new AlphaAnimation(0f, 1f);
+ private Animation mAnimOut = new AlphaAnimation(1f, 0f);
+ private static final int ANIM_DURATION = 200;
+
+ public ThreeDButton(Delegate delegate, Context context, RelativeLayout layout) {
+ mDelegate = delegate;
+ root = layout;
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mContainer = (ViewGroup) inflater
+ .inflate(R.layout.three_d_button, root, false);
+ mContainer.findViewById(R.id.three_dimensional).setOnClickListener(this);
+ root.addView(mContainer);
+ mAnimIn.setDuration(ANIM_DURATION);
+ mAnimOut.setDuration(ANIM_DURATION);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mContainer.getVisibility() == View.VISIBLE) {
+ mDelegate.on3DButtonClicked();
+ }
+ }
+
+ public void refresh() {
+ boolean visible = mDelegate.canDisplay3DButton();
+ if (visible != mContainerVisible) {
+ if (visible) {
+ show();
+ } else {
+ hide();
+ }
+ mContainerVisible = visible;
+ }
+ }
+
+ private void hide() {
+ mContainer.clearAnimation();
+ mAnimOut.reset();
+ mContainer.startAnimation(mAnimOut);
+ mContainer.setVisibility(View.GONE);
+ }
+
+ private void show() {
+ mContainer.clearAnimation();
+ mAnimIn.reset();
+ mContainer.startAnimation(mAnimIn);
+ mContainer.setVisibility(View.VISIBLE);
+ }
+
+ public void cleanup() {
+ root.removeView(mContainer);
+ }
+
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/Effect.java b/src/com/android/gallery3d/app/dualcam3d/Effect.java
new file mode 100644
index 000000000..0f9cde4cb
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/Effect.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app.dualcam3d;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import com.android.gallery3d.app.dualcam3d.threed.Controller;
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+
+public class Effect implements GLView.Listener {
+ private static final String TAG = "Effect";
+ public static final int INTENSITY_MAX = 10;
+ public static final float DEFAULT_BRIGHTNESS_INTENSITY = 1.0f;
+ private static final int PREVIEW_DIMENSION_MAX = 1080;
+
+ enum Type {
+ NONE, FOCUS, HALO, SKETCH, FUSION, ZOOM,
+ MOTION, BW, BLACKBOARD, WHITEBOARD,
+ POSTERIZE, NEGATIVE, THREE_DIMENSIONAL
+ }
+
+ private final ThreeDimensionalActivity mActivity;
+ private Bitmap mBitmap;
+ private Type mType = Type.NONE;
+ private int mX;
+ private int mY;
+ private float mIntensity = 0.5f;
+ private boolean mIsPreview = true;
+
+ private int mScale;
+
+ private Controller mController;
+
+ private Thread mThread;
+ private Boolean mCancelled;
+
+ public Effect(ThreeDimensionalActivity activity) {
+ mActivity = activity;
+ }
+
+ public void setBitmap(Bitmap bitmap, int scale) {
+ mBitmap = bitmap;
+ scaleBitmap(scale);
+ }
+
+ public static int scale(int width, int height) {
+ int scale = 1;
+ while (width / scale > PREVIEW_DIMENSION_MAX || height / scale > PREVIEW_DIMENSION_MAX) {
+ scale *= 2;
+ }
+ return scale > 1 ? scale : 1;
+ }
+
+ private void scaleBitmap(int scale) {
+ if (scale != 1) {
+ mScale = scale;
+ } else if (mBitmap != null) {
+ int width = mBitmap.getWidth(), height = mBitmap.getHeight();
+ mX = width / 2;
+ mY = height / 2;
+ mScale = scale(width, height);
+ if (mScale != 1) {
+ mBitmap = Bitmap.createScaledBitmap(mBitmap,
+ width / mScale, height / mScale, false);
+ }
+ }
+ mActivity.sendMessage(ThreeDimensionalActivity.MSG_UPDATE_IMAGE, mBitmap);
+ }
+
+ public void set(Type type) {
+ if (type == Type.THREE_DIMENSIONAL) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ DualCameraNativeEngine.DepthMap3D map = DualCameraNativeEngine.getInstance()
+ .getDepthMap3D(mBitmap);
+ if (map != null) {
+ Log.d(TAG, "got 3d map");
+ mActivity.sendMessage(ThreeDimensionalActivity.MSG_UPDATE_IMAGE, mBitmap);
+ mActivity.sendMessage(ThreeDimensionalActivity.MSG_UPDATE_3D_DEPTH_MAP, map);
+ } else {
+ Log.e(TAG, "cannot get 3d map");
+ }
+ }
+ }).start();
+ if (mController == null) {
+ mController = mActivity.getController();
+ }
+ mController.start();
+ } else {
+ if (mController != null) {
+ mController.stop(true);
+ }
+ mActivity.sendMessage(ThreeDimensionalActivity.MSG_UPDATE_3D_DEPTH_MAP, null);
+ request();
+ }
+ mType = type;
+ }
+
+ private void setCoordination(float x, float y) {
+ if (x >= 0 && y >= 0 && x < mBitmap.getWidth() && y < mBitmap.getHeight()) {
+ mX = (int) (x * mScale);
+ mY = (int) (y * mScale);
+ }
+ }
+
+ public void setIntensity(float intensity) {
+ if (intensity < 0) intensity = 0;
+ if (intensity > INTENSITY_MAX) intensity = 1;
+ if (intensity != mIntensity) {
+ mIntensity = intensity / INTENSITY_MAX;
+ request();
+ }
+ }
+
+ private synchronized void request() {
+ notify();
+ }
+
+
+ public void recycle() {
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ }
+
+ @Override
+ public void onMove(float deltaX, float deltaY) {
+ if (mType == Type.THREE_DIMENSIONAL && mController != null) {
+ mController.onMove(deltaX, deltaY);
+ }
+ }
+
+ @Override
+ public void onClick(float x, float y) {
+ if (mType != Type.THREE_DIMENSIONAL) {
+ setCoordination(x, y);
+ request();
+ }
+ }
+
+ @Override
+ public void onLayout(int width, int height) {
+ }
+
+ public void resume() {
+ if (mController != null && mType == Type.THREE_DIMENSIONAL) {
+ mController.start();
+ }
+ mCancelled = false;
+ mThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (!mCancelled) {
+ synchronized (Effect.this) {
+ try {
+ Effect.this.wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }
+ }
+ });
+ mThread.start();
+ }
+
+ public void pause() {
+ if (mController != null) {
+ mController.stop(false);
+ }
+ if (mThread != null) {
+ mCancelled = true;
+ synchronized (this) {
+ notify();
+ }
+ try {
+ mThread.join();
+ } catch (InterruptedException ignored) {
+ }
+ mThread = null;
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/GLView.java b/src/com/android/gallery3d/app/dualcam3d/GLView.java
new file mode 100644
index 000000000..181d3e095
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/GLView.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app.dualcam3d;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+
+public class GLView extends GLSurfaceView {
+ private static final String TAG = "GLView";
+ private final com.android.gallery3d.app.dualcam3d.gl.Renderer mRenderer;
+
+ private Bitmap mBitmap;
+ private DualCameraNativeEngine.DepthMap3D mDepthMap;
+ private int mRotation;
+
+ public GLView(Context context) {
+ this(context, null);
+ }
+
+ public GLView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setEGLContextClientVersion(2);
+ mRenderer = new com.android.gallery3d.app.dualcam3d.gl.Renderer();
+ setRenderer(mRenderer);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ mRenderer.setImageBitmap(mBitmap);
+ mRenderer.set3DEffectDepthMap(mDepthMap);
+ requestRender();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mRenderer.setImageBitmap(null);
+ mRenderer.set3DEffectDepthMap(null);
+ }
+
+ public void setImageBitmap(Bitmap bitmap) {
+ mBitmap = bitmap;
+ mRenderer.setImageBitmap(bitmap);
+ mRenderer.set3DEffectDepthMap(null);
+ requestRender();
+ }
+
+ public void set3DEffectDepthMap(DualCameraNativeEngine.DepthMap3D map) {
+ mDepthMap = map;
+ mRenderer.set3DEffectDepthMap(map);
+ requestRender();
+ }
+
+ public void setOffset(float x, float y) {
+ mRenderer.setOffset(x, y);
+ requestRender();
+ }
+
+ public void setOffsetWithRotation(float x, float y) {
+ if (mRotation == Surface.ROTATION_0 || mRotation == Surface.ROTATION_180) {
+ //noinspection SuspiciousNameCombination
+ setOffset(y, x);
+ } else {
+ setOffset(x, y);
+ }
+ }
+
+ public void setOffsetDelta(float deltaX, float deltaY) {
+ mRenderer.setOffsetDelta(deltaX, deltaY);
+ requestRender();
+ }
+
+ public void recycle() {
+ mListener = null;
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ mBitmap = null;
+ }
+ }
+
+ public void setRotation(int rotation) {
+ mRotation = rotation;
+ }
+
+ private static final float MOVE_THRESHOLD = 1.0f;
+ private PointF mLastPoint;
+ private int mLastAction = MotionEvent.ACTION_DOWN;
+ private Listener mListener;
+
+ public interface Listener {
+ void onMove(float deltaX, float deltaY);
+
+ void onClick(float x, float y);
+
+ void onLayout(int width, int height);
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ Matrix invertMatrix = mRenderer.getImageInvertMatrix();
+ if (invertMatrix == null) {
+ return true;
+ }
+
+ float[] point = new float[]{event.getX(), event.getY()};
+ invertMatrix.mapPoints(point);
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastPoint = new PointF(point[0], point[1]);
+ mLastAction = MotionEvent.ACTION_DOWN;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mLastPoint != null) {
+ float deltaX = point[0] - mLastPoint.x,
+ deltaY = point[1] - mLastPoint.y;
+ if (mLastAction == MotionEvent.ACTION_MOVE || (Math.abs(deltaX) > MOVE_THRESHOLD
+ && Math.abs(deltaY) > MOVE_THRESHOLD)) {
+ if (mListener != null) {
+ mListener.onMove(deltaX, deltaY);
+ }
+ mLastPoint.set(point[0], point[1]);
+ mLastAction = MotionEvent.ACTION_MOVE;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mLastAction != MotionEvent.ACTION_MOVE && mListener != null) {
+ mListener.onClick(point[0], point[1]);
+ }
+ mLastPoint = null;
+ mLastAction = MotionEvent.ACTION_UP;
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed && mListener != null) {
+ mListener.onLayout(right - left, bottom - top);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/ThreeDimensionalActivity.java b/src/com/android/gallery3d/app/dualcam3d/ThreeDimensionalActivity.java
new file mode 100644
index 000000000..18fe36394
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/ThreeDimensionalActivity.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Window;
+import android.widget.LinearLayout;
+
+import com.android.gallery3d.app.dualcam3d.mpo.Task;
+import com.android.gallery3d.app.dualcam3d.threed.Controller;
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+
+import org.codeaurora.gallery.R;
+
+public class ThreeDimensionalActivity extends Activity {
+ private static final String TAG = ThreeDimensionalActivity.class.getSimpleName();
+
+ final static int MSG_UPDATE_IMAGE = 1;
+ final static int MSG_UPDATE_3D_DEPTH_MAP = 2;
+ private final static int MSG_IMAGE_LOADED = 3;
+ private final static int MSG_FINISH = 4;
+
+ private Effect mEffect;
+ private GLView mImageView;
+ private Task mTask;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_IMAGE:
+ if (mImageView != null) {
+ mImageView.setImageBitmap((Bitmap) msg.obj);
+ }
+ break;
+ case MSG_UPDATE_3D_DEPTH_MAP:
+ if (mImageView != null) {
+ mImageView.set3DEffectDepthMap((DualCameraNativeEngine.DepthMap3D) msg.obj);
+ }
+ break;
+ case MSG_IMAGE_LOADED:
+ mEffect.setBitmap((Bitmap) msg.obj, msg.arg1);
+ break;
+ case MSG_FINISH:
+ mEffect.set(Effect.Type.THREE_DIMENSIONAL);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.activity_three_dimensional);
+
+ mEffect = new Effect(this);
+ processIntent();
+ init();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+
+ private void init() {
+ mImageView = (GLView) findViewById(R.id.image);
+ mImageView.setListener(mEffect);
+ mImageView.setRotation(getWindowManager().getDefaultDisplay().getRotation());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mImageView.onResume();
+ if (mEffect != null) {
+ mEffect.resume();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mImageView.onPause();
+ if (mEffect != null) {
+ mEffect.pause();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (DualCameraNativeEngine.getInstance().isLibLoaded()) {
+ DualCameraNativeEngine.getInstance().releaseDepthMap();
+ }
+ if (mEffect != null) {
+ mEffect.recycle();
+ mEffect = null;
+ }
+ mImageView.recycle();
+ mTask.cancel();
+ super.onDestroy();
+ }
+
+ private void processIntent() {
+ Intent intent = getIntent();
+ Uri uri = intent.getData();
+ if (uri == null) {
+ finish();
+ return;
+ }
+ startLoadImage(uri);
+ }
+
+ private void startLoadImage(Uri uri) {
+ if (DualCameraNativeEngine.getInstance().isLibLoaded()) {
+ mTask = new Task(this, uri, new Task.Listener() {
+ @Override
+ public void onBitmapLoaded(Bitmap bitmap, int scale) {
+ if (bitmap != null) {
+ mHandler.obtainMessage(MSG_IMAGE_LOADED, scale, 0, bitmap).sendToTarget();
+ } else {
+ finish();
+ }
+ }
+
+ @Override
+ public int onScale(int width, int height) {
+ return Effect.scale(width, height);
+ }
+
+ @Override
+ public void onFinish(boolean result) {
+ if (!result) {
+ finish();
+ } else {
+ sendMessage(MSG_FINISH, null);
+ }
+ }
+ });
+ mTask.start(Effect.DEFAULT_BRIGHTNESS_INTENSITY);
+ }
+ }
+
+ public void sendMessage(int what, Object obj) {
+ mHandler.obtainMessage(what, obj).sendToTarget();
+ }
+
+ public Controller getController() {
+ return new Controller(mImageView, (LinearLayout) findViewById(R.id.mode_3d));
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Error.java b/src/com/android/gallery3d/app/dualcam3d/gl/Error.java
new file mode 100644
index 000000000..55f04dd32
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Error.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+import android.opengl.GLES20;
+import android.util.Log;
+
+class Error {
+ private static final String TAG = "GL";
+
+ public static void check() {
+ int error = GLES20.glGetError();
+ if (error != 0) {
+ Throwable t = new Throwable();
+ Log.e(TAG, "error: " + error, t);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Mesh.java b/src/com/android/gallery3d/app/dualcam3d/gl/Mesh.java
new file mode 100644
index 000000000..72d29ad9a
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Mesh.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+import android.opengl.GLES20;
+
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+class Mesh {
+ private static final String TAG = "Mesh";
+
+ private static final float TAN_HALF_FOV =
+ (float) Math.tan(Math.toRadians(Settings.FIELD_OF_VIEW / 2));
+
+ public FloatBuffer vertices;
+ public FloatBuffer colors;
+ public FloatBuffer textures;
+ private IntBuffer indices;
+
+ private int indexLength;
+
+ public void render(Shader shader) {
+ if (vertices == null || textures == null || indices == null || indices.capacity() == 0)
+ return;
+ shader.setMesh(this);
+ GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexLength, GLES20.GL_UNSIGNED_INT, indices);
+ }
+
+ private void rewindBuffers() {
+ vertices.position(0);
+ colors.position(0);
+ textures.position(0);
+ indices.position(0);
+ }
+
+ private static ByteBuffer allocateBuffer(int capacity) {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ return buffer;
+ }
+
+ public static Mesh create() {
+ Mesh m = new Mesh();
+
+ final int resolutionH = Settings.MESH_RESOLUTION_H;
+ final int resolutionV = Settings.MESH_RESOLUTION_V;
+ final int vertexCount = (resolutionH + 1) * (resolutionV + 1);
+ final int indexCount = resolutionH * resolutionV * 2;
+
+ m.vertices = allocateBuffer(vertexCount * 3 * 4).asFloatBuffer();
+ m.colors = allocateBuffer(vertexCount * 4 * 4).asFloatBuffer();
+ m.textures = allocateBuffer(vertexCount * 2 * 4).asFloatBuffer();
+ m.indices = allocateBuffer(indexCount * 3 * 4).asIntBuffer();
+ m.indexLength = indexCount * 3;
+
+ m.rewindBuffers();
+ return m;
+ }
+
+ private static float getVertexScale(float depth) {
+ return ((depth + Settings.CAMERA_POSITION) * TAN_HALF_FOV);
+ }
+
+ public void update(DualCameraNativeEngine.DepthMap3D depthMap, int width, int height,
+ float depth) {
+ final int resolutionH = depthMap == null ? 1 : Settings.MESH_RESOLUTION_H;
+ final int resolutionV = depthMap == null ? 1 : Settings.MESH_RESOLUTION_V;
+ indexLength = resolutionH * resolutionV * 2 * 3;
+
+ // / Correct aspect ratio of the rendered image, fit width
+ float scale = getVertexScale(Math.abs(depth));
+ if (depthMap != null) scale *= Settings.ENLARGE_IMAGE;
+ float sizeV = scale * height / width;
+
+ rewindBuffers();
+
+ for (int v = 0; v <= resolutionV; ++v) {
+ float vV = v / (float) resolutionV;
+ float v2 = sizeV - 2 * sizeV * vV;
+ int y = (int) (vV * (height - 1));
+ for (int h = 0; h <= resolutionH; ++h) {
+ float vH = h / (float) resolutionH;
+
+ int depthValue = 0;
+ if (depthMap != null) {
+ int x = (int) (vH * (width - 1));
+ depthValue = depthMap.pixels[y * depthMap.width + x];
+ }
+ vertices.put(-scale + 2 * scale * vH);
+ vertices.put(v2);
+ vertices.put(depthValue * Settings.DEPTH_RATIO);
+
+ colors.put(1f);
+ colors.put(1f);
+ colors.put(1f);
+ colors.put(1f);
+
+ textures.put(vH);
+ textures.put(vV);
+ }
+ }
+
+ for (int v = 0; v < resolutionV; ++v) {
+ for (int h = 0; h < resolutionH; ++h) {
+ int index = v * (resolutionH + 1) + h;
+
+ indices.put(index);
+ indices.put(index + (resolutionH + 1));
+ indices.put(index + 1);
+
+ indices.put(index + (resolutionH + 1));
+ indices.put(index + 1 + (resolutionH + 1));
+ indices.put(index + 1);
+ }
+ }
+ rewindBuffers();
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Renderer.java b/src/com/android/gallery3d/app/dualcam3d/gl/Renderer.java
new file mode 100644
index 000000000..c2795f93d
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Renderer.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+public class Renderer implements GLSurfaceView.Renderer {
+ private static final String TAG = "Renderer";
+ public static final float THETA_MAX = (float) Math.toRadians(6);
+ private final float[] mProjectionMatrix = new float[16];
+
+ private float mOffsetX;
+ private float mOffsetY;
+
+ private Bitmap mImageBitmap;
+ private DualCameraNativeEngine.DepthMap3D mDepthMap;
+
+ private boolean mImageChanged;
+ private boolean mDepthMapChanged;
+
+ private RectF mSurfaceRect;
+ private android.graphics.Matrix mImageInvertMatrix;
+
+ private final Mesh mMesh = Mesh.create();
+ private Shader mShader;
+ private final Texture mImageTexture = new Texture();
+
+ public Renderer() {
+ }
+
+ public void setImageBitmap(Bitmap bitmap) {
+ mImageBitmap = bitmap;
+ mImageChanged = true;
+ mDepthMap = null;
+ mDepthMapChanged = true;
+ setImageInvertMatrix();
+ }
+
+ public void set3DEffectDepthMap(DualCameraNativeEngine.DepthMap3D map) {
+ mDepthMap = map;
+ mDepthMapChanged = true;
+ if (map == null) {
+ mOffsetX = 0;
+ mOffsetY = 0;
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ mShader = new Shader();
+ mShader.bind();
+ GLES20.glEnable(GLES20.GL_DEPTH_TEST);
+ GLES20.glEnable(GLES20.GL_BLEND);
+ GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ Log.d(TAG, "w=" + width + "x" + height);
+ float ratio = (float) width / height;
+ float sinY = (float) Math.sin(Math.toRadians(Settings.FIELD_OF_VIEW / 2)) / ratio;
+ float cosY = (float) Math.cos(Math.toRadians(Settings.FIELD_OF_VIEW / 2));
+ float fovY = (float) Math.toDegrees(Math.atan2(sinY, cosY) * 2.f);
+ Matrix.perspectiveM(mProjectionMatrix, 0, fovY, ratio, 0.1f, 500.f);
+ GLES20.glViewport(0, 0, width, height);
+ mSurfaceRect = new RectF(0, 0, width, height);
+ setImageInvertMatrix();
+ }
+
+ private void updateImage() {
+ if (mImageChanged) {
+ mImageTexture.upload(mImageBitmap);
+ mImageTexture.bind();
+ mImageChanged = false;
+ }
+ if (mDepthMapChanged) {
+ int width = mDepthMap == null ? mImageBitmap.getWidth() : mDepthMap.width;
+ int height = mDepthMap == null ? mImageBitmap.getHeight() : mDepthMap.height;
+ mMesh.update(mDepthMap, width, height, Settings.FOREGROUND_POSITION);
+ mDepthMapChanged = false;
+ }
+ }
+
+ private void updateMatrix() {
+ float x = (float) (Math.sin(mOffsetX) * Math.cos(mOffsetY)) * Settings.CAMERA_POSITION;
+ float y = (float) (Math.sin(mOffsetY)) * Settings.CAMERA_POSITION;
+ float z = (float) (Math.cos(mOffsetX) * Math.cos(mOffsetY)) * Settings.CAMERA_POSITION;
+
+ float[] viewMatrix = new float[16];
+ Matrix.setLookAtM(viewMatrix, 0, x, y, z, 0f, 0f, Settings.FOREGROUND_POSITION,
+ 0f, 1f, 0f);
+ float[] matrix = new float[16];
+ Matrix.multiplyMM(matrix, 0, mProjectionMatrix, 0, viewMatrix, 0);
+ mShader.setMatrix(matrix);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ if (mImageBitmap == null) {
+ return;
+ }
+
+ updateImage();
+ updateMatrix();
+
+ GLES20.glClearColor(0, 0, 0, 0);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+ mMesh.render(mShader);
+ }
+
+ public void setOffset(float x, float y) {
+ mOffsetX = limit(x);
+ mOffsetY = limit(y);
+ }
+
+ public void setOffsetDelta(float deltaX, float deltaY) {
+ mOffsetX = limit(mOffsetX + deltaX);
+ mOffsetY = limit(mOffsetY + deltaY);
+ }
+
+ private float limit(float theta) {
+ if (theta < -THETA_MAX) return -THETA_MAX;
+ if (theta > THETA_MAX) return THETA_MAX;
+ return theta;
+ }
+
+ public android.graphics.Matrix getImageInvertMatrix() {
+ return mImageInvertMatrix;
+ }
+
+ private void setImageInvertMatrix() {
+ if (mImageBitmap != null && mSurfaceRect != null) {
+ RectF rect = new RectF(0, 0, mImageBitmap.getWidth(), mImageBitmap.getHeight());
+ if (mImageInvertMatrix == null) {
+ mImageInvertMatrix = new android.graphics.Matrix();
+ }
+ android.graphics.Matrix matrix = new android.graphics.Matrix();
+ matrix.setRectToRect(rect, mSurfaceRect, android.graphics.Matrix.ScaleToFit.CENTER);
+ matrix.invert(mImageInvertMatrix);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Settings.java b/src/com/android/gallery3d/app/dualcam3d/gl/Settings.java
new file mode 100644
index 000000000..5321fdfc5
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Settings.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+class Settings {
+ // Number of horizontal subdivisions (horizontal resolution)
+ static final int MESH_RESOLUTION_H = 300;
+ // Number of vertical subdivisions (vertical resolution)
+ static final int MESH_RESOLUTION_V = 450;
+ // Field of view of the viewport (horizontal)
+ static final float FIELD_OF_VIEW = 60.0f;
+ // Distance of the viewer from the texture
+ static final float CAMERA_POSITION = 2.75f;
+ // Offset between the background and the foreground
+ static final float FOREGROUND_POSITION = 0.0f;
+
+ static final float ENLARGE_IMAGE = 1.0f;
+
+ // Some hard-coded scalar for now
+ static final float DEPTH_RATIO = 0.003f;
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Shader.java b/src/com/android/gallery3d/app/dualcam3d/gl/Shader.java
new file mode 100644
index 000000000..3641b2676
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Shader.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+import android.opengl.GLES20;
+
+class Shader {
+ private static final String vertexShaderCode = //
+ "uniform mat4 uMVPMatrix;" //
+ + "attribute vec4 vPosition;" //
+ + "attribute vec2 vTexCoord;" //
+ + "attribute vec4 vColor;" //
+ + "varying vec2 texCoord;" //
+ + "varying vec4 color;" //
+ + "void main() {" //
+ + " gl_Position = uMVPMatrix * vPosition;" //
+ + " texCoord = vTexCoord;" //
+ + " color = vColor;" //
+ + "}";
+
+ private static final String fragmentShaderCode = //
+ "precision mediump float;" //
+ + "uniform vec4 vColor;" //
+ + "varying vec2 texCoord;" //
+ + "varying vec4 color;" //
+ + "uniform sampler2D texSampler2D;" //
+ + "void main() {" //
+ + " gl_FragColor = texture2D(texSampler2D, texCoord);" //
+ + " gl_FragColor.w = color.w;" //
+ + "}";
+
+ private int shaderId = -1;
+
+ public Shader() {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
+ int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
+
+ shaderId = GLES20.glCreateProgram();
+ Error.check();
+ GLES20.glAttachShader(shaderId, vertexShader);
+ Error.check();
+ GLES20.glAttachShader(shaderId, fragmentShader);
+ Error.check();
+ GLES20.glLinkProgram(shaderId);
+ Error.check();
+ }
+
+ private static int loadShader(int type, String shaderCode) {
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, shaderCode);
+ Error.check();
+ GLES20.glCompileShader(shader);
+ Error.check();
+ return shader;
+ }
+
+ public void bind() {
+ GLES20.glUseProgram(shaderId);
+ }
+
+ public void setMesh(Mesh m) {
+ int vertexHandle = GLES20.glGetAttribLocation(shaderId, "vPosition");
+ GLES20.glEnableVertexAttribArray(vertexHandle);
+ GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT, false, 12, m.vertices);
+
+ int textureHandle = GLES20.glGetAttribLocation(shaderId, "vTexCoord");
+ GLES20.glEnableVertexAttribArray(textureHandle);
+ GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, m.textures);
+
+ int colorHandle = GLES20.glGetAttribLocation(shaderId, "vColor");
+ GLES20.glEnableVertexAttribArray(colorHandle);
+ GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, true, 16, m.colors);
+ }
+
+ public void setMatrix(float[] matrix) {
+ int matrixHandle = GLES20.glGetUniformLocation(shaderId, "uMVPMatrix");
+ GLES20.glUniformMatrix4fv(matrixHandle, 1, false, matrix, 0);
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/gl/Texture.java b/src/com/android/gallery3d/app/dualcam3d/gl/Texture.java
new file mode 100644
index 000000000..e93df8c43
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/gl/Texture.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.gallery3d.app.dualcam3d.gl;
+
+import android.graphics.Bitmap;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+
+class Texture {
+ private final int[] mId = new int[1];
+ private boolean mInitialized = false;
+
+ private void init() {
+ if (!mInitialized) {
+ GLES20.glGenTextures(1, mId, 0);
+ Error.check();
+ mInitialized = true;
+ }
+ }
+
+ public void upload(Bitmap bitmap) {
+ init();
+
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mId[0]);
+ Error.check();
+
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
+ }
+
+ public void bind() {
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mId[0]);
+ Error.check();
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/mpo/Task.java b/src/com/android/gallery3d/app/dualcam3d/mpo/Task.java
new file mode 100644
index 000000000..d449cb82d
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/mpo/Task.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app.dualcam3d.mpo;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine;
+import com.android.gallery3d.mpo.MpoParser;
+
+import java.io.OutputStream;
+
+public class Task {
+ private static final String TAG = "ParseMpoTask";
+
+ public interface Listener {
+ void onBitmapLoaded(Bitmap bitmap, int scale);
+
+ int onScale(int width, int height);
+
+ void onFinish(boolean result);
+ }
+
+ private final Context mContext;
+ private final Uri mUri;
+ private final Listener mListener;
+ private boolean mCancelled;
+
+ private Thread mThread1;
+ private Thread mThread2;
+
+ public Task(final Context context, final Uri uri, final Listener listener) {
+ mContext = context;
+ mUri = uri;
+ mListener = listener;
+ mCancelled = false;
+ }
+
+ public void start(final float brIntensity) {
+ mThread1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ boolean result = parse(brIntensity);
+ mListener.onFinish(result);
+ }
+ });
+ mThread1.start();
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ try {
+ if (mThread1 != null) {
+ mThread1.join();
+ }
+ if (mThread2 != null) {
+ mThread2.join();
+ }
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ private boolean parse(final float brIntensity) {
+ final Bitmap[] bitmaps = new Bitmap[2];
+ final byte[][] data = new byte[2][];
+
+ MpoParser parser = MpoParser.parse(mContext, mUri);
+ if (mCancelled) return false;
+ final boolean primaryForDisplay = parser.isPrimaryForDisplay();
+ if (!primaryForDisplay) {
+ decodeDisplayImg(parser);
+ try {
+ mThread2.join();
+ mThread2 = null;
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ bitmaps[0] = BitmapFactory.decodeByteArray(data[0], 0, data[0].length);
+ data[0] = null;
+ if (!mCancelled && primaryForDisplay && brIntensity == 0) {
+ mListener.onBitmapLoaded(bitmaps[0], 1);
+ }
+ }
+ };
+ data[0] = parser.readImgData(true);
+ if (mCancelled || data[0] == null) return false;
+ if (primaryForDisplay) {
+ mThread2 = new Thread(runnable);
+ mThread2.start();
+ } else {
+ runnable.run();
+ }
+
+ data[1] = parser.readImgData(false);
+ if (mCancelled || data[1] == null) return false;
+ bitmaps[1] = BitmapFactory.decodeByteArray(data[1], 0, data[1].length);
+ data[1] = null;
+ if (bitmaps[1] == null) return false;
+
+ if (primaryForDisplay) {
+ try {
+ mThread2.join();
+ mThread2 = null;
+ } catch (InterruptedException ignored) {
+ }
+ }
+ return loadDepthMap(bitmaps, brIntensity, primaryForDisplay);
+ }
+
+ private void decodeDisplayImg(final MpoParser parser) {
+ mThread2 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ BitmapFactory.Options o = new BitmapFactory.Options();
+ o.inJustDecodeBounds = true;
+ byte[] data = parser.readImgData(0);
+ if (data == null && !mCancelled) {
+ mListener.onBitmapLoaded(null, 1);
+ return;
+ }
+ if (mCancelled) return;
+ BitmapFactory.decodeByteArray(data, 0, data.length, o);
+ o.inSampleSize = mListener.onScale(o.outWidth, o.outHeight);
+ o.inJustDecodeBounds = false;
+ if (mCancelled) return;
+ Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, o);
+ if (mCancelled) return;
+ mListener.onBitmapLoaded(bitmap, o.inSampleSize);
+ }
+ });
+ mThread2.start();
+ }
+
+ private boolean loadDepthMap(Bitmap[] bitmaps, float brIntensity, boolean primaryForDisplay) {
+ // check for pre-generated dm file
+ String mpoFilepath = ImageLoader.getLocalPathFromUri(mContext, mUri);
+ if (mpoFilepath == null) {
+ Log.d(TAG, "Could not get file path from " + mUri);
+ return false;
+ }
+ DualCameraNativeEngine engine = DualCameraNativeEngine.getInstance();
+
+ if (mCancelled) return false;
+ boolean ok = engine.initDepthMap(bitmaps[0], bitmaps[1], mpoFilepath,
+ engine.getCalibFilepath(mContext), brIntensity);
+ bitmaps[1].recycle();
+ if (!ok) return false;
+
+ Point size = new Point();
+ ok = engine.getDepthMapSize(size);
+ if (ok) {
+ Log.d(TAG, "ddm size: " + size.x + "x" + size.y);
+ if (size.x == 0 || size.y == 0) {
+ Log.w(TAG, "invalid ddm size: " + size.x + "x" + size.y);
+ } else {
+ Bitmap depthMap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ALPHA_8);
+ if (engine.getDepthMap(depthMap)) {
+ if (!mCancelled && brIntensity != 0 && primaryForDisplay) {
+ scaleBitmap(bitmaps[0]);
+ }
+ return true;
+ } else {
+ Log.w(TAG, "getDepthMap returned false");
+ }
+ }
+ } else {
+ Log.w(TAG, "getDepthMapSize returned false");
+ }
+ return false;
+ }
+
+ private void save(Bitmap bitmap, String name) {
+ try {
+ OutputStream os = mContext.openFileOutput(name, Context.MODE_PRIVATE);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 95, os);
+ os.close();
+ } catch (Exception ignored) {
+ }
+ }
+
+ private void scaleBitmap(Bitmap bitmap) {
+ int width = bitmap.getWidth(), height = bitmap.getHeight();
+ int scale = mListener.onScale(width, height);
+ width /= scale;
+ height /= scale;
+
+ Bitmap b = Bitmap.createScaledBitmap(bitmap, width, height, false);
+ int[] roi = new int[4];
+ boolean result = DualCameraNativeEngine.getInstance().getPrimaryImg(0, 0, roi, true, b);
+ if (result && roi[2] != width && roi[3] != height) {
+ b = Bitmap.createBitmap(b, roi[0], roi[1], roi[2], roi[3]);
+ }
+ mListener.onBitmapLoaded(b, scale);
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/threed/Controller.java b/src/com/android/gallery3d/app/dualcam3d/threed/Controller.java
new file mode 100644
index 000000000..ff86d2bf5
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/threed/Controller.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app.dualcam3d.threed;
+
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import com.android.gallery3d.app.dualcam3d.GLView;
+import com.android.gallery3d.app.dualcam3d.gl.Renderer;
+
+import org.codeaurora.gallery.R;
+
+
+public class Controller implements Gyro.Listener {
+ private static final String TAG = "Controller";
+ private static final float ANGLE_PER_PIXEL = (float) Math.toRadians(0.03f);
+
+ private final GLView mGLView;
+ private final LinearLayout mModeView;
+ private Auto mAuto;
+ private Gyro mGyro;
+
+ public Controller(GLView glView, LinearLayout modeView) {
+ mGLView = glView;
+ mModeView = modeView;
+ View.OnClickListener listener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ switch (id) {
+ case R.id.mode_gyro:
+ startGyro();
+ break;
+ case R.id.mode_auto:
+ startAuto();
+ break;
+ case R.id.mode_touch:
+ stop(false);
+ break;
+ case R.id.three_dimensional:
+ start();
+ break;
+ }
+ }
+ };
+ for (int i = modeView.getChildCount() - 1; i >= 0; --i) {
+ ImageButton b = (ImageButton) modeView.getChildAt(i);
+ b.setOnClickListener(listener);
+ }
+ }
+
+ private boolean startGyro() {
+ stop(false);
+ if (mGyro == null) {
+ mGyro = new Gyro(mGLView.getContext());
+ if (mGyro.start()) {
+ mGyro.setListener(this);
+ return true;
+ } else {
+ mGyro = null;
+ mModeView.findViewById(R.id.mode_gyro).setEnabled(false);
+ }
+ }
+ return false;
+ }
+
+ private void startAuto() {
+ stop(false);
+ if (mAuto == null) {
+ mAuto = new Auto();
+ mAuto.start(mGLView);
+ }
+ }
+
+ public void start() {
+ if (!startGyro()) {
+ startAuto();
+ }
+// mModeView.setVisibility(View.VISIBLE);
+ }
+
+ public void stop(boolean hide) {
+ if (mGyro != null) {
+ mGyro.stop();
+ mGyro = null;
+ }
+ if (mAuto != null) {
+ mAuto.stop();
+ mAuto = null;
+ }
+// if (hide) {
+// mModeView.setVisibility(View.INVISIBLE);
+// }
+ }
+
+ @Override
+ public void onGyroChanged(float thetaX, float thetaY) {
+ mGLView.setOffsetWithRotation(thetaX, thetaY);
+ }
+
+ public void onMove(float deltaX, float deltaY) {
+ stop(false);
+ mGLView.setOffsetDelta(deltaX * ANGLE_PER_PIXEL, deltaY * ANGLE_PER_PIXEL);
+ }
+
+ private static class Auto {
+ private Thread mThread;
+ private boolean mStop;
+
+ public void start(final GLView glView) {
+ mStop = false;
+ mThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ float x = 0, y = 0, speed = 0.003f, angle = 0.87f;
+ double deltaX = angle * speed;
+ double deltaY = Math.sqrt(speed * speed - deltaX * deltaX);
+
+ while (!mStop) {
+ x += deltaX;
+ y += deltaY;
+ glView.setOffset(x, y);
+
+ if (x >= Renderer.THETA_MAX || x <= -Renderer.THETA_MAX) {
+ deltaX = -deltaX;
+ }
+ if (y >= Renderer.THETA_MAX || y <= -Renderer.THETA_MAX) {
+ deltaY = -deltaY;
+ }
+ try {
+ Thread.sleep(15);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ });
+ mThread.start();
+ }
+
+ public void stop() {
+ mStop = true;
+ if (mThread != null) {
+ try {
+ mThread.join();
+ mThread = null;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/app/dualcam3d/threed/Gyro.java b/src/com/android/gallery3d/app/dualcam3d/threed/Gyro.java
new file mode 100644
index 000000000..bb1d1c1de
--- /dev/null
+++ b/src/com/android/gallery3d/app/dualcam3d/threed/Gyro.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.gallery3d.app.dualcam3d.threed;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+class Gyro implements SensorEventListener {
+ private final SensorManager mSensorManager;
+ private final Sensor mGyro;
+
+ private static final float ALPHA = 0.4f;
+ private static final float ALPHA_ORIGIN = 0.95f;
+
+ private long mPrevTimestamp;
+ private float mPrevThetaX;
+ private float mPrevThetaY;
+ private float mPrevOriginX;
+ private float mPrevOriginY;
+ private float mPrevOmegaX;
+ private float mPrevOmegaY;
+
+ private Listener mListener;
+
+ interface Listener {
+ void onGyroChanged(float thetaX, float thetaY);
+ }
+
+ Gyro(Context context) {
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mGyro = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
+ }
+
+ public boolean start() {
+ return mGyro != null
+ && mSensorManager.registerListener(this, mGyro, SensorManager.SENSOR_DELAY_UI);
+ }
+
+ public void stop() {
+ mSensorManager.unregisterListener(this);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
+ float omegaX = event.values[0];
+ float omegaY = event.values[1];
+ update(omegaX, omegaY, event.timestamp);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ private void update(float omegaX, float omegaY, long timestamp) {
+ if (mPrevTimestamp == 0) {
+ mPrevTimestamp = timestamp;
+ return;
+ }
+ float deltaTimestamp = (float) (timestamp - mPrevTimestamp) / 1000_000_000;
+
+ float thetaX = mPrevThetaX + 0.5f * (mPrevOmegaX + omegaX) * deltaTimestamp;
+ float thetaY = mPrevThetaY + 0.5f * (mPrevOmegaY + omegaY) * deltaTimestamp;
+ float smoothThetaX = ALPHA * mPrevThetaX + ((1.0f - ALPHA) * thetaX);
+ float smoothThetaY = ALPHA * mPrevThetaY + ((1.0f - ALPHA) * thetaY);
+
+ mPrevOriginX = ALPHA_ORIGIN * mPrevOriginX + (1 - ALPHA_ORIGIN) * smoothThetaX;
+ mPrevOriginY = ALPHA_ORIGIN * mPrevOriginY + (1 - ALPHA_ORIGIN) * smoothThetaY;
+
+ mPrevTimestamp = timestamp;
+ mPrevOmegaX = omegaX;
+ mPrevOmegaY = omegaY;
+ mPrevThetaX = smoothThetaX;
+ mPrevThetaY = smoothThetaY;
+
+ if (mListener != null) {
+ mListener.onGyroChanged(smoothThetaX - mPrevOriginX, smoothThetaY - mPrevOriginY);
+ }
+ }
+
+ void setListener(Listener listener) {
+ mListener = listener;
+ }
+}