diff options
author | jinwu <jinwu@codeaurora.org> | 2017-04-07 13:27:48 +0800 |
---|---|---|
committer | jinwu <jinwu@codeaurora.org> | 2017-04-10 11:24:45 +0800 |
commit | 4fafa7a04ab78f91bbbf59c5b722a84a9b62f85c (patch) | |
tree | fddbf3ccafbd5a8a4be79775a0b22baa30ad848e /src/com/android/gallery3d/app | |
parent | 348658059575aa7a3bd722a1f9b06d0683225e9c (diff) | |
download | android_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')
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; + } +} |