diff options
49 files changed, 1355 insertions, 960 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/common/BlobCache.java b/gallerycommon/src/com/android/gallery3d/common/BlobCache.java index 7788e61f5..3c131e591 100644 --- a/gallerycommon/src/com/android/gallery3d/common/BlobCache.java +++ b/gallerycommon/src/com/android/gallery3d/common/BlobCache.java @@ -74,6 +74,7 @@ import java.io.RandomAccessFile; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.util.Arrays; import java.util.zip.Adler32; public class BlobCache implements Closeable { @@ -379,6 +380,16 @@ public class BlobCache implements Closeable { updateIndexHeader(); } + public void clearEntry(long key) throws IOException { + if (!lookupInternal(key, mActiveHashStart)) { + return; // Nothing to clear + } + byte[] header = mBlobHeader; + Arrays.fill(header, (byte) 0); + mActiveDataFile.seek(mFileOffset); + mActiveDataFile.write(header); + } + // Appends the data to the active file. It also updates the hash entry. // The proper hash entry (suitable for insertion or replacement) must be // pointed by mSlotOffset. @@ -485,6 +496,9 @@ public class BlobCache implements Closeable { return false; } long blobKey = readLong(header, BH_KEY); + if (blobKey == 0) { + return false; // This entry has been cleared. + } if (blobKey != req.key) { Log.w(TAG, "blob key does not match: " + blobKey); return false; diff --git a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java b/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java index cada234b3..115dc6625 100644 --- a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java +++ b/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java @@ -81,8 +81,12 @@ public class ThreadPool { private final Executor mExecutor; public ThreadPool() { + this(CORE_POOL_SIZE, MAX_POOL_SIZE); + } + + public ThreadPool(int initPoolSize, int maxPoolSize) { mExecutor = new ThreadPoolExecutor( - CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, + initPoolSize, maxPoolSize, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory("thread-pool", android.os.Process.THREAD_PRIORITY_BACKGROUND)); diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java index 5e779cd59..cb3aa9d0a 100644 --- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java +++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java @@ -84,6 +84,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); mStateManager.onConfigurationChange(config); + getGalleryActionBar().onConfigurationChanged(); invalidateOptionsMenu(); toggleStatusBarByOrientation(); } diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java index 1719b89f9..2cb1ca2fa 100644 --- a/src/com/android/gallery3d/app/AlbumSetPage.java +++ b/src/com/android/gallery3d/app/AlbumSetPage.java @@ -365,6 +365,7 @@ public class AlbumSetPage extends ActivityState implements } private boolean setupCameraButton() { + if (!GalleryUtils.isCameraAvailable(mActivity)) return false; RelativeLayout galleryRoot = (RelativeLayout) ((Activity) mActivity) .findViewById(R.id.gallery_root); if (galleryRoot == null) return false; diff --git a/src/com/android/gallery3d/app/Config.java b/src/com/android/gallery3d/app/Config.java index 04b210e0e..7183acc33 100644 --- a/src/com/android/gallery3d/app/Config.java +++ b/src/com/android/gallery3d/app/Config.java @@ -49,8 +49,7 @@ final class Config { slotViewSpec.rowsLand = r.getInteger(R.integer.albumset_rows_land); slotViewSpec.rowsPort = r.getInteger(R.integer.albumset_rows_port); slotViewSpec.slotGap = r.getDimensionPixelSize(R.dimen.albumset_slot_gap); - slotViewSpec.slotHeightAdditional = r.getDimensionPixelSize( - R.dimen.albumset_label_background_height); + slotViewSpec.slotHeightAdditional = 0; paddingTop = r.getDimensionPixelSize(R.dimen.albumset_padding_top); paddingBottom = r.getDimensionPixelSize(R.dimen.albumset_padding_bottom); diff --git a/src/com/android/gallery3d/app/GalleryActionBar.java b/src/com/android/gallery3d/app/GalleryActionBar.java index 1729c6433..f0be01a91 100644 --- a/src/com/android/gallery3d/app/GalleryActionBar.java +++ b/src/com/android/gallery3d/app/GalleryActionBar.java @@ -57,6 +57,7 @@ public class GalleryActionBar implements OnNavigationListener { private AlbumModeAdapter mAlbumModeAdapter; private OnAlbumModeSelectedListener mAlbumModeListener; + private int mLastAlbumModeSelected; private CharSequence [] mAlbumModes; public static final int ALBUM_FILMSTRIP_MODE_SELECTED = 0; public static final int ALBUM_GRID_MODE_SELECTED = 1; @@ -254,6 +255,13 @@ public class GalleryActionBar implements OnNavigationListener { } } + public void onConfigurationChanged() { + if (mActionBar != null && mAlbumModeListener != null) { + OnAlbumModeSelectedListener listener = mAlbumModeListener; + enableAlbumModeMenu(mLastAlbumModeSelected, listener); + } + } + public void enableAlbumModeMenu(int selected, OnAlbumModeSelectedListener listener) { if (mActionBar != null) { if (mAlbumModeAdapter == null) { @@ -265,6 +273,7 @@ public class GalleryActionBar implements OnNavigationListener { mAlbumModeAdapter = new AlbumModeAdapter(); } mAlbumModeListener = null; + mLastAlbumModeSelected = selected; mActionBar.setListNavigationCallbacks(mAlbumModeAdapter, this); mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); mActionBar.setSelectedNavigationItem(selected); diff --git a/src/com/android/gallery3d/app/OrientationManager.java b/src/com/android/gallery3d/app/OrientationManager.java index a8ef99ad8..0e033ebe4 100644 --- a/src/com/android/gallery3d/app/OrientationManager.java +++ b/src/com/android/gallery3d/app/OrientationManager.java @@ -98,8 +98,9 @@ public class OrientationManager implements OrientationSource { public void lockOrientation() { if (mOrientationLocked) return; mOrientationLocked = true; + int displayRotation = getDisplayRotation(); // Display rotation >= 180 means we need to use the REVERSE landscape/portrait - boolean standard = getDisplayRotation() < 180; + boolean standard = displayRotation < 180; if (mActivity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { Log.d(TAG, "lock orientation to landscape"); @@ -107,6 +108,13 @@ public class OrientationManager implements OrientationSource { ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); } else { + if (displayRotation == 90 || displayRotation == 270) { + // If displayRotation = 90 or 270 then we are on a landscape + // device. On landscape devices, portrait is a 90 degree + // clockwise rotation from landscape, so we need + // to flip which portrait we pick as display rotation is counter clockwise + standard = !standard; + } Log.d(TAG, "lock orientation to portrait"); mActivity.setRequestedOrientation(standard ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java index 2e19a2ba8..b11229f88 100644 --- a/src/com/android/gallery3d/app/PhotoPage.java +++ b/src/com/android/gallery3d/app/PhotoPage.java @@ -50,6 +50,7 @@ import com.android.gallery3d.data.LocalImage; import com.android.gallery3d.data.MediaDetails; import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; +import com.android.gallery3d.data.MediaObject.SupportedOperationsListener; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.MtpSource; import com.android.gallery3d.data.Path; @@ -93,6 +94,9 @@ public class PhotoPage extends ActivityState implements private static final int MSG_ON_CAMERA_CENTER = 9; private static final int MSG_ON_PICTURE_CENTER = 10; private static final int MSG_REFRESH_IMAGE = 11; + private static final int MSG_UPDATE_PHOTO_UI = 12; + private static final int MSG_UPDATE_PROGRESS = 13; + private static final int MSG_UPDATE_DEFERRED = 14; private static final int HIDE_BARS_TIMEOUT = 3500; private static final int UNFREEZE_GLROOT_TIMEOUT = 250; @@ -169,6 +173,10 @@ public class PhotoPage extends ActivityState implements private boolean mSkipUpdateCurrentPhoto = false; private static final long CAMERA_SWITCH_CUTOFF_THRESHOLD_MS = 300; + private static final long DEFERRED_UPDATE_MS = 150; + private boolean mDeferredUpdateWaiting = false; + private long mDeferUpdateUntil = Long.MAX_VALUE; + private RawTexture mFadeOutTexture; private Rect mOpenAnimationRect; public static final int ANIM_TIME_OPENING = 300; @@ -183,6 +191,21 @@ public class PhotoPage extends ActivityState implements new MyMenuVisibilityListener(); private UpdateProgressListener mProgressListener; + private SupportedOperationsListener mSupportedOperationsListener = + new SupportedOperationsListener() { + @Override + public void onChange(MediaObject item, int operations) { + if (item == mCurrentPhoto) { + if (mPhotoView.getFilmMode() + && SystemClock.uptimeMillis() < mDeferUpdateUntil) { + requestDeferredUpdate(); + } else { + mHandler.sendEmptyMessage(MSG_UPDATE_PHOTO_UI); + } + } + } + }; + public static interface Model extends PhotoView.Model { public void resume(); public void pause(); @@ -209,24 +232,24 @@ public class PhotoPage extends ActivityState implements @Override public void onStitchingResult(Uri uri) { - sendUpdate(uri); + sendUpdate(uri, MSG_REFRESH_IMAGE); } @Override public void onStitchingQueued(Uri uri) { - sendUpdate(uri); + sendUpdate(uri, MSG_UPDATE_PROGRESS); } @Override public void onStitchingProgress(Uri uri, final int progress) { - sendUpdate(uri); + sendUpdate(uri, MSG_UPDATE_PROGRESS); } - private void sendUpdate(Uri uri) { + private void sendUpdate(Uri uri, int message) { boolean isCurrentPhoto = mCurrentPhoto instanceof LocalImage && mCurrentPhoto.getContentUri().equals(uri); if (isCurrentPhoto) { - mHandler.sendEmptyMessage(MSG_REFRESH_IMAGE); + mHandler.sendEmptyMessage(message); } } }; @@ -314,6 +337,16 @@ public class PhotoPage extends ActivityState implements mActivity.getGLRoot().unfreeze(); break; } + case MSG_UPDATE_DEFERRED: { + long nextUpdate = mDeferUpdateUntil - SystemClock.uptimeMillis(); + if (nextUpdate <= 0) { + mDeferredUpdateWaiting = false; + updateUIForCurrentPhoto(); + } else { + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, nextUpdate); + } + break; + } case MSG_ON_CAMERA_CENTER: { mSkipUpdateCurrentPhoto = false; boolean stayedOnCamera = false; @@ -334,16 +367,24 @@ public class PhotoPage extends ActivityState implements break; } case MSG_ON_PICTURE_CENTER: { - if (mCurrentPhoto != null + if (!mPhotoView.getFilmMode() && mCurrentPhoto != null && (mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0) { mPhotoView.setFilmMode(true); } break; } case MSG_REFRESH_IMAGE: { - MediaItem currentPhoto = mCurrentPhoto; + final MediaItem photo = mCurrentPhoto; mCurrentPhoto = null; - updateCurrentPhoto(currentPhoto); + updateCurrentPhoto(photo); + break; + } + case MSG_UPDATE_PHOTO_UI: { + updateUIForCurrentPhoto(); + break; + } + case MSG_UPDATE_PROGRESS: { + updateProgressBar(); break; } default: throw new AssertionError(message.what); @@ -452,7 +493,7 @@ public class PhotoPage extends ActivityState implements int oldIndex = mCurrentIndex; mCurrentIndex = index; - if (mAppBridge != null) { + if (mInCameraRoll) { if (mCurrentIndex > 0) { mSkipUpdateCurrentPhoto = false; } @@ -521,6 +562,9 @@ public class PhotoPage extends ActivityState implements mProgressBar = new PhotoPageProgressBar(mActivity, galleryRoot); mProgressListener = new UpdateProgressListener(); progressManager.addChangeListener(mProgressListener); + if (mSecureAlbum != null) { + progressManager.addChangeListener(mSecureAlbum); + } } } } @@ -639,15 +683,21 @@ public class PhotoPage extends ActivityState implements setNfcBeamPushUri(uri); } - private void updateCurrentPhoto(MediaItem photo) { - if (mCurrentPhoto == photo) return; - mCurrentPhoto = photo; + private void requestDeferredUpdate() { + mDeferUpdateUntil = SystemClock.uptimeMillis() + DEFERRED_UPDATE_MS; + if (!mDeferredUpdateWaiting) { + mDeferredUpdateWaiting = true; + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_DEFERRED, DEFERRED_UPDATE_MS); + } + } + + private void updateUIForCurrentPhoto() { if (mCurrentPhoto == null) return; // If by swiping or deletion the user ends up on an action item // and zoomed in, zoom out so that the context of the action is // more clear - if ((photo.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0 + if ((mCurrentPhoto.getSupportedOperations() & MediaObject.SUPPORT_ACTION) != 0 && !mPhotoView.getFilmMode()) { mPhotoView.setWantPictureCenterCallbacks(true); } @@ -658,14 +708,33 @@ public class PhotoPage extends ActivityState implements mDetailsHelper.reloadDetails(); } if ((mSecureAlbum == null) - && (photo.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { - updateShareURI(photo.getPath()); + && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_SHARE) != 0) { + updateShareURI(mCurrentPhoto.getPath()); } + updateProgressBar(); + } + + private void updateCurrentPhoto(MediaItem photo) { + if (mCurrentPhoto == photo) return; + if (mCurrentPhoto != null) { + mCurrentPhoto.setSupportedOperationsListener(null); + } + mCurrentPhoto = photo; + mCurrentPhoto.setSupportedOperationsListener( + mSupportedOperationsListener); + if (mPhotoView.getFilmMode()) { + requestDeferredUpdate(); + } else { + updateUIForCurrentPhoto(); + } + } + + private void updateProgressBar() { if (mProgressBar != null) { mProgressBar.hideProgress(); StitchingProgressManager progressManager = mApplication.getStitchingProgressManager(); if (progressManager != null && mCurrentPhoto instanceof LocalImage) { - Integer progress = progressManager.getProgress(photo.getContentUri()); + Integer progress = progressManager.getProgress(mCurrentPhoto.getContentUri()); if (progress != null) { mProgressBar.setProgress(progress); } @@ -695,14 +764,18 @@ public class PhotoPage extends ActivityState implements if ((supportedOperations & MediaObject.SUPPORT_PANORAMA360) != 0) { mActivity.invalidateOptionsMenu(); item = menu.findItem(R.id.action_share); - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - item.setTitle( - mActivity.getResources().getString(R.string.share_as_photo)); + if (item != null) { + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + item.setTitle( + mActivity.getResources().getString(R.string.share_as_photo)); + } } else if ((supportedOperations & MediaObject.SUPPORT_SHARE) != 0) { item = menu.findItem(R.id.action_share); - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - item.setTitle( - mActivity.getResources().getString(R.string.share)); + if (item != null) { + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + item.setTitle( + mActivity.getResources().getString(R.string.share)); + } } } diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java index 95954e59a..3d2c0c2f0 100644 --- a/src/com/android/gallery3d/data/DataManager.java +++ b/src/com/android/gallery3d/data/DataManager.java @@ -250,6 +250,12 @@ public class DataManager { return getMediaObject(path).getSupportedOperations(); } + // getAll will cause this call to wait if any of the operations + // are expensive to compute. Do not call in UI thread. + public int getSupportedOperations(Path path, boolean getAll) { + return getMediaObject(path).getSupportedOperations(getAll); + } + public void delete(Path path) { getMediaObject(path).delete(); } diff --git a/src/com/android/gallery3d/data/ImageCacheService.java b/src/com/android/gallery3d/data/ImageCacheService.java index 38e32cbee..d42e9521d 100644 --- a/src/com/android/gallery3d/data/ImageCacheService.java +++ b/src/com/android/gallery3d/data/ImageCacheService.java @@ -91,6 +91,18 @@ public class ImageCacheService { } } + public void clearImageData(Path path, int type) { + byte[] key = makeKey(path, type); + long cacheKey = Utils.crc64Long(key); + synchronized (mCache) { + try { + mCache.clearEntry(cacheKey); + } catch (IOException ex) { + // ignore. + } + } + } + private static byte[] makeKey(Path path, int type) { return GalleryUtils.getBytes(path.toString() + "+" + type); } diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java index 5d6d39dcf..2ba784de7 100644 --- a/src/com/android/gallery3d/data/LocalImage.java +++ b/src/com/android/gallery3d/data/LocalImage.java @@ -36,8 +36,11 @@ import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.app.StitchingProgressManager; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.util.Future; +import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.LightCycleHelper; +import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata; import com.android.gallery3d.util.ThreadPool.Job; import com.android.gallery3d.util.ThreadPool.JobContext; import com.android.gallery3d.util.UpdateHelper; @@ -101,11 +104,11 @@ public class LocalImage extends LocalMediaItem { public int rotation; - private boolean mUsePanoramaViewer; - private boolean mUsePanoramaViewerInitialized; - - private boolean mIsPanorama360; - private boolean mIsPanorama360Initialized; + private Object mLock = new Object(); + private Future<PanoramaMetadata> mGetPanoMetadataTask; + private boolean mPanoramaMetadataInitialized; + private PanoramaMetadata mPanoramaMetadata; + private SupportedOperationsListener mListener; public LocalImage(Path path, GalleryApp application, Cursor cursor) { super(path, nextVersionNumber()); @@ -255,9 +258,9 @@ public class LocalImage extends LocalMediaItem { operation |= SUPPORT_SHOW_ON_MAP; } - if (usePanoramaViewer()) { + if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) { operation |= SUPPORT_PANORAMA; - if (isPanorama360()) { + if (mPanoramaMetadata.mIsPanorama360) { operation |= SUPPORT_PANORAMA360; // disable destructive rotate and crop for 360 degree panorama operation &= ~(SUPPORT_ROTATE | SUPPORT_CROP); @@ -267,6 +270,53 @@ public class LocalImage extends LocalMediaItem { } @Override + public int getSupportedOperations(boolean getAll) { + synchronized (mLock) { + if (getAll && !mPanoramaMetadataInitialized) { + if (mGetPanoMetadataTask == null) { + mGetPanoMetadataTask = getThreadPool().submit( + new PanoramaMetadataJob(mApplication.getAndroidContext(), + getContentUri())); + } + mPanoramaMetadata = mGetPanoMetadataTask.get(); + mPanoramaMetadataInitialized = true; + } + } + return getSupportedOperations(); + } + + @Override + public void setSupportedOperationsListener(SupportedOperationsListener l) { + synchronized (mLock) { + if (l == null) { + if (mGetPanoMetadataTask != null) { + mGetPanoMetadataTask.cancel(); + mGetPanoMetadataTask = null; + } + } else { + if (mGetPanoMetadataTask == null) { + mGetPanoMetadataTask = getThreadPool().submit( + new PanoramaMetadataJob(mApplication.getAndroidContext(), + getContentUri()), + new FutureListener<PanoramaMetadata>() { + @Override + public void onFutureDone(Future<PanoramaMetadata> future) { + mGetPanoMetadataTask = null; + if (future.isCancelled()) return; + mPanoramaMetadata = future.get(); + mPanoramaMetadataInitialized = true; + if (mListener != null) { + mListener.onChange(LocalImage.this, getSupportedOperations()); + } + } + }); + } + } + mListener = l; + } + } + + @Override public void delete() { GalleryUtils.assertNotInRenderThread(); Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; @@ -360,27 +410,4 @@ public class LocalImage extends LocalMediaItem { public String getFilePath() { return filePath; } - - @Override - public boolean usePanoramaViewer() { - if (!mUsePanoramaViewerInitialized) { - Context context = mApplication.getAndroidContext(); - mUsePanoramaViewer = LightCycleHelper.hasLightCycleView(context) - && LightCycleHelper.isPanorama(mApplication.getContentResolver(), - getContentUri()); - mUsePanoramaViewerInitialized = true; - } - return mUsePanoramaViewer; - } - - @Override - public boolean isPanorama360() { - // cache flag for faster access - if (!mIsPanorama360Initialized) { - mIsPanorama360 = LightCycleHelper.isPanorama360( - mApplication.getAndroidContext(), getContentUri()); - mIsPanorama360Initialized = true; - } - return mIsPanorama360; - } } diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java index da59abeef..19084d41e 100644 --- a/src/com/android/gallery3d/data/MediaItem.java +++ b/src/com/android/gallery3d/data/MediaItem.java @@ -103,14 +103,6 @@ public abstract class MediaItem extends MediaObject { return ""; } - public boolean usePanoramaViewer() { - return false; - } - - public boolean isPanorama360() { - return false; - } - // Returns width and height of the media item. // Returns 0, 0 if the information is not available. public abstract int getWidth(); diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java index a16b9666d..14cd5242a 100644 --- a/src/com/android/gallery3d/data/MediaObject.java +++ b/src/com/android/gallery3d/data/MediaObject.java @@ -18,6 +18,8 @@ package com.android.gallery3d.data; import android.net.Uri; +import com.android.gallery3d.util.ThreadPool; + public abstract class MediaObject { @SuppressWarnings("unused") private static final String TAG = "MediaObject"; @@ -37,14 +39,20 @@ public abstract class MediaObject { public static final int SUPPORT_INFO = 1 << 10; public static final int SUPPORT_IMPORT = 1 << 11; public static final int SUPPORT_TRIM = 1 << 12; - public static final int SUPPORT_PANORAMA = 1 << 13; - public static final int SUPPORT_PANORAMA360 = 1 << 14; - public static final int SUPPORT_UNLOCK = 1 << 15; - public static final int SUPPORT_BACK = 1 << 16; - public static final int SUPPORT_ACTION = 1 << 17; - public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 18; + public static final int SUPPORT_UNLOCK = 1 << 13; + public static final int SUPPORT_BACK = 1 << 14; + public static final int SUPPORT_ACTION = 1 << 15; + public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 16; + // The panorama specific bits are expensive to compute. + // Use SupportedOperationsListener to request them. + public static final int SUPPORT_PANORAMA = 1 << 30; + public static final int SUPPORT_PANORAMA360 = 1 << 31; public static final int SUPPORT_ALL = 0xffffffff; + public static interface SupportedOperationsListener { + public void onChange(MediaObject item, int operations); + } + // These are the bits returned from getMediaType(): public static final int MEDIA_TYPE_UNKNOWN = 1; public static final int MEDIA_TYPE_IMAGE = 2; @@ -72,6 +80,11 @@ public abstract class MediaObject { protected final Path mPath; + private static ThreadPool sThreadPool = new ThreadPool(1, 1); + public static ThreadPool getThreadPool() { + return sThreadPool; + } + public MediaObject(Path path, long version) { path.setObject(this); mPath = path; @@ -86,6 +99,14 @@ public abstract class MediaObject { return 0; } + public int getSupportedOperations(boolean getAll) { + return getSupportedOperations(); + } + + public void setSupportedOperationsListener(SupportedOperationsListener l) { + // nothing to do + } + public void delete() { throw new UnsupportedOperationException(); } diff --git a/src/com/android/gallery3d/data/PanoramaMetadataJob.java b/src/com/android/gallery3d/data/PanoramaMetadataJob.java new file mode 100644 index 000000000..e0a69c41f --- /dev/null +++ b/src/com/android/gallery3d/data/PanoramaMetadataJob.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.data; + +import android.content.Context; +import android.net.Uri; + +import com.android.gallery3d.util.LightCycleHelper; +import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata; +import com.android.gallery3d.util.ThreadPool.Job; +import com.android.gallery3d.util.ThreadPool.JobContext; + +class PanoramaMetadataJob implements Job<PanoramaMetadata> { + Context mContext; + Uri mUri; + + public PanoramaMetadataJob(Context context, Uri uri) { + mContext = context; + mUri = uri; + } + + @Override + public PanoramaMetadata run(JobContext jc) { + return LightCycleHelper.getPanoramaMetadata(mContext, mUri); + } +} diff --git a/src/com/android/gallery3d/data/SecureAlbum.java b/src/com/android/gallery3d/data/SecureAlbum.java index c666bdc75..0a8c5a827 100644 --- a/src/com/android/gallery3d/data/SecureAlbum.java +++ b/src/com/android/gallery3d/data/SecureAlbum.java @@ -24,12 +24,13 @@ import android.provider.MediaStore.MediaColumns; import android.provider.MediaStore.Video; import com.android.gallery3d.app.GalleryApp; +import com.android.gallery3d.app.StitchingChangeListener; import com.android.gallery3d.util.MediaSetUtils; import java.util.ArrayList; // This class lists all media items added by the client. -public class SecureAlbum extends MediaSet { +public class SecureAlbum extends MediaSet implements StitchingChangeListener { @SuppressWarnings("unused") private static final String TAG = "SecureAlbum"; private static final String[] PROJECTION = {MediaColumns._ID}; @@ -183,4 +184,18 @@ public class SecureAlbum extends MediaSet { public boolean isLeafAlbum() { return true; } + + @Override + public void onStitchingQueued(Uri uri) { + int id = Integer.parseInt(uri.getLastPathSegment()); + addMediaItem(false, id); + } + + @Override + public void onStitchingResult(Uri uri) { + } + + @Override + public void onStitchingProgress(Uri uri, final int progress) { + } } diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java index b2cbc7d68..aaa36a917 100644 --- a/src/com/android/gallery3d/data/UriImage.java +++ b/src/com/android/gallery3d/data/UriImage.java @@ -28,7 +28,10 @@ import android.os.ParcelFileDescriptor; import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.common.BitmapUtils; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.util.Future; +import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.LightCycleHelper; +import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata; import com.android.gallery3d.util.ThreadPool.CancelListener; import com.android.gallery3d.util.ThreadPool.Job; import com.android.gallery3d.util.ThreadPool.JobContext; @@ -56,10 +59,12 @@ public class UriImage extends MediaItem { private int mWidth; private int mHeight; private int mRotation; - private boolean mUsePanoramaViewer; - private boolean mUsePanoramaViewerInitialized; - private boolean mIsPanorama360; - private boolean mIsPanorama360Initialized; + + private Object mLock = new Object(); + private Future<PanoramaMetadata> mGetPanoMetadataTask; + private boolean mPanoramaMetadataInitialized; + private PanoramaMetadata mPanoramaMetadata; + private SupportedOperationsListener mListener; private GalleryApp mApplication; @@ -220,9 +225,9 @@ public class UriImage extends MediaItem { if (BitmapUtils.isSupportedByRegionDecoder(mContentType)) { supported |= SUPPORT_FULL_IMAGE; } - if (usePanoramaViewer()) { + if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) { supported |= SUPPORT_PANORAMA; - if (isPanorama360()) { + if (mPanoramaMetadata.mIsPanorama360) { supported |= SUPPORT_PANORAMA360; // disable destructive crop for 360 degree panorama supported &= ~SUPPORT_CROP; @@ -231,6 +236,53 @@ public class UriImage extends MediaItem { return supported; } + @Override + public int getSupportedOperations(boolean getAll) { + synchronized (mLock) { + if (getAll && !mPanoramaMetadataInitialized) { + if (mGetPanoMetadataTask == null) { + mGetPanoMetadataTask = getThreadPool().submit( + new PanoramaMetadataJob(mApplication.getAndroidContext(), + getContentUri())); + } + mPanoramaMetadata = mGetPanoMetadataTask.get(); + mPanoramaMetadataInitialized = true; + } + } + return getSupportedOperations(); + } + + @Override + public void setSupportedOperationsListener(SupportedOperationsListener l) { + synchronized (mLock) { + if (l != null) { + if (mGetPanoMetadataTask != null) { + mGetPanoMetadataTask.cancel(); + mGetPanoMetadataTask = null; + } + } else { + if (mGetPanoMetadataTask == null) { + mGetPanoMetadataTask = getThreadPool().submit( + new PanoramaMetadataJob(mApplication.getAndroidContext(), + getContentUri()), + new FutureListener<PanoramaMetadata>() { + @Override + public void onFutureDone(Future<PanoramaMetadata> future) { + mGetPanoMetadataTask = null; + if (future.isCancelled()) return; + mPanoramaMetadata = future.get(); + mPanoramaMetadataInitialized = true; + if (mListener != null) { + mListener.onChange(UriImage.this, getSupportedOperations()); + } + } + }); + } + } + mListener = l; + } + } + private boolean isSharable() { // We cannot grant read permission to the receiver since we put // the data URI in EXTRA_STREAM instead of the data part of an intent @@ -297,27 +349,4 @@ public class UriImage extends MediaItem { public int getRotation() { return mRotation; } - - @Override - public boolean usePanoramaViewer() { - if (!mUsePanoramaViewerInitialized) { - Context context = mApplication.getAndroidContext(); - mUsePanoramaViewer = LightCycleHelper.hasLightCycleView(context) - && LightCycleHelper.isPanorama(mApplication.getContentResolver(), - getContentUri()); - mUsePanoramaViewerInitialized = true; - } - return mUsePanoramaViewer; - } - - @Override - public boolean isPanorama360() { - // cache flag for faster access - if (!mIsPanorama360Initialized) { - mIsPanorama360 = LightCycleHelper.isPanorama360( - mApplication.getAndroidContext(), getContentUri()); - mIsPanorama360Initialized = true; - } - return mIsPanorama360; - } } diff --git a/src/com/android/gallery3d/filtershow/CenteredLinearLayout.java b/src/com/android/gallery3d/filtershow/CenteredLinearLayout.java new file mode 100644 index 000000000..16b969535 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/CenteredLinearLayout.java @@ -0,0 +1,36 @@ +package com.android.gallery3d.filtershow; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View.MeasureSpec; +import android.widget.LinearLayout; + +import com.android.gallery3d.R; + +public class CenteredLinearLayout extends LinearLayout { + private final int mMaxWidth; + + public CenteredLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CenteredLinearLayout); + mMaxWidth = a.getDimensionPixelSize(R.styleable.CenteredLinearLayout_max_width, 0); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int parentWidth = MeasureSpec.getSize(widthMeasureSpec); + int parentHeight = MeasureSpec.getSize(heightMeasureSpec); + Resources r = getContext().getResources(); + float value = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, parentWidth, + r.getDisplayMetrics()); + if (mMaxWidth > 0 && parentWidth > mMaxWidth) { + int measureMode = MeasureSpec.getMode(widthMeasureSpec); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, measureMode); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + +} diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index f204e4020..fbec411e3 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -11,17 +11,18 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; +import android.view.Display; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageButton; @@ -35,8 +36,17 @@ import com.android.gallery3d.R; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.filters.ImageFilterBorder; +import com.android.gallery3d.filtershow.filters.ImageFilterContrast; +import com.android.gallery3d.filtershow.filters.ImageFilterExposure; +import com.android.gallery3d.filtershow.filters.ImageFilterFx; +import com.android.gallery3d.filtershow.filters.ImageFilterHue; import com.android.gallery3d.filtershow.filters.ImageFilterParametricBorder; import com.android.gallery3d.filtershow.filters.ImageFilterRS; +import com.android.gallery3d.filtershow.filters.ImageFilterSaturated; +import com.android.gallery3d.filtershow.filters.ImageFilterShadows; +import com.android.gallery3d.filtershow.filters.ImageFilterVibrance; +import com.android.gallery3d.filtershow.filters.ImageFilterVignette; +import com.android.gallery3d.filtershow.filters.ImageFilterWBalance; import com.android.gallery3d.filtershow.imageshow.ImageBorder; import com.android.gallery3d.filtershow.imageshow.ImageCrop; import com.android.gallery3d.filtershow.imageshow.ImageFlip; @@ -45,18 +55,12 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.ImageSmallBorder; import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter; import com.android.gallery3d.filtershow.imageshow.ImageStraighten; +import com.android.gallery3d.filtershow.imageshow.ImageWithIcon; import com.android.gallery3d.filtershow.imageshow.ImageZoom; import com.android.gallery3d.filtershow.presets.ImagePreset; -import com.android.gallery3d.filtershow.presets.ImagePresetBW; -import com.android.gallery3d.filtershow.presets.ImagePresetBWBlue; -import com.android.gallery3d.filtershow.presets.ImagePresetBWGreen; -import com.android.gallery3d.filtershow.presets.ImagePresetBWRed; -import com.android.gallery3d.filtershow.presets.ImagePresetFX; -import com.android.gallery3d.filtershow.presets.ImagePresetOld; -import com.android.gallery3d.filtershow.presets.ImagePresetSaturated; -import com.android.gallery3d.filtershow.presets.ImagePresetXProcessing; import com.android.gallery3d.filtershow.provider.SharedImageProvider; import com.android.gallery3d.filtershow.tools.SaveCopyTask; +import com.android.gallery3d.filtershow.ui.ImageButtonTitle; import com.android.gallery3d.filtershow.ui.ImageCurves; import java.io.File; @@ -93,6 +97,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private static final int SELECT_PICTURE = 1; private static final String LOGTAG = "FilterShowActivity"; protected static final boolean ANIMATE_PANELS = true; + private static int mImageBorderSize = 40; private boolean mShowingHistoryPanel = false; private boolean mShowingImageStatePanel = false; @@ -114,6 +119,15 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, ImageFilterRS.setRenderScriptContext(this); + // TODO: get those values from XML. + ImageShow.setTextSize((int) getPixelsFromDip(12)); + ImageShow.setTextPadding((int) getPixelsFromDip(10)); + ImageButtonTitle.setTextSize((int) getPixelsFromDip(12)); + ImageButtonTitle.setTextPadding((int) getPixelsFromDip(10)); + ImageSmallFilter.setMargin((int) getPixelsFromDip(6)); + ImageSmallFilter.setTextMargin((int) getPixelsFromDip(4)); + mImageBorderSize = (int) getPixelsFromDip(20); + setContentView(R.layout.filtershow_activity); ActionBar actionBar = getActionBar(); actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); @@ -130,6 +144,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, LinearLayout listFilters = (LinearLayout) findViewById(R.id.listFilters); LinearLayout listBorders = (LinearLayout) findViewById(R.id.listBorders); + LinearLayout listColors = (LinearLayout) findViewById(R.id.listColorsFx); mImageShow = (ImageShow) findViewById(R.id.imageShow); mImageCurves = (ImageCurves) findViewById(R.id.imageCurves); @@ -200,7 +215,84 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton)); mPanelController.addPanel(mColorsButton, mListColors, 3); - mPanelController.addComponent(mColorsButton, findViewById(R.id.vignetteButton)); + + int []recastIDs = { + R.id.vignetteButton, + R.id.vibranceButton, + R.id.contrastButton, + R.id.saturationButton, + R.id.wbalanceButton, + R.id.hueButton, + R.id.exposureButton, + R.id.shadowRecoveryButton + }; + ImageFilter []filters = { + new ImageFilterVignette(), + new ImageFilterVibrance(), + new ImageFilterContrast(), + new ImageFilterSaturated(), + new ImageFilterWBalance(), + new ImageFilterHue(), + new ImageFilterExposure(), + new ImageFilterShadows() + }; + + + for (int i = 0; i < filters.length; i++) { + + ImageSmallFilter fView = new ImageSmallFilter(this); + View v = listColors.findViewById(recastIDs[i]); + int pos = listColors.indexOfChild(v); + listColors.removeView(v); + + filters[i].setParameter(100); + fView.setImageFilter(filters[i]); + fView.setController(this); + fView.setImageLoader(mImageLoader); + fView.setId(recastIDs[i]); + + mPanelController.addComponent(mColorsButton, fView); + listColors.addView(fView,pos); + } + + int []overlayIDs = { + R.id.sharpenButton, + R.id.curvesButtonRGB + }; + int []overlayBitmaps = { + R.drawable.filtershow_button_colors_sharpen, + R.drawable.filtershow_button_colors_curve + }; + int []overlayNames = { + R.string.sharpen, + R.string.curvesRGB + }; + + for (int i = 0; i < overlayIDs.length; i++) { + ImageWithIcon fView = new ImageWithIcon(this); + View v = listColors.findViewById(overlayIDs[i]); + int pos = listColors.indexOfChild(v); + listColors.removeView(v); + final int sid =overlayNames[i]; + ImageFilterExposure efilter = new ImageFilterExposure(){ + { + mName = getString(sid); + } + }; + efilter.setParameter(-300); + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), + overlayBitmaps[i] ); + + fView.setIcon(bitmap); + fView.setImageFilter(efilter); + fView.setController(this); + fView.setImageLoader(mImageLoader); + fView.setId(overlayIDs[i]); + + mPanelController.addComponent(mColorsButton, fView); + listColors.addView(fView,pos); + } + mPanelController.addComponent(mColorsButton, findViewById(R.id.curvesButtonRGB)); mPanelController.addComponent(mColorsButton, findViewById(R.id.sharpenButton)); mPanelController.addComponent(mColorsButton, findViewById(R.id.vibranceButton)); @@ -211,12 +303,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addComponent(mColorsButton, findViewById(R.id.exposureButton)); mPanelController.addComponent(mColorsButton, findViewById(R.id.shadowRecoveryButton)); - mPanelController.addView(findViewById(R.id.resetEffect)); mPanelController.addView(findViewById(R.id.applyEffect)); - findViewById(R.id.compareWithOriginalImage).setOnTouchListener( - createOnTouchShowOriginalButton()); - findViewById(R.id.resetOperationsButton).setOnClickListener( createOnClickResetOperationsButton()); @@ -236,7 +324,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageZoom.setSeekBar(seekBar); mPanelController.setRowPanel(findViewById(R.id.secondRowPanel)); mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList), - findViewById(R.id.compareWithOriginalImage), findViewById(R.id.applyEffect)); mPanelController.setMasterImage(mImageShow); mPanelController.setCurrentPanel(mFxButton); @@ -244,12 +331,22 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, String data = intent.getDataString(); if (data != null) { Uri uri = Uri.parse(data); - mImageLoader.loadBitmap(uri); + mImageLoader.loadBitmap(uri,getScreenImageSize()); } else { pickImage(); } } + private int getScreenImageSize(){ + DisplayMetrics metrics = new DisplayMetrics(); + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + display.getMetrics(metrics); + int msize = Math.min(size.x, size.y); + return (133*msize)/metrics.densityDpi; + } + private void showSavingProgress() { ProgressDialog progress; if (mSavingProgressDialog != null) { @@ -397,7 +494,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private void fillListImages(LinearLayout listFilters) { // TODO: use listview // TODO: load the filters straight from the filesystem - ImagePreset[] preset = new ImagePreset[18]; + + ImageFilterFx[] fxArray = new ImageFilterFx[18]; int p = 0; int[] drawid = { @@ -424,25 +522,34 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, R.string.ffx_washout_color, }; - preset[p++] = new ImagePreset(); + ImagePreset preset = new ImagePreset(); // empty + ImageSmallFilter filter = new ImageSmallFilter(this); + + filter.setSelected(true); + mCurrentImageSmallFilter = filter; + + filter.setPreviousImageSmallFilter(null); + preset.setIsFx(true); + filter.setImagePreset(preset); + + filter.setController(this); + filter.setImageLoader(mImageLoader); + listFilters.addView(filter); + ImageSmallFilter previousFilter = filter; + BitmapFactory.Options o = new BitmapFactory.Options(); o.inScaled = false; for (int i = 0; i < drawid.length; i++) { Bitmap b = BitmapFactory.decodeResource(getResources(), drawid[i], o); - preset[p++] = new ImagePresetFX(b, getString(fxNameid[i])); + fxArray[p++] = new ImageFilterFx(b, getString(fxNameid[i])); } - ImageSmallFilter previousFilter = null; for (int i = 0; i < p; i++) { - ImageSmallFilter filter = new ImageSmallFilter(this); - if (i == 0) { - filter.setSelected(true); - mCurrentImageSmallFilter = filter; - } + filter = new ImageSmallFilter(this); + filter.setPreviousImageSmallFilter(previousFilter); - preset[i].setIsFx(true); - filter.setImagePreset(preset[i]); + filter.setImageFilter(fxArray[i]); filter.setController(this); filter.setImageLoader(mImageLoader); listFilters.addView(filter); @@ -450,7 +557,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } // Default preset (original) - mImageShow.setImagePreset(preset[0]); + mImageShow.setImagePreset(preset); } private void fillListBorders(LinearLayout listBorders) { @@ -460,15 +567,14 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, ImageFilter[] borders = new ImageFilter[7]; borders[p++] = new ImageFilterBorder(null); - Drawable npd3 = getResources().getDrawable(R.drawable.filtershow_border_film3); - borders[p++] = new ImageFilterBorder(npd3); - Drawable npd = getResources().getDrawable( - R.drawable.filtershow_border_scratch3); - borders[p++] = new ImageFilterBorder(npd); - borders[p++] = new ImageFilterParametricBorder(Color.BLACK, 100, 0); - borders[p++] = new ImageFilterParametricBorder(Color.BLACK, 100, 100); - borders[p++] = new ImageFilterParametricBorder(Color.WHITE, 100, 0); - borders[p++] = new ImageFilterParametricBorder(Color.WHITE, 100, 100); + Drawable npd1 = getResources().getDrawable(R.drawable.filtershow_border_4x5); + borders[p++] = new ImageFilterBorder(npd1); + Drawable npd2 = getResources().getDrawable(R.drawable.filtershow_border_brush); + borders[p++] = new ImageFilterBorder(npd2); + borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, 0); + borders[p++] = new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, mImageBorderSize); + borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, 0); + borders[p++] = new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, mImageBorderSize); ImageSmallFilter previousFilter = null; for (int i = 0; i < p; i++) { @@ -526,23 +632,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } } - // ////////////////////////////////////////////////////////////////////////////// - // Click handlers for the top row buttons - - private OnTouchListener createOnTouchShowOriginalButton() { - return new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - boolean show = false; - if ((event.getActionMasked() != MotionEvent.ACTION_UP) - || (event.getActionMasked() == MotionEvent.ACTION_CANCEL)) { - show = true; - } - showOriginalViews(show); - return true; - } - }; - } // ////////////////////////////////////////////////////////////////////////////// // imageState panel... @@ -629,6 +718,12 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, }; } + @Override + public void onBackPressed() { + if (mPanelController.onBackPressed()) { + finish(); + } + } // ////////////////////////////////////////////////////////////////////////////// public float getPixelsFromDip(float value) { @@ -672,12 +767,9 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, ImagePreset oldPreset = mImageShow.getImagePreset(); ImagePreset copy = new ImagePreset(oldPreset); // TODO: use a numerical constant instead. - if (setBorder) { - copy.setHistoryName("Border"); - copy.setBorder(imageFilter); - } else { - copy.add(imageFilter); - } + + copy.add(imageFilter); + mImageShow.setImagePreset(copy); invalidateViews(); } @@ -703,7 +795,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, if (resultCode == RESULT_OK) { if (requestCode == SELECT_PICTURE) { Uri selectedImageUri = data.getData(); - mImageLoader.loadBitmap(selectedImageUri); + mImageLoader.loadBitmap(selectedImageUri,getScreenImageSize()); } } } diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index 8da762559..963d17a6f 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -108,17 +108,15 @@ public class PanelController implements OnClickListener { class UtilityPanel { private final Context mContext; private final View mView; - private final View mCompareView; private final TextView mTextView; private boolean mSelected = false; private String mEffectName = null; private int mParameterValue = 0; private boolean mShowParameterValue = false; - public UtilityPanel(Context context, View view, View compareView, View textView) { + public UtilityPanel(Context context, View view, View textView) { mContext = context; mView = view; - mCompareView = compareView; mTextView = (TextView) textView; } @@ -131,31 +129,23 @@ public class PanelController implements OnClickListener { updateText(); } - public void setGeometryEffect(boolean isGeometryEffect) { - if (isGeometryEffect) { - mCompareView.setVisibility(View.INVISIBLE); - } else { - mCompareView.setVisibility(View.VISIBLE); - } - } - public void setEffectName(String effectName) { mEffectName = effectName; - showParameter(true); - updateText(); + setShowParameter(true); } - public void showParameter(boolean s) { + public void setShowParameter(boolean s) { mShowParameterValue = s; + updateText(); } public void updateText() { String apply = mContext.getString(R.string.apply_effect); if (mShowParameterValue) { - mTextView.setText(Html.fromHtml(apply + "<br/><small>" + mEffectName + "<br/>" - + mParameterValue + "</small>")); + mTextView.setText(Html.fromHtml(apply + " " + mEffectName + " " + + mParameterValue)); } else { - mTextView.setText(Html.fromHtml(apply + "<br/><small>" + mEffectName + "</small>")); + mTextView.setText(Html.fromHtml(apply + " " + mEffectName)); } } @@ -239,12 +229,22 @@ public class PanelController implements OnClickListener { imageShow.setPanelController(this); } + public boolean onBackPressed() { + if (mUtilityPanel == null || !mUtilityPanel.selected()) { + return true; + } + mCurrentImage.resetParameter(); + showPanel(mCurrentPanel); + mCurrentImage.select(); + return false; + } + public void onNewValue(int value) { mUtilityPanel.onNewValue(value); } public void showParameter(boolean s) { - mUtilityPanel.showParameter(s); + mUtilityPanel.setShowParameter(s); } public void setCurrentPanel(View panel) { @@ -255,8 +255,8 @@ public class PanelController implements OnClickListener { mRowPanel = rowPanel; } - public void setUtilityPanel(Context context, View utilityPanel, View compareView, View textView) { - mUtilityPanel = new UtilityPanel(context, utilityPanel, compareView, textView); + public void setUtilityPanel(Context context, View utilityPanel, View textView) { + mUtilityPanel = new UtilityPanel(context, utilityPanel, textView); } public void setMasterImage(ImageShow imageShow) { @@ -337,7 +337,6 @@ public class PanelController implements OnClickListener { public ImageFilter setImagePreset(ImageFilter filter, String name) { ImagePreset copy = new ImagePreset(getImagePreset()); copy.add(filter); - copy.setHistoryName(filter.getName()); copy.setIsFx(false); mMasterImage.setImagePreset(copy); return filter; @@ -397,41 +396,42 @@ public class PanelController implements OnClickListener { switch (view.getId()) { case R.id.straightenButton: { mCurrentImage = showImageView(R.id.imageStraighten); - mUtilityPanel.setEffectName("Straighten"); - mUtilityPanel.setGeometryEffect(true); + String ename = mCurrentImage.getContext().getString(R.string.straighten); + mUtilityPanel.setEffectName(ename); break; } case R.id.cropButton: { mCurrentImage = showImageView(R.id.imageCrop); - mUtilityPanel.setEffectName("Crop"); - mUtilityPanel.showParameter(false); - mUtilityPanel.setGeometryEffect(true); + String ename = mCurrentImage.getContext().getString(R.string.crop); + mUtilityPanel.setEffectName(ename); + mUtilityPanel.setShowParameter(false); break; } case R.id.rotateButton: { mCurrentImage = showImageView(R.id.imageRotate); - mUtilityPanel.setEffectName("Rotate"); - mUtilityPanel.setGeometryEffect(true); + String ename = mCurrentImage.getContext().getString(R.string.rotate); + mUtilityPanel.setEffectName(ename); break; } case R.id.flipButton: { mCurrentImage = showImageView(R.id.imageFlip); - mUtilityPanel.setEffectName("Flip"); - mUtilityPanel.showParameter(false); - mUtilityPanel.setGeometryEffect(true); + String ename = mCurrentImage.getContext().getString(R.string.flip); + mUtilityPanel.setEffectName(ename); + mUtilityPanel.setShowParameter(false); break; } case R.id.vignetteButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Vignette"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.vignette); + mUtilityPanel.setEffectName(ename); ensureFilter("Vignette"); break; } case R.id.curvesButtonRGB: { ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves); - mUtilityPanel.setEffectName("Curves"); - mUtilityPanel.setGeometryEffect(true); + String ename = curves.getContext().getString(R.string.curvesRGB); + mUtilityPanel.setEffectName(ename); + mUtilityPanel.setShowParameter(false); curves.setUseRed(true); curves.setUseGreen(true); curves.setUseBlue(true); @@ -441,71 +441,68 @@ public class PanelController implements OnClickListener { } case R.id.sharpenButton: { mCurrentImage = showImageView(R.id.imageZoom).setShowControls(true); - mUtilityPanel.setEffectName("Sharpen"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.sharpen); + mUtilityPanel.setEffectName(ename); ensureFilter("Sharpen"); break; } case R.id.contrastButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Contrast"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.contrast); + mUtilityPanel.setEffectName(ename); ensureFilter("Contrast"); break; } case R.id.saturationButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Saturated"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.saturation); + mUtilityPanel.setEffectName(ename); ensureFilter("Saturated"); break; } case R.id.wbalanceButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(false); - mUtilityPanel.setEffectName("White Balance"); - mUtilityPanel.setGeometryEffect(true); + String ename = mCurrentImage.getContext().getString(R.string.wbalance); + mUtilityPanel.setEffectName(ename); + mUtilityPanel.setShowParameter(false); ensureFilter("WBalance"); break; } case R.id.hueButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Hue"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.hue); + mUtilityPanel.setEffectName(ename); ensureFilter("Hue"); break; } case R.id.exposureButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Exposure"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.exposure); + mUtilityPanel.setEffectName(ename); ensureFilter("Exposure"); break; } case R.id.vibranceButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Vibrance"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.vibrance); + mUtilityPanel.setEffectName(ename); ensureFilter("Vibrance"); break; } case R.id.shadowRecoveryButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Shadows"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.shadow_recovery); + mUtilityPanel.setEffectName(ename); ensureFilter("Shadows"); break; } case R.id.redEyeButton: { mCurrentImage = showImageView(R.id.imageShow).setShowControls(true); - mUtilityPanel.setEffectName("Redeye"); - mUtilityPanel.setGeometryEffect(false); + String ename = mCurrentImage.getContext().getString(R.string.redeye); + mUtilityPanel.setEffectName(ename); ensureFilter("Redeye"); break; } - case R.id.resetEffect: { - mCurrentImage.resetParameter(); - break; - } case R.id.applyEffect: { showPanel(mCurrentPanel); break; diff --git a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java index 67bd49b1c..25d1db414 100644 --- a/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java +++ b/src/com/android/gallery3d/filtershow/cache/DirectPresetCache.java @@ -62,7 +62,7 @@ public class DirectPresetCache implements Cache { private CachedPreset getCachedPreset(ImagePreset preset) { for (int i = 0; i < mCache.size(); i++) { CachedPreset cache = mCache.elementAt(i); - if (cache.mPreset == preset && !cache.mBusy) { + if (cache.mPreset == preset) { return cache; } } @@ -73,7 +73,7 @@ public class DirectPresetCache implements Cache { public Bitmap get(ImagePreset preset) { // Log.v(LOGTAG, "get preset " + preset.name() + " : " + preset); CachedPreset cache = getCachedPreset(preset); - if (cache != null) { + if (cache != null && !cache.mBusy) { return cache.mBitmap; } // Log.v(LOGTAG, "didn't find preset " + preset.name() + " : " + preset @@ -138,18 +138,21 @@ public class DirectPresetCache implements Cache { public void prepare(ImagePreset preset) { // Log.v(LOGTAG, "prepare preset " + preset.name() + " : " + preset); CachedPreset cache = getCachedPreset(preset); - if (cache == null) { - if (mCache.size() < mCacheSize) { - cache = new CachedPreset(); - mCache.add(cache); - } else { - cache = getOldestCachedPreset(); + if (cache == null || (cache.mBitmap == null && !cache.mBusy)) { + if (cache == null) { + if (mCache.size() < mCacheSize) { + cache = new CachedPreset(); + mCache.add(cache); + } else { + cache = getOldestCachedPreset(); + } } if (cache != null) { cache.mPreset = preset; + willCompute(cache); } } - willCompute(cache); + } } diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 54d71ad77..e00a1b77d 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -61,11 +61,12 @@ public class ImageLoader { mHiresCache = new DelayedPresetCache(this, 2); } - public void loadBitmap(Uri uri) { + public void loadBitmap(Uri uri,int size) { mUri = uri; mOrientation = getOrientation(uri); + mOriginalBitmapSmall = loadScaledBitmap(uri, 160); - mOriginalBitmapLarge = loadScaledBitmap(uri, 320); + mOriginalBitmapLarge = loadScaledBitmap(uri, size); updateBitmaps(); } @@ -179,6 +180,7 @@ public class ImageLoader { // decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; + closeStream(is); is = mContext.getContentResolver().openInputStream(uri); return BitmapFactory.decodeStream(is, null, o2); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java index 662e8ed2b..6d0c020a7 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java @@ -8,12 +8,27 @@ public class ImageFilter implements Cloneable { protected int mParameter = 0; protected String mName = "Original"; private final String LOGTAG = "ImageFilter"; + public static final byte TYPE_BORDER =1; + public static final byte TYPE_FX = 2; + public static final byte TYPE_WBALANCE = 3; + public static final byte TYPE_VIGNETTE = 4; + public static final byte TYPE_NORMAL = 5; + private byte filterType = TYPE_NORMAL; + + public byte getFilterType(){ + return filterType; + } + + protected void setFilterType(byte type){ + filterType = type; + } @Override public ImageFilter clone() throws CloneNotSupportedException { ImageFilter filter = (ImageFilter) super.clone(); filter.setName(getName()); filter.setParameter(getParameter()); + filter.setFilterType(filterType); return filter; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java index 9fcf1dc1d..dd7d17c46 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java @@ -17,6 +17,7 @@ public class ImageFilterBorder extends ImageFilter { } public ImageFilterBorder(Drawable ninePatch) { + setFilterType(TYPE_BORDER); mName = "Border"; mNinePatch = ninePatch; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java index 00fd20cf5..1575b18bb 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java @@ -13,8 +13,9 @@ public class ImageFilterFx extends ImageFilter { private static final String TAG = "ImageFilterFx"; Bitmap fxBitmap; - public ImageFilterFx(Bitmap fxBitmap) { - mName = "fx"; + public ImageFilterFx(Bitmap fxBitmap,String name) { + setFilterType(TYPE_FX); + mName = name; this.fxBitmap = fxBitmap; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java index 311ae6310..3d48b7e53 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java @@ -18,14 +18,12 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; -import android.util.Log; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP; -import com.android.gallery3d.filtershow.imageshow.ImageGeometry; public class ImageFilterGeometry extends ImageFilter { private final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888; @@ -65,99 +63,41 @@ public class ImageFilterGeometry extends ImageFilter { native protected void nativeApplyFilterStraighten(Bitmap src, int srcWidth, int srcHeight, Bitmap dst, int dstWidth, int dstHeight, float straightenAngle); - @Override - public Bitmap apply(Bitmap originalBitmap, float scaleFactor, boolean highQuality) { - Rect cropBounds = new Rect(); - Rect originalBounds = new Rect(); - FLIP flipType = mGeometry.getFlipType(); - float rAngle = mGeometry.getRotation(); - float sAngle = mGeometry.getStraightenRotation(); - mGeometry.getCropBounds().roundOut(cropBounds); - mGeometry.getPhotoBounds().roundOut(originalBounds); - boolean flip = flipType != FLIP.NONE; - boolean rotate = rAngle != 0; - boolean crop = !cropBounds.equals(originalBounds); - boolean straighten = sAngle != 0; - - int jniFlipType = 0; - switch (flipType) { - case BOTH: - jniFlipType = BOTH; - break; - case VERTICAL: - jniFlipType = VERTICAL; - break; - case HORIZONTAL: - jniFlipType = HORIZONTAL; - break; - default: - jniFlipType = 0; - break; - } - int bmWidth = originalBitmap.getWidth(); - int bmHeight = originalBitmap.getHeight(); - if (!(flip || rotate || crop || straighten)) { - return originalBitmap; - } - if (originalBounds.width() != bmWidth || originalBounds.height() != bmHeight) { - if (LOGV) - Log.v(LOGTAG, "PHOTOBOUNDS WIDTH/HEIGHT NOT SAME AS BITMAP WIDTH/HEIGHT"); - return originalBitmap; - } - Bitmap modBitmap = originalBitmap; - Rect modifiedBounds = new Rect(originalBounds); - if (flip) { - modBitmap = originalBitmap.copy(mConfig, true); - nativeApplyFilterFlip(originalBitmap, bmWidth, bmHeight, modBitmap, - bmWidth, bmHeight, jniFlipType); - } - if (rotate) { - // Fails for non-90 degree rotations - Bitmap modBitmapRotate = null; - rAngle %= 360; - int deg = (int) (rAngle / 90); - deg = -deg; // Make CCW positive - if (deg < 0) - deg += 4; - // Now deg is in [1, 3] as required by native rotate - if (deg == ONE_EIGHTY) { - modBitmapRotate = Bitmap.createBitmap(bmWidth, bmHeight, mConfig); - nativeApplyFilterRotate(modBitmap, bmWidth, bmHeight, modBitmapRotate, - bmWidth, bmHeight, deg); - modifiedBounds = new Rect(0, 0, bmWidth, bmHeight); - } else if (deg == TWO_SEVENTY || deg == NINETY) { - modBitmapRotate = Bitmap.createBitmap(bmHeight, bmWidth, mConfig); - nativeApplyFilterRotate(modBitmap, bmWidth, bmHeight, modBitmapRotate, - bmHeight, bmWidth, deg); - modifiedBounds = new Rect(0, 0, bmHeight, bmWidth); - } - modBitmap = modBitmapRotate; - } - if (straighten) { - Rect straightenBounds = new Rect(); - ImageGeometry.getUntranslatedStraightenCropBounds(new RectF(modifiedBounds), sAngle) - .roundOut(straightenBounds); - Bitmap modBitmapStraighten = Bitmap.createBitmap(straightenBounds.width(), - straightenBounds.height(), mConfig); - nativeApplyFilterStraighten(modBitmap, modifiedBounds.width(), modifiedBounds.height(), - modBitmapStraighten, - straightenBounds.width(), straightenBounds.height(), - mGeometry.getStraightenRotation()); - modifiedBounds = straightenBounds; - modBitmap = modBitmapStraighten; + public Matrix buildMatrix(RectF r) { + float dx = r.width()/2; + float dy = r.height()/2; + if(mGeometry.hasSwitchedWidthHeight()){ + float temp = dx; + dx = dy; + dy = temp; } - if (crop) { - Bitmap modBitmapCrop = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), - mConfig); - // Force crop bounds to be within straighten bounds. - if (!modifiedBounds.intersect(cropBounds)) { - return modBitmap; - } - nativeApplyFilterCrop(modBitmap, bmWidth, bmHeight, modBitmapCrop, - cropBounds.width(), cropBounds.height(), cropBounds.left, cropBounds.top); - modBitmap = modBitmapCrop; + float w = r.left * 2 + r.width(); + float h = r.top * 2 + r.height(); + Matrix m = mGeometry.buildGeometryMatrix(w, h, 1f, dx, dy, false); + + return m; + } + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + // TODO: implement bilinear or bicubic here... for now, just use + // canvas to do a simple implementation... + // TODO: and be more memory efficient! (do it in native?) + Rect cropBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF crop = mGeometry.getCropBounds(bitmap); + if(crop.width() > 0 && crop.height() > 0) + crop.roundOut(cropBounds); + Bitmap temp = null; + if (mGeometry.hasSwitchedWidthHeight()) { + temp = Bitmap.createBitmap(cropBounds.height(), cropBounds.width(), mConfig); + } else { + temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig); } - return modBitmap; + + Matrix drawMatrix = buildMatrix(crop); + Canvas canvas = new Canvas(temp); + canvas.drawBitmap(bitmap, drawMatrix, new Paint()); + return temp; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java index 2ead04764..3649d28fa 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java @@ -14,11 +14,13 @@ public class ImageFilterParametricBorder extends ImageFilter { private int mBorderCornerRadius = 10; public ImageFilterParametricBorder() { + setFilterType(TYPE_BORDER); mName = "Border"; } public ImageFilterParametricBorder(int color, int size, int radius) { setBorder(color, size, radius); + setFilterType(TYPE_BORDER); } @Override diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java index 4c43410e3..fed583832 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java @@ -6,6 +6,7 @@ import android.graphics.Bitmap; public class ImageFilterVignette extends ImageFilter { public ImageFilterVignette() { + setFilterType(TYPE_VIGNETTE); mName = "Vignette"; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java index 163ed0c65..5e613ebce 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java @@ -8,6 +8,7 @@ public class ImageFilterWBalance extends ImageFilter { private static final String TAG = "ImageFilterWBalance"; public ImageFilterWBalance() { + setFilterType(TYPE_WBALANCE); mName = "WBalance"; } @@ -17,7 +18,6 @@ public class ImageFilterWBalance extends ImageFilter { public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); - Log.v(TAG,"White Balance Call"); nativeApplyFilter(bitmap, w, h, -1,-1); return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java index 4f172285d..352fa5bf3 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java @@ -17,28 +17,23 @@ package com.android.gallery3d.filtershow.imageshow; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.RectF; import com.android.gallery3d.filtershow.filters.ImageFilterGeometry; -/** - * This class holds metadata about an image's geometry. Specifically: rotation, - * scaling, cropping, and image boundaries. It maintains the invariant that the - * cropping boundaries are within or equal to the image boundaries (before - * rotation) WHEN mSafe is true. - */ - public class GeometryMetadata { // Applied in order: rotate, crop, scale. // Do not scale saved image (presumably?). private static final ImageFilterGeometry mImageFilter = new ImageFilterGeometry(); - private float mScaleFactor = 0; + private float mScaleFactor = 1.0f; private float mRotation = 0; private float mStraightenRotation = 0; private final RectF mCropBounds = new RectF(); private final RectF mPhotoBounds = new RectF(); private FLIP mFlip = FLIP.NONE; - private boolean mSafe = false; + + private RectF mBounds = new RectF(); public enum FLIP { NONE, VERTICAL, HORIZONTAL, BOTH @@ -51,31 +46,12 @@ public class GeometryMetadata { set(g); } - public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality){ + public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality) { mImageFilter.setGeometryMetadata(this); Bitmap m = mImageFilter.apply(original, scaleFactor, highQuality); - mPhotoBounds.set(0,0, m.getWidth(), m.getHeight()); - mCropBounds.set(mPhotoBounds); - mScaleFactor = 0; - mRotation = 0; - mStraightenRotation = 0; - mFlip = FLIP.NONE; - mSafe = false; return m; } - public GeometryMetadata(float scale, float rotation, float straighten, RectF cropBounds, - RectF photoBounds, FLIP flipType) { - mScaleFactor = scale; - mRotation = rotation; - mStraightenRotation = straighten; - mCropBounds.set(cropBounds); - mPhotoBounds.set(photoBounds); - mFlip = flipType; - mSafe = cropFitsInPhoto(mCropBounds); - } - - // Safe as long as invariant holds. public void set(GeometryMetadata g) { mScaleFactor = g.mScaleFactor; mRotation = g.mRotation; @@ -83,35 +59,7 @@ public class GeometryMetadata { mCropBounds.set(g.mCropBounds); mPhotoBounds.set(g.mPhotoBounds); mFlip = g.mFlip; - mSafe = g.mSafe; - } - - public void safeSet(GeometryMetadata g) { - if (g.safe()) { - set(g); - return; - } - - mScaleFactor = g.mScaleFactor; - mRotation = g.mRotation; - mStraightenRotation = g.mStraightenRotation; - mCropBounds.set(g.mCropBounds); - safeSetPhotoBounds(g.mPhotoBounds); - mFlip = g.mFlip; - } - - public void safeSet(float scale, - float rotation, - float straighten, - RectF cropBounds, - RectF photoBounds, - FLIP flipType) { - mScaleFactor = scale; - mStraightenRotation = straighten; - mRotation = rotation; - mCropBounds.set(cropBounds); - safeSetPhotoBounds(photoBounds); - mFlip = flipType; + mBounds = g.mBounds; } public float getScaleFactor() { @@ -126,10 +74,19 @@ public class GeometryMetadata { return mStraightenRotation; } - public RectF getCropBounds() { + public RectF getPreviewCropBounds() { return new RectF(mCropBounds); } + public RectF getCropBounds(Bitmap bitmap) { + float scale = 1.0f; + if (mPhotoBounds.width() > 0) { + scale = bitmap.getWidth() / mPhotoBounds.width(); + } + return new RectF(mCropBounds.left * scale, mCropBounds.top * scale, + mCropBounds.right * scale, mCropBounds.bottom * scale); + } + public FLIP getFlipType() { return mFlip; } @@ -138,10 +95,6 @@ public class GeometryMetadata { return new RectF(mPhotoBounds); } - public boolean safe() { - return mSafe; - } - public void setScaleFactor(float scale) { mScaleFactor = scale; } @@ -158,41 +111,12 @@ public class GeometryMetadata { mStraightenRotation = straighten; } - /** - * Sets crop bounds to be the intersection of mPhotoBounds and the new crop - * bounds. If there was no intersection, returns false and does not set crop - * bounds - */ - public boolean safeSetCropBounds(RectF newCropBounds) { - if (mCropBounds.setIntersect(newCropBounds, mPhotoBounds)) { - mSafe = true; - return true; - } - return false; - } - public void setCropBounds(RectF newCropBounds) { mCropBounds.set(newCropBounds); - mSafe = false; - } - - /** - * Sets mPhotoBounds to be the new photo bounds and sets mCropBounds to be - * the intersection of the new photo bounds and the old crop bounds. Sets - * the crop bounds to mPhotoBounds if there is no intersection. - */ - - public void safeSetPhotoBounds(RectF newPhotoBounds) { - mPhotoBounds.set(newPhotoBounds); - if (!mCropBounds.intersect(mPhotoBounds)) { - mCropBounds.set(mPhotoBounds); - } - mSafe = true; } public void setPhotoBounds(RectF newPhotoBounds) { mPhotoBounds.set(newPhotoBounds); - mSafe = false; } public boolean cropFitsInPhoto(RectF cropBounds) { @@ -210,7 +134,7 @@ public class GeometryMetadata { return (mScaleFactor == d.mScaleFactor && mRotation == d.mRotation && mStraightenRotation == d.mStraightenRotation && - mFlip == d.mFlip && mSafe == d.mSafe && + mFlip == d.mFlip && mCropBounds.equals(d.mCropBounds) && mPhotoBounds.equals(d.mPhotoBounds)); } @@ -223,17 +147,76 @@ public class GeometryMetadata { result = 31 * result + mFlip.hashCode(); result = 31 * result + mCropBounds.hashCode(); result = 31 * result + mPhotoBounds.hashCode(); - result = 31 * result + (mSafe ? 1 : 0); return result; } @Override public String toString() { return getClass().getName() + "[" + "scale=" + mScaleFactor - + ",rotation=" + mRotation + ",flip=" + mFlip + ",safe=" - + (mSafe ? "true" : "false") + ",straighten=" + + ",rotation=" + mRotation + ",flip=" + mFlip + ",straighten=" + mStraightenRotation + ",cropRect=" + mCropBounds.toShortString() + ",photoRect=" + mPhotoBounds.toShortString() + "]"; } + protected Matrix getHorizontalMatrix(float width) { + Matrix flipHorizontalMatrix = new Matrix(); + flipHorizontalMatrix.setScale(-1, 1); + flipHorizontalMatrix.postTranslate(width, 0); + return flipHorizontalMatrix; + } + + protected Matrix getVerticalMatrix(float height) { + Matrix flipVerticalMatrix = new Matrix(); + flipVerticalMatrix.setScale(1, -1); + flipVerticalMatrix.postTranslate(0, height); + return flipVerticalMatrix; + } + + public Matrix getFlipMatrix(float width, float height) { + FLIP type = getFlipType(); + if (type == FLIP.HORIZONTAL) { + return getHorizontalMatrix(width); + } else if (type == FLIP.VERTICAL) { + return getVerticalMatrix(height); + } else if (type == FLIP.BOTH) { + Matrix flipper = getVerticalMatrix(height); + flipper.postConcat(getHorizontalMatrix(width)); + return flipper; + } else { + Matrix m = new Matrix(); + m.reset(); // identity + return m; + } + } + + public boolean hasSwitchedWidthHeight() { + return (((int) (mRotation / 90)) % 2) != 0; + } + + public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy, + float rotation) { + float dx0 = width / 2; + float dy0 = height / 2; + Matrix m = getFlipMatrix(width, height); + m.postTranslate(-dx0, -dy0); + m.postRotate(rotation); + m.postScale(scaling, scaling); + m.postTranslate(dx, dy); + return m; + } + + public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy, + boolean onlyRotate) { + float rot = mRotation; + if (!onlyRotate) { + rot += mStraightenRotation; + } + return buildGeometryMatrix(width, height, scaling, dx, dy, rot); + } + + public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) { + float w = mPhotoBounds.width(); + float h = mPhotoBounds.height(); + return buildGeometryMatrix(w, h, scaling, dx, dy, false); + } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java index 836ec82f8..4d171bf4c 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java @@ -22,17 +22,12 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; -import android.view.MotionEvent; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.presets.ImagePreset; - public class ImageCrop extends ImageGeometry { private static final boolean LOGV = false; private static final int MOVE_LEFT = 1; @@ -43,19 +38,14 @@ public class ImageCrop extends ImageGeometry { private static final float MIN_CROP_WIDTH_HEIGHT = 0.1f; private static final int TOUCH_TOLERANCE = 30; - private static final int SHADOW_ALPHA = 160; - private float mAspectWidth = 4; - private float mAspectHeight = 3; - private boolean mFixAspectRatio = false; // not working yet + private boolean mFirstDraw = true; + private float mAspectWidth = 1; + private float mAspectHeight = 1; + private boolean mFixAspectRatio = false; private final Paint borderPaint; - private float mCropOffsetX = 0; - private float mCropOffsetY = 0; - private float mPrevOffsetX = 0; - private float mPrevOffsetY = 0; - private int movingEdges; private final Drawable cropIndicator; private final int indicatorSize; @@ -89,15 +79,15 @@ public class ImageCrop extends ImageGeometry { } private float getScaledMinWidthHeight() { - RectF disp = getLocalDisplayBounds(); + RectF disp = new RectF(0, 0, getWidth(), getHeight()); float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT - / getLocalScale(); + / computeScale(getWidth(), getHeight()); return scaled; } - protected static Matrix getCropRotationMatrix(float rotation, RectF localImage) { - Matrix m = new Matrix(); - m.setRotate(rotation, localImage.centerX(), localImage.centerY()); + protected Matrix getCropRotationMatrix(float rotation, RectF localImage) { + Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height()); + m.postRotate(rotation, localImage.centerX(), localImage.centerY()); if (!m.rectStaysRect()) { return null; } @@ -117,7 +107,7 @@ public class ImageCrop extends ImageGeometry { m.mapRect(crop); } m = new Matrix(); - float zoom = getLocalScale(); + float zoom = computeScale(getWidth(), getHeight()); m.setScale(zoom, zoom, mCenterX, mCenterY); m.preTranslate(mXOffset, mYOffset); m.mapRect(crop); @@ -139,6 +129,44 @@ public class ImageCrop extends ImageGeometry { return crop; } + private RectF getUnrotatedCropBounds(RectF cropBounds) { + Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); + + if (m == null) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX"); + return null; + } + Matrix m0 = new Matrix(); + if (!m.invert(m0)) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX"); + return null; + } + RectF crop = new RectF(cropBounds); + if (!m0.mapRect(crop)) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS"); + return null; + } + return crop; + } + + private RectF getRotatedStraightenBounds() { + RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten()); + Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); + + if (m == null) { + if (LOGV) + Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE"); + return null; + } else { + m.mapRect(straightenBounds); + } + return straightenBounds; + } + /** * Sets cropped bounds; modifies the bounds if it's smaller than the allowed * dimensions. @@ -147,16 +175,33 @@ public class ImageCrop extends ImageGeometry { // Avoid cropping smaller than minimum width or height. RectF cbounds = new RectF(bounds); float minWidthHeight = getScaledMinWidthHeight(); + float aw = mAspectWidth; + float ah = mAspectHeight; + if (mFixAspectRatio) { + minWidthHeight /= aw * ah; + int r = (int) (getLocalRotation() / 90); + if (r % 2 != 0) { + float temp = aw; + aw = ah; + ah = temp; + } + } float newWidth = cbounds.width(); float newHeight = cbounds.height(); - if (newWidth < minWidthHeight) { - newWidth = minWidthHeight; - } - if (newHeight < minWidthHeight) { - newHeight = minWidthHeight; + if (mFixAspectRatio) { + if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) { + newWidth = minWidthHeight * aw; + newHeight = minWidthHeight * ah; + } + } else { + if (newWidth < minWidthHeight) { + newWidth = minWidthHeight; + } + if (newHeight < minWidthHeight) { + newHeight = minWidthHeight; + } } - RectF pbounds = getLocalPhotoBounds(); if (pbounds.width() < minWidthHeight) { newWidth = pbounds.width(); @@ -166,13 +211,14 @@ public class ImageCrop extends ImageGeometry { } cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight); - RectF snappedCrop = findCropBoundForRotatedImg(cbounds, pbounds, getLocalStraighten(), - mCenterX - mXOffset, mCenterY - mYOffset); + RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten()); + cbounds.intersect(straightenBounds); + if (mFixAspectRatio) { - // TODO: add aspect ratio stuff - fixAspectRatio(snappedCrop, mAspectWidth, mAspectHeight); + fixAspectRatio(cbounds, aw, ah); } - setLocalCropBounds(snappedCrop); + setLocalCropBounds(cbounds); invalidate(); } @@ -199,33 +245,88 @@ public class ImageCrop extends ImageGeometry { else if (bottom <= TOUCH_TOLERANCE) { movingEdges |= MOVE_BOTTOM; } + // Check inside block. + if (cropped.contains(x, y) && (movingEdges == 0)) { + movingEdges = MOVE_BLOCK; + } invalidate(); } private void moveEdges(float dX, float dY) { RectF cropped = getRotatedCropBounds(); float minWidthHeight = getScaledMinWidthHeight(); - float scale = getLocalScale(); + float scale = computeScale(getWidth(), getHeight()); float deltaX = dX / scale; float deltaY = dY / scale; - if (movingEdges == MOVE_BLOCK) { - // TODO + int select = movingEdges; + if (mFixAspectRatio && (select != MOVE_BLOCK)) { + if ((select & MOVE_LEFT) != 0) { + select &= ~MOVE_BOTTOM; + select |= MOVE_TOP; + deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_TOP) != 0) { + select &= ~MOVE_RIGHT; + select |= MOVE_LEFT; + deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_RIGHT) != 0) { + select &= ~MOVE_TOP; + select |= MOVE_BOTTOM; + deltaY = getNewHeightForWidthAspect(deltaX, mAspectWidth, mAspectHeight); + } + if ((select & MOVE_BOTTOM) != 0) { + select &= ~MOVE_LEFT; + select |= MOVE_RIGHT; + deltaX = getNewWidthForHeightAspect(deltaY, mAspectWidth, mAspectHeight); + } + } + + if (select == MOVE_BLOCK) { + RectF straight = getRotatedStraightenBounds(); + // Move the whole cropped bounds within the photo display bounds. + deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX) + : Math.max(straight.left - cropped.left, deltaX); + deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY) + : Math.max(straight.top - cropped.top, deltaY); + cropped.offset(deltaX, deltaY); } else { - if ((movingEdges & MOVE_LEFT) != 0) { - cropped.left = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight); - fixRectAspectW(cropped); + float dx = 0; + float dy = 0; + if ((select & MOVE_LEFT) != 0) { + dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left; + } + if ((select & MOVE_TOP) != 0) { + dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top; + } + if ((select & MOVE_RIGHT) != 0) { + dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight) + - cropped.right; + } + if ((select & MOVE_BOTTOM) != 0) { + dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight) + - cropped.bottom; + } + + if (mFixAspectRatio) { + if (dx < dy) { + dy = getNewHeightForWidthAspect(dx, mAspectWidth, mAspectHeight); + } else { + dx = getNewWidthForHeightAspect(dy, mAspectWidth, mAspectHeight); + } + } + + if ((select & MOVE_LEFT) != 0) { + cropped.left += dx; } - if ((movingEdges & MOVE_TOP) != 0) { - cropped.top = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight); - fixRectAspectH(cropped); + if ((select & MOVE_TOP) != 0) { + cropped.top += dy; } - if ((movingEdges & MOVE_RIGHT) != 0) { - cropped.right = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight); - fixRectAspectW(cropped); + if ((select & MOVE_RIGHT) != 0) { + cropped.right += dx; } - if ((movingEdges & MOVE_BOTTOM) != 0) { - cropped.bottom = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight); - fixRectAspectH(cropped); + if ((select & MOVE_BOTTOM) != 0) { + cropped.bottom += dy; } } Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds()); @@ -241,31 +342,6 @@ public class ImageCrop extends ImageGeometry { setCropBounds(cropped); } - private void fixRectAspectH(RectF cropped) { - if (mFixAspectRatio) { - float half = getNewWidthForHeightAspect(cropped.height(), mAspectWidth, mAspectHeight) / 2; - float mid = (cropped.right - cropped.left) / 2; - cropped.left = mid - half; - cropped.right = mid + half; - } - } - - private void fixRectAspectW(RectF cropped) { - if (mFixAspectRatio) { - float half = getNewHeightForWidthAspect(cropped.width(), mAspectWidth, mAspectHeight) / 2; - float mid = (cropped.bottom - cropped.top) / 2; - cropped.top = mid - half; - cropped.bottom = mid + half; - } - } - - private void drawShadow(Canvas canvas, float left, float top, float right, float bottom) { - canvas.save(); - canvas.clipRect(left, top, right, bottom); - canvas.drawARGB(SHADOW_ALPHA, 0, 0, 0); - canvas.restore(); - } - private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) { int left = (int) centerX - indicatorSize / 2; int top = (int) centerY - indicatorSize / 2; @@ -277,28 +353,43 @@ public class ImageCrop extends ImageGeometry { protected void setActionDown(float x, float y) { super.setActionDown(x, y); detectMovingEdges(x, y); - if (movingEdges == 0) { - mPrevOffsetX = mCropOffsetX; - mPrevOffsetY = mCropOffsetY; - } + } + + @Override + protected void setActionUp() { + super.setActionUp(); + movingEdges = 0; } @Override protected void setActionMove(float x, float y) { - if (movingEdges != 0) { + if (movingEdges != 0) moveEdges(x - mCurrentX, y - mCurrentY); + + super.setActionMove(x, y); + } + + private void cropSetup() { + if (mFixAspectRatio) { + RectF cb = getRotatedCropBounds(); + fixAspectRatio(cb, mAspectWidth, mAspectHeight); + RectF cb0 = getUnrotatedCropBounds(cb); + setCropBounds(cb0); } else { - float dx = x - mTouchCenterX; - float dy = y - mTouchCenterY; - mCropOffsetX = dx + mPrevOffsetX; - mCropOffsetY = dy + mPrevOffsetY; + setCropBounds(getLocalCropBounds()); } - super.setActionMove(x, y); } @Override protected void gainedVisibility() { - setCropBounds(getLocalCropBounds()); + cropSetup(); + mFirstDraw = true; + } + + @Override + public void resetParameter() { + super.resetParameter(); + cropSetup(); } @Override @@ -307,48 +398,66 @@ public class ImageCrop extends ImageGeometry { @Override protected void drawShape(Canvas canvas, Bitmap image) { + // TODO: move style to xml gPaint.setAntiAlias(true); gPaint.setFilterBitmap(true); gPaint.setDither(true); gPaint.setARGB(255, 255, 255, 255); - drawRegularFlippedBitmap(canvas, image, gPaint); - - RectF displayRect = getLocalDisplayBounds(); - float dWidth = displayRect.width(); - float dHeight = displayRect.height(); - RectF boundsRect = getCropBoundsDisplayed(); - gPaint.setARGB(128, 0, 0, 0); - gPaint.setStyle(Paint.Style.FILL); - // TODO: move this to style when refactoring - canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint); - canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint); - canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom, - gPaint); - canvas.drawRect(boundsRect.right, boundsRect.top, dWidth, - boundsRect.bottom, gPaint); - - Path path = new Path(); - path.addRect(boundsRect, Path.Direction.CCW); - gPaint.setARGB(255, 255, 255, 255); + if (mFirstDraw) { + cropSetup(); + mFirstDraw = false; + } + float rotation = getLocalRotation(); + drawTransformedBitmap(canvas, image, gPaint, true); + + gPaint.setARGB(255, 125, 255, 128); gPaint.setStrokeWidth(3); gPaint.setStyle(Paint.Style.STROKE); - - canvas.drawPath(path, gPaint); - - boolean notMoving = movingEdges == 0; - if (((movingEdges & MOVE_TOP) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, boundsRect.centerX(), boundsRect.top); + drawStraighten(canvas, gPaint); + RectF scaledCrop = unrotatedCropBounds(); + int decoded_moving = decoder(movingEdges, rotation); + canvas.save(); + canvas.rotate(rotation, mCenterX, mCenterY); + boolean notMoving = decoded_moving == 0; + if (((decoded_moving & MOVE_TOP) != 0) || notMoving) { + drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top); } - if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, boundsRect.centerX(), boundsRect.bottom); + if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) { + drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom); } - if (((movingEdges & MOVE_LEFT) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, boundsRect.left, boundsRect.centerY()); + if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) { + drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY()); } - if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, boundsRect.right, boundsRect.centerY()); + if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) { + drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY()); } + canvas.restore(); + } + + private int bitCycleLeft(int x, int times, int d){ + int mask = (1 << d) - 1; + int mout = x & mask; + times %= d; + int hi = mout >> (d - times); + int low = (mout << times) & mask; + int ret = x & ~mask; + ret |= low; + ret |= hi; + return ret; } -} + protected int decoder(int movingEdges, float rotation) { + int rot = constrainedRotation(rotation); + switch(rot){ + case 90: + return bitCycleLeft(movingEdges, 3, 4); + case 180: + return bitCycleLeft(movingEdges, 2, 4); + case 270: + return bitCycleLeft(movingEdges, 1, 4); + default: + return movingEdges; + } + } +}
\ No newline at end of file diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java index ba01d888a..3408405dc 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java @@ -19,9 +19,7 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; @@ -125,55 +123,7 @@ public class ImageFlip extends ImageGeometry { gPaint.setFilterBitmap(true); gPaint.setDither(true); gPaint.setARGB(255, 255, 255, 255); - - FLIP flip = mNextFlip; - canvas.save(); - float zoom = getLocalScale(); - canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY); - canvas.scale(zoom, zoom, mCenterX, mCenterY); - canvas.translate(mXOffset, mYOffset); - if (flip == FLIP.HORIZONTAL) { - Matrix flipper = getHorizontalMatrix(image.getWidth()); - canvas.drawBitmap(image, flipper, gPaint); - } else if (flip == FLIP.VERTICAL) { - Matrix flipper = getVerticalMatrix(image.getHeight()); - canvas.drawBitmap(image, flipper, gPaint); - } else if (flip == FLIP.BOTH) { - Matrix flipper = getVerticalMatrix(image.getHeight()); - flipper.postConcat(getHorizontalMatrix(image.getWidth())); - canvas.drawBitmap(image, flipper, gPaint); - } else { - canvas.drawBitmap(image, 0, 0, gPaint); - } - canvas.restore(); - - RectF cropBounds = getCropBoundsDisplayed(getLocalCropBounds()); - - Matrix m0 = new Matrix(); - m0.setRotate(getLocalRotation(), mCenterX, mCenterY); - float[] corners = getCornersFromRect(cropBounds); - m0.mapPoints(corners); - gPaint.setARGB(255, 255, 255, 255); - // TODO: pull out style to xml - gPaint.setStrokeWidth(3); - gPaint.setStyle(Paint.Style.STROKE); - drawClosedPath(canvas, gPaint, corners); - - canvas.save(); - canvas.rotate(getLocalRotation(), mCenterX, mCenterY); - RectF displayRect = getLocalDisplayBounds(); - float dWidth = displayRect.width(); - float dHeight = displayRect.height(); - RectF boundsRect = cropBounds; - gPaint.setARGB(128, 0, 0, 0); - gPaint.setStyle(Paint.Style.FILL); - canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint); - canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint); - canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom, - gPaint); - canvas.drawRect(boundsRect.right, boundsRect.top, dWidth, - boundsRect.bottom, gPaint); - canvas.rotate(-getLocalRotation(), mCenterX, mCenterY); - canvas.restore(); + drawTransformedBitmap(canvas, image, gPaint, false); } + } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java index 606e4770c..68df702ea 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java @@ -19,12 +19,13 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -47,7 +48,7 @@ public abstract class ImageGeometry extends ImageSlave { protected float mTouchCenterY; // Local geometry data - private GeometryMetadata mLocalGeoMetadata = null; + private GeometryMetadata mLocalGeometry = null; private RectF mLocalDisplayBounds = null; protected float mXOffset = 0; protected float mYOffset = 0; @@ -73,10 +74,20 @@ public abstract class ImageGeometry extends ImageSlave { calculateLocalScalingFactorAndOffset(); } + protected float computeScale(float width, float height) { + float imageWidth = mLocalGeometry.getPhotoBounds().width(); + float imageHeight = mLocalGeometry.getPhotoBounds().height(); + float zoom = width / imageWidth; + if (imageHeight > imageWidth) { + zoom = height / imageHeight; + } + return zoom; + } + private void calculateLocalScalingFactorAndOffset() { - if (mLocalGeoMetadata == null || mLocalDisplayBounds == null) + if (mLocalGeometry == null || mLocalDisplayBounds == null) return; - RectF imageBounds = mLocalGeoMetadata.getPhotoBounds(); + RectF imageBounds = mLocalGeometry.getPhotoBounds(); float imageWidth = imageBounds.width(); float imageHeight = imageBounds.height(); float displayWidth = mLocalDisplayBounds.width(); @@ -84,13 +95,9 @@ public abstract class ImageGeometry extends ImageSlave { mCenterX = displayWidth / 2; mCenterY = displayHeight / 2; - float zoom = displayWidth / imageWidth; - if (imageHeight > imageWidth) { - zoom = displayHeight / imageHeight; - } mYOffset = (displayHeight - imageHeight) / 2.0f; mXOffset = (displayWidth - imageWidth) / 2.0f; - mLocalGeoMetadata.setScaleFactor(zoom); + updateScale(); } @Override @@ -104,22 +111,18 @@ public abstract class ImageGeometry extends ImageSlave { invalidate(); } - private GeometryMetadata getMasterImageGeometryMetadataCopy() { - return getMaster().getGeometry(); - } - // Overwrites local with master protected void syncLocalToMasterGeometry() { - mLocalGeoMetadata = getMasterImageGeometryMetadataCopy(); + mLocalGeometry = getMaster().getGeometry(); calculateLocalScalingFactorAndOffset(); } protected RectF getLocalPhotoBounds() { - return mLocalGeoMetadata.getPhotoBounds(); + return mLocalGeometry.getPhotoBounds(); } protected RectF getLocalCropBounds() { - return mLocalGeoMetadata.getCropBounds(); + return mLocalGeometry.getPreviewCropBounds(); } protected RectF getLocalDisplayBounds() { @@ -127,39 +130,62 @@ public abstract class ImageGeometry extends ImageSlave { } protected float getLocalScale() { - return mLocalGeoMetadata.getScaleFactor(); + return mLocalGeometry.getScaleFactor(); } protected float getLocalRotation() { - return mLocalGeoMetadata.getRotation(); + return mLocalGeometry.getRotation(); } protected float getLocalStraighten() { - return mLocalGeoMetadata.getStraightenRotation(); + return mLocalGeometry.getStraightenRotation(); } protected void setLocalScale(float s) { - mLocalGeoMetadata.setScaleFactor(s); + mLocalGeometry.setScaleFactor(s); + } + + protected void updateScale() { + RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeometry.getPhotoBounds(), + getLocalStraighten()); + float zoom = computeScale(bounds.width(), bounds.height()); + setLocalScale(zoom); } protected void setLocalRotation(float r) { - mLocalGeoMetadata.setRotation(r); + mLocalGeometry.setRotation(r); + updateScale(); + } + + /** + * Constrains rotation to be in [0, 90, 180, 270]. + */ + protected int constrainedRotation(float rotation) { + int r = (int) ((rotation % 360) / 90); + r = (r < 0) ? (r + 4) : r; + return r * 90; + } + + protected Matrix getLocalGeoFlipMatrix(float width, float height) { + return mLocalGeometry.getFlipMatrix(width, height); } protected void setLocalStraighten(float r) { - mLocalGeoMetadata.setStraightenRotation(r); + mLocalGeometry.setStraightenRotation(r); + updateScale(); } protected void setLocalCropBounds(RectF c) { - mLocalGeoMetadata.setCropBounds(c); + mLocalGeometry.setCropBounds(c); + updateScale(); } protected FLIP getLocalFlip() { - return mLocalGeoMetadata.getFlipType(); + return mLocalGeometry.getFlipType(); } protected void setLocalFlip(FLIP flip) { - mLocalGeoMetadata.setFlipType(flip); + mLocalGeometry.setFlipType(flip); } protected float getTotalLocalRotation() { @@ -172,74 +198,21 @@ public abstract class ImageGeometry extends ImageSlave { return getMaster().mForegroundImage; } - protected static Matrix getHorizontalMatrix(int width) { - Matrix flipHorizontalMatrix = new Matrix(); - flipHorizontalMatrix.setScale(-1, 1); - flipHorizontalMatrix.postTranslate(width, 0); - return flipHorizontalMatrix; - } - - protected static Matrix getVerticalMatrix(int height) { - Matrix flipVerticalMatrix = new Matrix(); - flipVerticalMatrix.setScale(1, -1); - flipVerticalMatrix.postTranslate(0, height); - return flipVerticalMatrix; - } - - protected static Matrix getFlipMatrix(FLIP type, int width, int height) { - if (type == FLIP.HORIZONTAL) { - return getHorizontalMatrix(width); - } else if (type == FLIP.VERTICAL) { - return getVerticalMatrix(height); - } else if (type == FLIP.BOTH) { - Matrix flipper = getVerticalMatrix(height); - flipper.postConcat(getHorizontalMatrix(width)); - return flipper; - } else { - Matrix m = new Matrix(); - m.reset(); // identity - return m; - } - } - - protected static float clamp(float i, float low, float high) { - return Math.max(Math.min(i, high), low); - } - protected static float[] getCornersFromRect(RectF r) { // Order is: // 0------->1 - // ^ | - // | v + // ^ | + // | v // 3<-------2 float[] corners = { r.left, r.top, // 0 r.right, r.top, // 1 r.right, r.bottom,// 2 - r.left, r.bottom - };// 3 + r.left, r.bottom // 3 + }; return corners; } - // Returns maximal rectangular crop bound that still fits within - // the image bound after the image has been rotated. - protected static RectF findCropBoundForRotatedImg(RectF cropBound, - RectF imageBound, - float rotation, - float centerX, - float centerY) { - Matrix m = new Matrix(); - float[] cropEdges = getCornersFromRect(cropBound); - m.setRotate(rotation, centerX, centerY); - Matrix m0 = new Matrix(); - if (!m.invert(m0)) - return null; - m0.mapPoints(cropEdges); - getEdgePoints(imageBound, cropEdges); - m.mapPoints(cropEdges); - return trapToRect(cropEdges); - } - // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the // image bound rectangle, clamps it to the edge of the rectangle. protected static void getEdgePoints(RectF imageBound, float[] array) { @@ -251,24 +224,6 @@ public abstract class ImageGeometry extends ImageSlave { } } - protected static RectF trapToRect(float[] array) { - float dx0 = array[4] - array[0]; - float dy0 = array[5] - array[1]; - float dx1 = array[6] - array[2]; - float dy1 = array[7] - array[3]; - float l0 = dx0 * dx0 + dy0 * dy0; - float l1 = dx1 * dx1 + dy1 * dy1; - if (l0 > l1) { - RectF n = new RectF(array[2], array[3], array[6], array[7]); - n.sort(); - return n; - } else { - RectF n = new RectF(array[0], array[1], array[4], array[5]); - n.sort(); - return n; - } - } - protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) { Path crop = new Path(); crop.moveTo(points[0], points[1]); @@ -280,26 +235,14 @@ public abstract class ImageGeometry extends ImageSlave { return crop; } - protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) { - float x1 = l1[0]; - float x2 = l2[0]; - float y1 = l1[1]; - float y2 = l2[1]; - float xdelt = x2 - x1; - float ydelt = y2 - y1; - if (xdelt == 0 && ydelt == 0) - return null; - float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt) - / (xdelt * xdelt + ydelt * ydelt); - float[] ret = { - (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1)) - }; - return ret; - } - protected static void fixAspectRatio(RectF r, float w, float h) { float scale = Math.min(r.width() / w, r.height() / h); - r.set(r.left, r.top, scale * w, scale * h); + float centX = r.centerX(); + float centY = r.centerY(); + float hw = scale * w / 2; + float hh = scale * h / 2; + r.set(centX - hw, centY - hh, centX + hw, centY + hh); + } protected static float getNewHeightForWidthAspect(float width, float w, float h) { @@ -316,6 +259,7 @@ public abstract class ImageGeometry extends ImageSlave { if (visibility == View.VISIBLE) { mVisibilityGained = true; syncLocalToMasterGeometry(); + updateScale(); gainedVisibility(); } else { if (mVisibilityGained == true && mHasDrawn == true) { @@ -357,7 +301,7 @@ public abstract class ImageGeometry extends ImageSlave { setNoAction(); } if (getPanelController() != null) { - getPanelController().onNewValue((int) getLocalValue()); + getPanelController().onNewValue(getLocalValue()); } invalidate(); return true; @@ -396,116 +340,14 @@ public abstract class ImageGeometry extends ImageSlave { protected void saveAndSetPreset() { ImagePreset copy = new ImagePreset(getImagePreset()); - copy.setGeometry(mLocalGeoMetadata); + copy.setGeometry(mLocalGeometry); + copy.setHistoryName("Geometry"); + copy.setIsFx(false); setImagePreset(copy); } - @Override - public void updateImage() { - } - - protected void drawRegularFlippedBitmap(Canvas canvas, Bitmap image, Paint p) { - float zoom = getLocalScale(); - canvas.save(); - canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY); - canvas.scale(zoom, zoom, mCenterX, mCenterY); - canvas.translate(mXOffset, mYOffset); - Matrix flipper = getFlipMatrix(getLocalFlip(), image.getWidth(), image.getHeight()); - canvas.drawBitmap(image, flipper, p); - canvas.restore(); - } - - protected void drawRegularBitmap(Canvas canvas, Bitmap image, Paint p) { - float zoom = getLocalScale(); - canvas.save(); - canvas.rotate(getTotalLocalRotation(), mCenterX, mCenterY); - canvas.scale(zoom, zoom, mCenterX, mCenterY); - canvas.translate(mXOffset, mYOffset); - canvas.drawBitmap(image, 0, 0, p); - canvas.restore(); - } - - protected RectF getCropBoundsDisplayed() { - return getCropBoundsDisplayed(getLocalCropBounds()); - } - - protected RectF getCropBoundsDisplayed(RectF bounds) { - RectF crop = new RectF(bounds); - Matrix m = new Matrix(); - float zoom = getLocalScale(); - m.setScale(zoom, zoom, mCenterX, mCenterY); - m.preTranslate(mXOffset, mYOffset); - m.mapRect(crop); - return crop; - } - - protected static float[] correctAngles(float rotation, float straighten) { - float[] ret = { - rotation, straighten - }; - if (straighten >= MIN_STRAIGHTEN_ANGLE && straighten <= MAX_STRAIGHTEN_ANGLE - && (rotation % 90 == 0)) { - return ret; - } - float remainder = rotation % 90; - float newRot = (int) (rotation / 90); - float tempStraighten = straighten + remainder; - newRot += (int) (tempStraighten / 90); - tempStraighten %= 90; - if (tempStraighten > MAX_STRAIGHTEN_ANGLE) { - tempStraighten -= 90; - newRot += 1; - } else if (tempStraighten < MIN_STRAIGHTEN_ANGLE) { - tempStraighten += 90; - newRot -= 1; - } - ret[0] = newRot * 90; - ret[1] = tempStraighten; - return ret; - } - - protected void correctStraightenRotateAngles() { - float straighten = getLocalStraighten(); - float rotation = getLocalRotation(); - float[] angles = correctAngles(rotation, straighten); - setLocalStraighten(angles[1]); - setLocalRotation(angles[0]); - } - - protected static Matrix getCropRotationMatrix(float rotation) { - Matrix m = new Matrix(); - m.setRotate(rotation); - if (!m.rectStaysRect()) { - return null; - } - return m; - } - - protected RectF getStraightenCropBounds() { - RectF imageRect = getLocalPhotoBounds(); - Matrix m = new Matrix(); - float lRot = getLocalRotation(); - m.setRotate(lRot); - if (!m.rectStaysRect()) { - correctStraightenRotateAngles(); - m.setRotate(getLocalRotation()); - } - RectF crop = getStraightenCropBounds(imageRect, getLocalStraighten()); - setLocalCropBounds(crop); - if (!m.mapRect(crop)) { - return null; - } - return crop; - } - - protected RectF getStraightenCropBounds(RectF imageRect, float straightenAngle) { - RectF boundsRect = getUntranslatedStraightenCropBounds(imageRect, straightenAngle); - RectF nonRotateImage = getLocalPhotoBounds(); - Matrix m1 = new Matrix(); - m1.setTranslate(nonRotateImage.centerX() - boundsRect.centerX(), nonRotateImage.centerY() - - boundsRect.centerY()); - m1.mapRect(boundsRect); - return boundsRect; + protected static float clamp(float i, float low, float high) { + return Math.max(Math.min(i, high), low); } public static RectF getUntranslatedStraightenCropBounds(RectF imageRect, float straightenAngle) { @@ -532,15 +374,123 @@ public abstract class ImageGeometry extends ImageSlave { return new RectF(left, top, right, bottom); } - protected void drawShadows(Canvas canvas, RectF innerBounds, RectF outerBounds, Paint p) { - float dWidth = outerBounds.width(); - float dHeight = outerBounds.height(); - canvas.drawRect(0, 0, dWidth, innerBounds.top, p); - canvas.drawRect(0, innerBounds.bottom, dWidth, dHeight, p); - canvas.drawRect(0, innerBounds.top, innerBounds.left, innerBounds.bottom, + protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) { + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + float w = r.left * 2 + r.width(); + float h = r.top * 2 + r.height(); + return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate); + } + + protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) { + canvas.save(); + canvas.drawBitmap(bitmap, m, paint); + canvas.restore(); + } + + protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) { + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff); + drawImageBitmap(canvas, bitmap, paint, m); + } + + protected RectF straightenBounds() { + RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten()); + Matrix m = getGeoMatrix(bounds, true); + m.mapRect(bounds); + return bounds; + } + + protected void drawStraighten(Canvas canvas, Paint paint) { + RectF bounds = straightenBounds(); + canvas.save(); + canvas.drawRect(bounds, paint); + canvas.restore(); + } + + protected RectF unrotatedCropBounds() { + RectF bounds = getLocalCropBounds(); + RectF pbounds = getLocalPhotoBounds(); + float scale = computeScale(getWidth(), getHeight()); + float yoff = getHeight() / 2; + float xoff = getWidth() / 2; + Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale, xoff, yoff, 0); + m.mapRect(bounds); + return bounds; + } + + protected RectF cropBounds() { + RectF bounds = getLocalCropBounds(); + Matrix m = getGeoMatrix(getLocalPhotoBounds(), true); + m.mapRect(bounds); + return bounds; + } + + // Fails for non-90 degree + protected void drawCrop(Canvas canvas, Paint paint) { + RectF bounds = cropBounds(); + canvas.save(); + canvas.drawRect(bounds, paint); + canvas.restore(); + } + + protected void drawCropSafe(Canvas canvas, Paint paint) { + Matrix m = getGeoMatrix(getLocalPhotoBounds(), true); + RectF crop = getLocalCropBounds(); + if (!m.rectStaysRect()) { + float[] corners = getCornersFromRect(crop); + m.mapPoints(corners); + drawClosedPath(canvas, paint, corners); + } else { + m.mapRect(crop); + Path path = new Path(); + path.addRect(crop, Path.Direction.CCW); + canvas.drawPath(path, paint); + } + } + + protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) { + paint.setARGB(255, 0, 0, 0); + drawImageBitmap(canvas, bitmap, paint); + paint.setColor(Color.WHITE); + paint.setStyle(Style.STROKE); + paint.setStrokeWidth(2); + drawCropSafe(canvas, paint); + paint.setARGB(128, 0, 0, 0); + paint.setStyle(Paint.Style.FILL); + drawShadows(canvas, paint, unrotatedCropBounds()); + } + + protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) { + RectF display = new RectF(0, 0, getWidth(), getHeight()); + drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2, + getHeight() / 2); + } + + protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds, + float rotation, float centerX, float centerY) { + canvas.save(); + canvas.rotate(rotation, centerX, centerY); + + float x = (outerBounds.left - outerBounds.right); + float y = (outerBounds.top - outerBounds.bottom); + float longest = (float) Math.sqrt(x * x + y * y) / 2; + float minX = centerX - longest; + float maxX = centerX + longest; + float minY = centerY - longest; + float maxY = centerY + longest; + canvas.drawRect(minX, minY, innerBounds.right, innerBounds.top, p); + canvas.drawRect(minX, innerBounds.top, innerBounds.left, maxY, p); + canvas.drawRect(innerBounds.left, innerBounds.bottom, maxX, maxY, p); - canvas.drawRect(innerBounds.right, innerBounds.top, dWidth, + canvas.drawRect(innerBounds.right, minY, maxX, innerBounds.bottom, p); + canvas.rotate(-rotation, centerX, centerY); + canvas.restore(); } @Override diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java index c0999e3d0..089e11732 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java @@ -19,12 +19,8 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; public class ImageRotate extends ImageGeometry { private static final float MATH_PI = (float) Math.PI; @@ -32,8 +28,7 @@ public class ImageRotate extends ImageGeometry { private float mBaseAngle = 0; private float mAngle = 0; - private RectF mLocalBoundsCopy = null; - private boolean mSnapToNinety = true; + private final boolean mSnapToNinety = true; private static final String LOGTAG = "ImageRotate"; public ImageRotate(Context context, AttributeSet attrs) { @@ -98,21 +93,6 @@ public class ImageRotate extends ImageGeometry { } @Override - public void resetParameter() { - super.resetParameter(); - mLocalBoundsCopy = getLocalCropBounds(); - } - - @Override - protected void gainedVisibility() { - mLocalBoundsCopy = getLocalCropBounds(); - } - - @Override - protected void lostVisibility() { - } - - @Override protected int getLocalValue() { return (int) getLocalRotation(); } @@ -123,39 +103,6 @@ public class ImageRotate extends ImageGeometry { gPaint.setFilterBitmap(true); gPaint.setDither(true); gPaint.setARGB(255, 255, 255, 255); - - drawRegularFlippedBitmap(canvas, image, gPaint); - - RectF cropBounds = getCropBoundsDisplayed(mLocalBoundsCopy); - - Matrix m0 = new Matrix(); - m0.setRotate(getLocalRotation(), mCenterX, mCenterY); - float[] corners = getCornersFromRect(cropBounds); - m0.mapPoints(corners); - - gPaint.setARGB(255, 255, 255, 255); - gPaint.setStrokeWidth(3); - gPaint.setStyle(Paint.Style.STROKE); - drawClosedPath(canvas, gPaint, corners); - - canvas.save(); - canvas.rotate(getLocalRotation(), mCenterX, mCenterY); - RectF displayRect = getLocalDisplayBounds(); - float dWidth = displayRect.width(); - float dHeight = displayRect.height(); - RectF boundsRect = cropBounds; - gPaint.setARGB(128, 0, 0, 0); - // TODO: move style to xml - gPaint.setStyle(Paint.Style.FILL); - canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint); - canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint); - canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom, - gPaint); - canvas.drawRect(boundsRect.right, boundsRect.top, dWidth, - boundsRect.bottom, gPaint); - canvas.rotate(-getLocalRotation(), mCenterX, mCenterY); - canvas.restore(); - + drawTransformedBitmap(canvas, image, gPaint, true); } - } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index aba731293..3fd6d4f85 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -57,6 +57,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi private boolean mDirtyGeometry = true; private Bitmap mBackgroundImage = null; + // TODO: remove protected here, it should be private protected Bitmap mForegroundImage = null; protected Bitmap mFilteredImage = null; @@ -87,6 +88,14 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi private SeekBar mSeekBar = null; private PanelController mController = null; + public static void setTextSize(int value) { + mTextSize = value; + } + + public static void setTextPadding(int value) { + mTextPadding = value; + } + private final Handler mHandler = new Handler(); public void select() { @@ -232,7 +241,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi return mImagePreset; } - public Bitmap getOriginalFrontBitmap() { + protected Bitmap getOriginalFrontBitmap() { if (mImageLoader != null) { return mImageLoader.getOriginalBitmapLarge(); } @@ -277,7 +286,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi canvas.drawRect(textRect, mPaint); mPaint.setARGB(255, 200, 200, 200); canvas.drawText(getImagePreset().name(), mTextPadding, - 10 + mTextPadding, mPaint); + 1.5f * mTextPadding, mPaint); } if (showControls()) { @@ -393,7 +402,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi } } - protected void setDirtyGeometryFlag() { + private void setDirtyGeometryFlag() { mDirtyGeometry = true; } @@ -410,6 +419,11 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi return; float w = image.getWidth(); float h = image.getHeight(); + GeometryMetadata geo = getImagePreset().mGeoData; + RectF pb = geo.getPhotoBounds(); + if (w == pb.width() && h == pb.height()) { + return; + } RectF r = new RectF(0, 0, w, h); getImagePreset().mGeoData.setPhotoBounds(r); getImagePreset().mGeoData.setCropBounds(r); @@ -419,6 +433,7 @@ public class ImageShow extends View implements SliderListener, OnSeekBarChangeLi public void updateImage() { mForegroundImage = getOriginalFrontBitmap(); imageSizeChanged(mForegroundImage); // TODO: should change to filtered + setDirtyGeometryFlag(); } public void updateFilteredImage(Bitmap bitmap) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java index d0c67f783..90986f912 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallBorder.java @@ -18,7 +18,7 @@ public class ImageSmallBorder extends ImageSmallFilter { protected final int mSelectedBackgroundColor = Color.WHITE; protected final int mInnerBorderColor = Color.BLACK; protected final int mInnerBorderWidth = 2; - protected final float mImageScaleFactor = 2.5f; + protected final float mImageScaleFactor = 3.5f; public ImageSmallBorder(Context context) { super(context); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java index 0b0c2c475..a5d99a098 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java @@ -27,12 +27,20 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener private ImageSmallFilter mPreviousImageSmallFilter = null; // TODO: move this to xml. - protected final int mMargin = 12; - protected final int mTextMargin = 8; + protected static int mMargin = 12; + protected static int mTextMargin = 8; protected final int mBackgroundColor = Color.argb(255, 30, 32, 40); protected final int mSelectedBackgroundColor = Color.WHITE; protected final int mTextColor = Color.WHITE; + public static void setMargin(int value) { + mMargin = value; + } + + public static void setTextMargin(int value) { + mTextMargin = value; + } + public ImageSmallFilter(Context context, AttributeSet attrs) { super(context, attrs); setOnClickListener(this); @@ -46,6 +54,7 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener public void setImageFilter(ImageFilter filter) { mImageFilter = filter; mImagePreset = new ImagePreset(); + mImagePreset.setName(filter.getName()); mImagePreset.add(mImageFilter); } @@ -99,7 +108,14 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener } @Override - public Bitmap getOriginalFrontBitmap() { + public void updateImage() { + // We don't want to warn listeners here that the image size has changed, because + // we'll be working with the small image... + mForegroundImage = getOriginalFrontBitmap(); + } + + @Override + protected Bitmap getOriginalFrontBitmap() { if (mImageLoader == null) { return null; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java index 2b15bf373..2fd6b9b35 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java @@ -19,14 +19,12 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; -import android.view.MotionEvent; -import com.android.gallery3d.filtershow.presets.ImagePreset; +import com.android.gallery3d.filtershow.imageshow.ImageGeometry.MODES; public class ImageStraighten extends ImageGeometry { @@ -50,11 +48,17 @@ public class ImageStraighten extends ImageGeometry { mBaseAngle = mAngle = getLocalStraighten(); } + private void setCropToStraighten(){ + setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), + getLocalStraighten())); + } + @Override protected void setActionMove(float x, float y) { super.setActionMove(x, y); computeValue(); setLocalStraighten(mAngle); + setCropToStraighten(); } private float angleFor(float dx, float dy) { @@ -79,13 +83,19 @@ public class ImageStraighten extends ImageGeometry { } @Override - protected void gainedVisibility() { - correctStraightenRotateAngles(); + protected void lostVisibility() { + saveAndSetPreset(); } @Override - protected void lostVisibility() { - saveAndSetPreset(); + protected void gainedVisibility(){ + setCropToStraighten(); + } + + @Override + protected void setActionUp() { + super.setActionUp(); + setCropToStraighten(); } @Override @@ -104,53 +114,24 @@ public class ImageStraighten extends ImageGeometry { @Override protected void drawShape(Canvas canvas, Bitmap image) { - gPaint.setAntiAlias(true); - gPaint.setFilterBitmap(true); - gPaint.setDither(true); - gPaint.setARGB(255, 255, 255, 255); + drawTransformedBitmap(canvas, image, gPaint, false); - // Draw fully rotated image. - drawRegularFlippedBitmap(canvas, image, gPaint); - - // Get cropping frame - RectF boundsRect = getStraightenCropBounds(); - - Matrix m1 = new Matrix(); - float zoom = getLocalScale(); - // Center and scale - m1.setScale(zoom, zoom, mCenterX, mCenterY); - m1.preTranslate(mCenterX - boundsRect.centerX(), mCenterY - boundsRect.centerY()); - m1.mapRect(boundsRect); - RectF displayRect = getLocalDisplayBounds(); - float dWidth = displayRect.width(); - float dHeight = displayRect.height(); - - // Draw shadows - gPaint.setARGB(128, 0, 0, 0); - gPaint.setStyle(Paint.Style.FILL); - - // TODO: move to xml file - canvas.drawRect(0, 0, dWidth, boundsRect.top, gPaint); - canvas.drawRect(0, boundsRect.bottom, dWidth, dHeight, gPaint); - canvas.drawRect(0, boundsRect.top, boundsRect.left, boundsRect.bottom, - gPaint); - canvas.drawRect(boundsRect.right, boundsRect.top, dWidth, - boundsRect.bottom, gPaint); - - // Draw crop frame + // Draw the grid + RectF bounds = straightenBounds(); Path path = new Path(); - path.addRect(boundsRect, Path.Direction.CCW); + path.addRect(bounds, Path.Direction.CCW); gPaint.setARGB(255, 255, 255, 255); gPaint.setStrokeWidth(3); - gPaint.setStyle(Paint.Style.STROKE); - canvas.drawPath(path, gPaint); - gPaint.setStyle(Paint.Style.FILL_AND_STROKE); - // Draw grid + RectF display = getLocalDisplayBounds(); + float dWidth = display.width(); + float dHeight = display.height(); + if (mMode == MODES.MOVE) { canvas.save(); canvas.clipPath(path); + int n = 16; float step = dWidth / n; float p = 0; diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java b/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java new file mode 100644 index 000000000..2666cb73d --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java @@ -0,0 +1,34 @@ +package com.android.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; + +/** + * TODO: Insert description here. (generated by hoford) + */ +public class ImageWithIcon extends ImageSmallFilter { + /** + * @param context + */ + public ImageWithIcon(Context context) { + super(context); + // TODO(hoford): Auto-generated constructor stub + } + + private Bitmap bitmap; + + public void setIcon(Bitmap bitmap){ + this.bitmap = bitmap; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (bitmap != null) { + Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth()); + drawImage(canvas, bitmap, d); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java index ec0ed7402..42dbcb90e 100644 --- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java @@ -35,6 +35,11 @@ public class ImagePreset { setup(); } + public ImagePreset(ImagePreset source, String historyName) { + this(source); + if (historyName!=null) setHistoryName(historyName); + } + public ImagePreset(ImagePreset source) { try { if (source.mImageBorder != null) { @@ -57,7 +62,7 @@ public class ImagePreset { mGeoData.set(m); } - public void setBorder(ImageFilter filter) { + private void setBorder(ImageFilter filter) { mImageBorder = filter; } @@ -117,7 +122,38 @@ public class ImagePreset { } public void add(ImageFilter filter) { - mFilters.add(filter); + + if (filter.getFilterType() == ImageFilter.TYPE_BORDER){ + setHistoryName("Border"); + setBorder(filter); + } else if (filter.getFilterType() == ImageFilter.TYPE_FX){ + + boolean found = false; + for (int i = 0; i < mFilters.size(); i++) { + byte type = mFilters.get(i).getFilterType(); + if (found) { + if (type != ImageFilter.TYPE_VIGNETTE){ + mFilters.remove(i); + continue; + } + } + if (type==ImageFilter.TYPE_FX){ + mFilters.remove(i); + mFilters.add(i, filter); + setHistoryName(filter.getName()); + found = true; + } + + } + if (!found) { + mFilters.add(filter); + setHistoryName(filter.getName()); + } + } else { + mFilters.add(filter); + setHistoryName(filter.getName()); + } + } public void remove(String filterName) { diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePresetFX.java b/src/com/android/gallery3d/filtershow/presets/ImagePresetFX.java index 326471ef3..fa207a6d0 100644 --- a/src/com/android/gallery3d/filtershow/presets/ImagePresetFX.java +++ b/src/com/android/gallery3d/filtershow/presets/ImagePresetFX.java @@ -22,7 +22,7 @@ public class ImagePresetFX extends ImagePreset { public void setup() { if (fxBitmap != null) { - mFilters.add(new ImageFilterFx(fxBitmap)); + mFilters.add(new ImageFilterFx(fxBitmap,name)); } } diff --git a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java index 7f0b0437d..51ed7fb20 100644 --- a/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java +++ b/src/com/android/gallery3d/filtershow/ui/ImageButtonTitle.java @@ -1,16 +1,15 @@ package com.android.gallery3d.filtershow.ui; -import com.android.gallery3d.R; - import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; -import android.util.Log; import android.widget.ImageButton; +import com.android.gallery3d.R; + public class ImageButtonTitle extends ImageButton { private static final String LOGTAG = "ImageButtonTitle"; private String mText = null; @@ -18,6 +17,14 @@ public class ImageButtonTitle extends ImageButton { private static int mTextPadding = 20; private static Paint gPaint = new Paint(); + public static void setTextSize(int value) { + mTextSize = value; + } + + public static void setTextPadding(int value) { + mTextPadding = value; + } + public ImageButtonTitle(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = getContext().obtainStyledAttributes( @@ -26,6 +33,7 @@ public class ImageButtonTitle extends ImageButton { mText = a.getString(R.styleable.ImageButtonTitle_android_text); } + @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); if (mText != null) { diff --git a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java index fb18bcf8f..660a4fadd 100644 --- a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java +++ b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java @@ -240,17 +240,20 @@ public class ImageCurves extends ImageSlave { applyNewCurve(); // Log.v(LOGTAG, "ACTION UP, mCurrentControlPoint set to null!"); mCurrentControlPoint = null; - ImagePreset copy = new ImagePreset(getImagePreset()); - + String name = null; if (mUseRed && mUseGreen && mUseBlue) { - copy.setHistoryName("Curves (RGB)"); + name = "Curves (RGB)"; } else if (mUseRed) { - copy.setHistoryName("Curves (Red)"); + name = "Curves (Red)"; } else if (mUseGreen) { - copy.setHistoryName("Curves (Green)"); + name = "Curves (Green)"; } else if (mUseBlue) { - copy.setHistoryName("Curves (Blue)"); + name = "Curves (Blue)"; } + + + ImagePreset copy = new ImagePreset(getImagePreset(),name); + copy.setIsFx(false); mImageLoader.getHistory().insert(copy, 0); diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java index c5aba53f9..8cebdddad 100644 --- a/src/com/android/gallery3d/ui/ActionModeHandler.java +++ b/src/com/android/gallery3d/ui/ActionModeHandler.java @@ -227,7 +227,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi int type = 0; for (Path path : unexpandedPaths) { if (jc.isCancelled()) return 0; - int support = manager.getSupportedOperations(path); + int support = manager.getSupportedOperations(path, true); type |= manager.getMediaType(path); operation &= support; } @@ -297,7 +297,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi final Intent intent = new Intent(); for (Path path : expandedPaths) { if (jc.isCancelled()) return null; - int support = manager.getSupportedOperations(path); + int support = manager.getSupportedOperations(path, true); type |= manager.getMediaType(path); if ((support & MediaObject.SUPPORT_SHARE) != 0) { diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java index 5c246e8e3..d5337f00d 100644 --- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java +++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java @@ -127,10 +127,10 @@ public class AlbumSetSlotRenderer extends AbstractSlotRenderer { GLCanvas canvas, int index, AlbumSetEntry entry, int width, int height) { int renderRequestFlags = 0; if (entry.album != null && entry.album.isCameraRoll()) { - int minDim = Math.min(width, height); - int dim = minDim / 2; - int pos = (minDim - dim) / 2; - mCameraOverlay.draw(canvas, pos, pos, dim, dim); + int uncoveredHeight = height - mLabelSpec.labelBackgroundHeight; + int dim = uncoveredHeight / 2; + mCameraOverlay.draw(canvas, (width - dim) / 2, + (uncoveredHeight - dim) / 2, dim, dim); } if (mPressedIndex == index) { if (mAnimatePressedUp) { diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java index 1651b4361..13b610b23 100644 --- a/src/com/android/gallery3d/ui/GLRoot.java +++ b/src/com/android/gallery3d/ui/GLRoot.java @@ -16,6 +16,7 @@ package com.android.gallery3d.ui; +import android.content.Context; import android.graphics.Matrix; import com.android.gallery3d.anim.CanvasAnimation; @@ -45,4 +46,6 @@ public interface GLRoot { public void freeze(); public void unfreeze(); public void setLightsOutMode(boolean enabled); + + public Context getContext(); } diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java index 214d0e017..c6bf535db 100644 --- a/src/com/android/gallery3d/ui/PhotoView.java +++ b/src/com/android/gallery3d/ui/PhotoView.java @@ -17,6 +17,7 @@ package com.android.gallery3d.ui; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; @@ -546,10 +547,20 @@ public class PhotoView extends GLView { } private int getPanoramaRotation() { - // Panorama only support rotations of 0 and 90, so if it is greater - // than that flip the output surface texture to compensate - if (mDisplayRotation > 180) + // This function is magic + // The issue here is that Pano makes bad assumptions about rotation and + // orientation. The first is it assumes only two rotations are possible, + // 0 and 90. Thus, if display rotation is >= 180, we invert the output. + // The second is that it assumes landscape is a 90 rotation from portrait, + // however on landscape devices this is not true. Thus, if we are in portrait + // on a landscape device, we need to invert the output + int orientation = getGLRoot().getContext().getResources().getConfiguration().orientation; + boolean invertPortrait = (orientation == Configuration.ORIENTATION_PORTRAIT + && (mDisplayRotation == 90 || mDisplayRotation == 270)); + boolean invert = (mDisplayRotation >= 180); + if (invert != invertPortrait) { return (mCompensation + 180) % 360; + } return mCompensation; } @@ -1318,6 +1329,7 @@ public class PhotoView extends GLView { public void resume() { mTileView.prepareTextures(); + mPositionController.skipToFinalPosition(); } // move to the camera preview and show controls after resume diff --git a/src_pd/com/android/gallery3d/util/LightCycleHelper.java b/src_pd/com/android/gallery3d/util/LightCycleHelper.java index cc8ea20b9..995eac8b1 100644 --- a/src_pd/com/android/gallery3d/util/LightCycleHelper.java +++ b/src_pd/com/android/gallery3d/util/LightCycleHelper.java @@ -27,13 +27,20 @@ import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.app.StitchingProgressManager; public class LightCycleHelper { + public static class PanoramaMetadata { + // Whether a panorama viewer should be used + public final boolean mUsePanoramaViewer; + // Whether a panorama is 360 degrees + public final boolean mIsPanorama360; - public static void setupCaptureIntent(Context context, Intent it, String outputDir) { - /* Do nothing */ + public PanoramaMetadata(boolean usePanoramaViewer, boolean isPanorama360) { + mUsePanoramaViewer = usePanoramaViewer; + mIsPanorama360 = isPanorama360; + } } - public static boolean hasLightCycleView(Context context) { - return false; + public static void setupCaptureIntent(Context context, Intent it, String outputDir) { + /* Do nothing */ } public static boolean hasLightCycleCapture(Context context) { @@ -44,12 +51,8 @@ public class LightCycleHelper { /* Do nothing */ } - public static boolean isPanorama(ContentResolver contentResolver, Uri uri) { - return false; - } - - public static boolean isPanorama360(Context context, Uri uri) { - return false; + public static PanoramaMetadata getPanoramaMetadata(Context context, Uri uri) { + return null; } public static CameraModule createPanoramaModule() { diff --git a/tests/src/com/android/gallery3d/ui/GLRootMock.java b/tests/src/com/android/gallery3d/ui/GLRootMock.java index 467edfc7f..b1c4355d0 100644 --- a/tests/src/com/android/gallery3d/ui/GLRootMock.java +++ b/tests/src/com/android/gallery3d/ui/GLRootMock.java @@ -16,6 +16,7 @@ package com.android.gallery3d.ui; +import android.content.Context; import android.graphics.Matrix; import com.android.gallery3d.anim.CanvasAnimation; @@ -42,4 +43,5 @@ public class GLRootMock implements GLRoot { public void freeze() {} public void unfreeze() {} public void setLightsOutMode(boolean enabled) {} + public Context getContext() { return null; } } diff --git a/tests/src/com/android/gallery3d/ui/GLRootStub.java b/tests/src/com/android/gallery3d/ui/GLRootStub.java index 0f3a00164..7f134dbf1 100644 --- a/tests/src/com/android/gallery3d/ui/GLRootStub.java +++ b/tests/src/com/android/gallery3d/ui/GLRootStub.java @@ -16,6 +16,7 @@ package com.android.gallery3d.ui; +import android.content.Context; import android.graphics.Matrix; import com.android.gallery3d.anim.CanvasAnimation; @@ -35,4 +36,5 @@ public class GLRootStub implements GLRoot { public void freeze() {} public void unfreeze() {} public void setLightsOutMode(boolean enabled) {} + public Context getContext() { return null; } } |