summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/data/CameraDataAdapter.java335
-rw-r--r--src/com/android/camera/data/LocalData.java421
-rw-r--r--src/com/android/camera/ui/FilmStripGestureRecognizer.java107
-rw-r--r--src/com/android/camera/ui/FilmStripView.java846
-rw-r--r--src/com/android/gallery3d/app/CommonControllerOverlay.java4
-rw-r--r--src/com/android/gallery3d/app/MoviePlayer.java12
-rw-r--r--src/com/android/gallery3d/app/TimeBar.java4
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java32
-rw-r--r--src/com/android/gallery3d/filtershow/cache/CachingPipeline.java41
-rw-r--r--src/com/android/gallery3d/filtershow/controller/BasicParameterStyle.java1
-rw-r--r--src/com/android/gallery3d/filtershow/controller/BasicSlider.java1
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropMath.java9
-rw-r--r--src/com/android/gallery3d/filtershow/data/FilterStackDBHelper.java101
-rw-r--r--src/com/android/gallery3d/filtershow/data/FilterStackSource.java143
-rw-r--r--src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java39
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java21
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java1
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java1
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java5
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java1
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java31
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java24
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java41
-rw-r--r--src/com/android/gallery3d/filtershow/filters/FiltersManagerInterface.java21
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilter.java17
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java11
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java6
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java4
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java2
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java2
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java49
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java5
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java2
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java3
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java6
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java2
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java113
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/MasterImage.java2
-rw-r--r--src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java24
-rw-r--r--src/com/android/gallery3d/filtershow/presets/ImagePreset.java146
-rw-r--r--src/com/android/gallery3d/filtershow/presets/PipelineInterface.java31
-rw-r--r--src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java3
-rw-r--r--src/com/android/gallery3d/filtershow/tools/XmpPresets.java133
-rw-r--r--src/com/android/gallery3d/ui/MenuExecutor.java2
-rw-r--r--src/com/android/gallery3d/util/SaveVideoFileUtils.java19
-rw-r--r--src/com/android/photos/data/BitmapDecoder.java2
54 files changed, 2732 insertions, 118 deletions
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
new file mode 100644
index 000000000..1fb9465a6
--- /dev/null
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2013 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.camera.data;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
+import android.util.Log;
+import android.view.View;
+
+import com.android.camera.Storage;
+import com.android.camera.ui.FilmStripView;
+import com.android.camera.ui.FilmStripView.ImageData;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A FilmStrip.DataProvider that provide data in the camera folder.
+ *
+ * The given view for camera preview won't be added until the preview info
+ * has been set by setCameraPreviewInfo(int, int).
+ */
+public class CameraDataAdapter implements FilmStripView.DataAdapter {
+ private static final String TAG = CameraDataAdapter.class.getSimpleName();
+
+ private static final int DEFAULT_DECODE_SIZE = 3000;
+ private static final String[] CAMERA_PATH = { Storage.DIRECTORY + "%" };
+
+ private List<LocalData> mImages;
+
+ private Listener mListener;
+ private View mCameraPreviewView;
+ private Drawable mPlaceHolder;
+
+ private int mSuggestedWidth = DEFAULT_DECODE_SIZE;
+ private int mSuggestedHeight = DEFAULT_DECODE_SIZE;
+
+ public CameraDataAdapter(Drawable placeHolder) {
+ mPlaceHolder = placeHolder;
+ }
+
+ public void setCameraPreviewInfo(View cameraPreview, int width, int height) {
+ mCameraPreviewView = cameraPreview;
+ addOrReplaceCameraData(buildCameraImageData(width, height));
+ }
+
+ public void requestLoad(ContentResolver resolver) {
+ QueryTask qtask = new QueryTask();
+ qtask.execute(resolver);
+ }
+
+ @Override
+ public int getTotalNumber() {
+ if (mImages == null) {
+ return 0;
+ }
+ return mImages.size();
+ }
+
+ @Override
+ public ImageData getImageData(int id) {
+ if (mImages == null || id >= mImages.size() || id < 0) {
+ return null;
+ }
+ return mImages.get(id);
+ }
+
+ @Override
+ public void suggestDecodeSize(int w, int h) {
+ if (w <= 0 || h <= 0) {
+ mSuggestedWidth = mSuggestedHeight = DEFAULT_DECODE_SIZE;
+ } else {
+ mSuggestedWidth = (w < DEFAULT_DECODE_SIZE ? w : DEFAULT_DECODE_SIZE);
+ mSuggestedHeight = (h < DEFAULT_DECODE_SIZE ? h : DEFAULT_DECODE_SIZE);
+ }
+ }
+
+ @Override
+ public View getView(Context c, int dataID) {
+ if (mImages == null) {
+ return null;
+ }
+ if (dataID >= mImages.size() || dataID < 0) {
+ return null;
+ }
+
+ return mImages.get(dataID).getView(
+ c, mSuggestedWidth, mSuggestedHeight, mPlaceHolder);
+ }
+
+ @Override
+ public void setListener(Listener listener) {
+ mListener = listener;
+ if (mImages != null) {
+ mListener.onDataLoaded();
+ }
+ }
+
+ private LocalData buildCameraImageData(int width, int height) {
+ LocalData d = new CameraPreviewData(width, height);
+ return d;
+ }
+
+ private void addOrReplaceCameraData(LocalData data) {
+ if (mImages == null) {
+ mImages = new ArrayList<LocalData>();
+ }
+ if (mImages.size() == 0) {
+ // No data at all.
+ mImages.add(0, data);
+ if (mListener != null) {
+ mListener.onDataLoaded();
+ }
+ return;
+ }
+
+ LocalData first = mImages.get(0);
+ if (first.getType() == ImageData.TYPE_CAMERA_PREVIEW) {
+ // Replace the old camera data.
+ mImages.set(0, data);
+ if (mListener != null) {
+ mListener.onDataUpdated(new UpdateReporter() {
+ @Override
+ public boolean isDataRemoved(int id) {
+ return false;
+ }
+
+ @Override
+ public boolean isDataUpdated(int id) {
+ if (id == 0) {
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+ } else {
+ // Add a new camera data.
+ mImages.add(0, data);
+ if (mListener != null) {
+ mListener.onDataLoaded();
+ }
+ }
+ }
+
+ private class QueryTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
+ @Override
+ protected List<LocalData> doInBackground(ContentResolver... resolver) {
+ List<LocalData> l = new ArrayList<LocalData>();
+ // Photos
+ Cursor c = resolver[0].query(
+ Images.Media.EXTERNAL_CONTENT_URI,
+ LocalData.Photo.QUERY_PROJECTION,
+ MediaStore.Images.Media.DATA + " like ? ", CAMERA_PATH,
+ LocalData.Photo.QUERY_ORDER);
+ if (c != null && c.moveToFirst()) {
+ // build up the list.
+ while (true) {
+ LocalData data = LocalData.Photo.buildFromCursor(c);
+ if (data != null) {
+ l.add(data);
+ } else {
+ Log.e(TAG, "Error loading data:"
+ + c.getString(LocalData.Photo.COL_DATA));
+ }
+ if (c.isLast()) {
+ break;
+ }
+ c.moveToNext();
+ }
+ }
+ if (c != null) {
+ c.close();
+ }
+
+ c = resolver[0].query(
+ Video.Media.EXTERNAL_CONTENT_URI,
+ LocalData.Video.QUERY_PROJECTION,
+ MediaStore.Video.Media.DATA + " like ? ", CAMERA_PATH,
+ LocalData.Video.QUERY_ORDER);
+ if (c != null && c.moveToFirst()) {
+ // build up the list.
+ c.moveToFirst();
+ while (true) {
+ LocalData data = LocalData.Video.buildFromCursor(c);
+ if (data != null) {
+ l.add(data);
+ Log.v(TAG, "video data added:" + data);
+ } else {
+ Log.e(TAG, "Error loading data:"
+ + c.getString(LocalData.Video.COL_DATA));
+ }
+ if (!c.isLast()) {
+ c.moveToNext();
+ } else {
+ break;
+ }
+ }
+ }
+ if (c != null) {
+ c.close();
+ }
+
+ if (l.size() == 0) return null;
+
+ Collections.sort(l, new LocalData.NewestFirstComparator());
+ return l;
+ }
+
+ @Override
+ protected void onPostExecute(List<LocalData> l) {
+ boolean changed = (l != mImages);
+ LocalData cameraData = null;
+ if (mImages != null && mImages.size() > 0) {
+ cameraData = mImages.get(0);
+ if (cameraData.getType() != ImageData.TYPE_CAMERA_PREVIEW) {
+ cameraData = null;
+ }
+ }
+
+ mImages = l;
+ if (cameraData != null) {
+ // camera view exists, so we make sure at least 1 data is in the list.
+ if (mImages == null) {
+ mImages = new ArrayList<LocalData>();
+ }
+ mImages.add(0, cameraData);
+ if (mListener != null) {
+ // Only the camera data is not changed, everything else is changed.
+ mListener.onDataUpdated(new UpdateReporter() {
+ @Override
+ public boolean isDataRemoved(int id) {
+ return false;
+ }
+
+ @Override
+ public boolean isDataUpdated(int id) {
+ if (id == 0) return false;
+ return true;
+ }
+ });
+ }
+ } else {
+ // both might be null.
+ if (changed) {
+ mListener.onDataLoaded();
+ }
+ }
+ }
+ }
+
+ private class CameraPreviewData implements LocalData {
+ private int width;
+ private int height;
+
+ CameraPreviewData(int w, int h) {
+ width = w;
+ height = h;
+ }
+
+ @Override
+ public long getDateTaken() {
+ // This value is used for sorting.
+ return -1;
+ }
+
+ @Override
+ public long getDateModified() {
+ // This value might be used for sorting.
+ return -1;
+ }
+
+ @Override
+ public String getTitle() {
+ return "";
+ }
+
+ @Override
+ public int getWidth() {
+ return width;
+ }
+
+ @Override
+ public int getHeight() {
+ return height;
+ }
+
+ @Override
+ public int getType() {
+ return ImageData.TYPE_CAMERA_PREVIEW;
+ }
+
+ @Override
+ public boolean isActionSupported(int action) {
+ return false;
+ }
+
+ @Override
+ public View getView(Context c, int width, int height, Drawable placeHolder) {
+ return mCameraPreviewView;
+ }
+
+ @Override
+ public void prepare() {
+ // do nothing.
+ }
+
+ @Override
+ public void recycle() {
+ // do nothing.
+ }
+ }
+
+}
diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java
new file mode 100644
index 000000000..1fda9eb67
--- /dev/null
+++ b/src/com/android/camera/data/LocalData.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2013 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.camera.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.graphics.drawable.Drawable;
+import android.media.MediaMetadataRetriever;
+import android.os.AsyncTask;
+import android.provider.MediaStore.Images.ImageColumns;
+import android.provider.MediaStore.Video;
+import android.provider.MediaStore.Video.VideoColumns;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.camera.ui.FilmStripView;
+
+import java.util.Comparator;
+import java.util.Date;
+
+/* An abstract interface that represents the local media data. Also implements
+ * Comparable interface so we can sort in DataAdapter.
+ */
+abstract interface LocalData extends FilmStripView.ImageData {
+ static final String TAG = "LocalData";
+
+ abstract View getView(Context c, int width, int height, Drawable placeHolder);
+ abstract long getDateTaken();
+ abstract long getDateModified();
+ abstract String getTitle();
+
+ static class NewestFirstComparator implements Comparator<LocalData> {
+
+ private static int compare(long v1, long v2) {
+ if (v1 == -1) {
+ if (v2 == -1) return 0;
+ return -1;
+ }
+ if (v2 == -1) return 0;
+
+ return ((v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0));
+ }
+
+ @Override
+ public int compare(LocalData d1, LocalData d2) {
+ int cmp = compare(d1.getDateTaken(), d2.getDateTaken());
+ if (cmp == 0) {
+ cmp = compare(d1.getDateModified(), d2.getDateModified());
+ }
+ if (cmp == 0) {
+ cmp = d1.getTitle().compareTo(d2.getTitle());
+ }
+ return cmp;
+ }
+ }
+
+ /*
+ * A base class for all the local media files. The bitmap is loaded in background
+ * thread. Subclasses should implement their own background loading thread by
+ * subclassing BitmapLoadTask and overriding doInBackground() to return a bitmap.
+ */
+ abstract static class LocalMediaData implements LocalData {
+ protected long id;
+ protected String title;
+ protected String mimeType;
+ protected long dateTaken;
+ protected long dateModified;
+ protected String path;
+ // width and height should be adjusted according to orientation.
+ protected int width;
+ protected int height;
+
+ // true if this data has a corresponding visible view.
+ protected Boolean mUsing = false;
+
+ @Override
+ public long getDateTaken() {
+ return dateTaken;
+ }
+
+ @Override
+ public long getDateModified() {
+ return dateModified;
+ }
+
+ @Override
+ public String getTitle() {
+ return new String(title);
+ }
+
+ @Override
+ public int getWidth() {
+ return width;
+ }
+
+ @Override
+ public int getHeight() {
+ return height;
+ }
+
+ @Override
+ public boolean isActionSupported(int action) {
+ return false;
+ }
+
+ @Override
+ public View getView(Context c,
+ int decodeWidth, int decodeHeight, Drawable placeHolder) {
+ ImageView v = new ImageView(c);
+ v.setImageDrawable(placeHolder);
+
+ v.setScaleType(ImageView.ScaleType.FIT_XY);
+ BitmapLoadTask task = getBitmapLoadTask(v, decodeWidth, decodeHeight);
+ task.execute();
+ return v;
+ }
+
+ @Override
+ public void prepare() {
+ synchronized (mUsing) {
+ mUsing = true;
+ }
+ }
+
+ @Override
+ public void recycle() {
+ synchronized (mUsing) {
+ mUsing = false;
+ }
+ }
+
+ protected boolean isUsing() {
+ synchronized (mUsing) {
+ return mUsing;
+ }
+ }
+
+ @Override
+ public abstract int getType();
+
+ protected abstract BitmapLoadTask getBitmapLoadTask(
+ ImageView v, int decodeWidth, int decodeHeight);
+
+ /*
+ * An AsyncTask class that loads the bitmap in the background thread.
+ * Sub-classes should implement their own "protected Bitmap doInBackground(Void... )"
+ */
+ protected abstract class BitmapLoadTask extends AsyncTask<Void, Void, Bitmap> {
+ protected ImageView mView;
+
+ protected BitmapLoadTask(ImageView v) {
+ mView = v;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (!isUsing()) return;
+ if (bitmap == null) {
+ Log.e(TAG, "Failed decoding bitmap for file:" + path);
+ return;
+ }
+ mView.setScaleType(ImageView.ScaleType.FIT_XY);
+ mView.setImageBitmap(bitmap);
+ }
+ }
+ }
+
+ static class Photo extends LocalMediaData {
+ public static final int COL_ID = 0;
+ public static final int COL_TITLE = 1;
+ public static final int COL_MIME_TYPE = 2;
+ public static final int COL_DATE_TAKEN = 3;
+ public static final int COL_DATE_MODIFIED = 4;
+ public static final int COL_DATA = 5;
+ public static final int COL_ORIENTATION = 6;
+ public static final int COL_WIDTH = 7;
+ public static final int COL_HEIGHT = 8;
+
+ static final String QUERY_ORDER = ImageColumns.DATE_TAKEN + " DESC, "
+ + ImageColumns._ID + " DESC";
+ static final String[] QUERY_PROJECTION = {
+ ImageColumns._ID, // 0, int
+ ImageColumns.TITLE, // 1, string
+ ImageColumns.MIME_TYPE, // 2, string
+ ImageColumns.DATE_TAKEN, // 3, int
+ ImageColumns.DATE_MODIFIED, // 4, int
+ ImageColumns.DATA, // 5, string
+ ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270
+ ImageColumns.WIDTH, // 7, int
+ ImageColumns.HEIGHT, // 8, int
+ };
+
+ // 32K buffer.
+ private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024];
+
+ // from MediaStore, can only be 0, 90, 180, 270;
+ public int orientation;
+
+ static Photo buildFromCursor(Cursor c) {
+ Photo d = new Photo();
+ d.id = c.getLong(COL_ID);
+ d.title = c.getString(COL_TITLE);
+ d.mimeType = c.getString(COL_MIME_TYPE);
+ d.dateTaken = c.getLong(COL_DATE_TAKEN);
+ d.dateModified = c.getLong(COL_DATE_MODIFIED);
+ d.path = c.getString(COL_DATA);
+ d.orientation = c.getInt(COL_ORIENTATION);
+ d.width = c.getInt(COL_WIDTH);
+ d.height = c.getInt(COL_HEIGHT);
+ if (d.width <= 0 || d.height <= 0) {
+ Log.v(TAG, "warning! zero dimension for "
+ + d.path + ":" + d.width + "x" + d.height);
+ BitmapFactory.Options opts = decodeDimension(d.path);
+ if (opts != null) {
+ d.width = opts.outWidth;
+ d.height = opts.outHeight;
+ } else {
+ Log.v(TAG, "warning! dimension decode failed for " + d.path);
+ Bitmap b = BitmapFactory.decodeFile(d.path);
+ if (b == null) {
+ return null;
+ }
+ d.width = b.getWidth();
+ d.height = b.getHeight();
+ }
+ }
+ if (d.orientation == 90 || d.orientation == 270) {
+ int b = d.width;
+ d.width = d.height;
+ d.height = b;
+ }
+ return d;
+ }
+
+ @Override
+ public String toString() {
+ return "Photo:" + ",data=" + path + ",mimeType=" + mimeType
+ + "," + width + "x" + height + ",orientation=" + orientation
+ + ",date=" + new Date(dateTaken);
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_PHOTO;
+ }
+
+ @Override
+ protected BitmapLoadTask getBitmapLoadTask(
+ ImageView v, int decodeWidth, int decodeHeight) {
+ return new PhotoBitmapLoadTask(v, decodeWidth, decodeHeight);
+ }
+
+ private static BitmapFactory.Options decodeDimension(String path) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inJustDecodeBounds = true;
+ Bitmap b = BitmapFactory.decodeFile(path, opts);
+ if (b == null) {
+ return null;
+ }
+ return opts;
+ }
+
+ private final class PhotoBitmapLoadTask extends BitmapLoadTask {
+ private int mDecodeWidth;
+ private int mDecodeHeight;
+
+ public PhotoBitmapLoadTask(ImageView v, int decodeWidth, int decodeHeight) {
+ super(v);
+ mDecodeWidth = decodeWidth;
+ mDecodeHeight = decodeHeight;
+ }
+
+ @Override
+ protected Bitmap doInBackground(Void... v) {
+ BitmapFactory.Options opts = null;
+ Bitmap b;
+ int sample = 1;
+ while (mDecodeWidth * sample < width
+ || mDecodeHeight * sample < height) {
+ sample *= 2;
+ }
+ opts = new BitmapFactory.Options();
+ opts.inSampleSize = sample;
+ opts.inTempStorage = DECODE_TEMP_STORAGE;
+ if (isCancelled() || !isUsing()) {
+ return null;
+ }
+ b = BitmapFactory.decodeFile(path, opts);
+ if (orientation != 0) {
+ if (isCancelled() || !isUsing()) {
+ return null;
+ }
+ Matrix m = new Matrix();
+ m.setRotate((float) orientation);
+ b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
+ }
+ return b;
+ }
+ }
+ }
+
+ static class Video extends LocalMediaData {
+ public static final int COL_ID = 0;
+ public static final int COL_TITLE = 1;
+ public static final int COL_MIME_TYPE = 2;
+ public static final int COL_DATE_TAKEN = 3;
+ public static final int COL_DATE_MODIFIED = 4;
+ public static final int COL_DATA = 5;
+ public static final int COL_WIDTH = 6;
+ public static final int COL_HEIGHT = 7;
+
+ static final String QUERY_ORDER = VideoColumns.DATE_TAKEN + " DESC, "
+ + VideoColumns._ID + " DESC";
+ static final String[] QUERY_PROJECTION = {
+ VideoColumns._ID, // 0, int
+ VideoColumns.TITLE, // 1, string
+ VideoColumns.MIME_TYPE, // 2, string
+ VideoColumns.DATE_TAKEN, // 3, int
+ VideoColumns.DATE_MODIFIED, // 4, int
+ VideoColumns.DATA, // 5, string
+ VideoColumns.WIDTH, // 6, int
+ VideoColumns.HEIGHT, // 7, int
+ VideoColumns.RESOLUTION
+ };
+
+ static Video buildFromCursor(Cursor c) {
+ Video d = new Video();
+ d.id = c.getLong(COL_ID);
+ d.title = c.getString(COL_TITLE);
+ d.mimeType = c.getString(COL_MIME_TYPE);
+ d.dateTaken = c.getLong(COL_DATE_TAKEN);
+ d.dateModified = c.getLong(COL_DATE_MODIFIED);
+ d.path = c.getString(COL_DATA);
+ d.width = c.getInt(COL_WIDTH);
+ d.height = c.getInt(COL_HEIGHT);
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(d.path);
+ String rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ if (d.width == 0 || d.height == 0) {
+ d.width = Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+ d.height = Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+ }
+ retriever.release();
+ if (rotation.equals("90") || rotation.equals("270")) {
+ int b = d.width;
+ d.width = d.height;
+ d.height = b;
+ }
+ return d;
+ }
+
+ @Override
+ public String toString() {
+ return "Video:" + ",data=" + path + ",mimeType=" + mimeType
+ + "," + width + "x" + height + ",date=" + new Date(dateTaken);
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_PHOTO;
+ }
+
+ @Override
+ protected BitmapLoadTask getBitmapLoadTask(
+ ImageView v, int decodeWidth, int decodeHeight) {
+ return new VideoBitmapLoadTask(v);
+ }
+
+ private final class VideoBitmapLoadTask extends BitmapLoadTask {
+
+ public VideoBitmapLoadTask(ImageView v) {
+ super(v);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Void... v) {
+ if (isCancelled() || !isUsing()) {
+ return null;
+ }
+ android.media.MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(path);
+ byte[] data = retriever.getEmbeddedPicture();
+ Bitmap bitmap = null;
+ if (isCancelled() || !isUsing()) {
+ retriever.release();
+ return null;
+ }
+ if (data != null) {
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ }
+ if (bitmap == null) {
+ bitmap = (Bitmap) retriever.getFrameAtTime();
+ }
+ retriever.release();
+ return bitmap;
+ }
+ }
+ }
+}
+
diff --git a/src/com/android/camera/ui/FilmStripGestureRecognizer.java b/src/com/android/camera/ui/FilmStripGestureRecognizer.java
new file mode 100644
index 000000000..f0e2534d3
--- /dev/null
+++ b/src/com/android/camera/ui/FilmStripGestureRecognizer.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 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.camera.ui;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+// This class aggregates three gesture detectors: GestureDetector,
+// ScaleGestureDetector.
+public class FilmStripGestureRecognizer {
+ @SuppressWarnings("unused")
+ private static final String TAG = "FilmStripGestureRecognizer";
+
+ public interface Listener {
+ boolean onSingleTapUp(float x, float y);
+ boolean onDoubleTap(float x, float y);
+ boolean onScroll(float x, float y, float dx, float dy);
+ boolean onFling(float velocityX, float velocityY);
+ boolean onScaleBegin(float focusX, float focusY);
+ boolean onScale(float focusX, float focusY, float scale);
+ boolean onDown(float x, float y);
+ void onScaleEnd();
+ }
+
+ private final GestureDetector mGestureDetector;
+ private final ScaleGestureDetector mScaleDetector;
+ private final Listener mListener;
+
+ public FilmStripGestureRecognizer(Context context, Listener listener) {
+ mListener = listener;
+ mGestureDetector = new GestureDetector(context, new MyGestureListener(),
+ null, true /* ignoreMultitouch */);
+ mScaleDetector = new ScaleGestureDetector(
+ context, new MyScaleListener());
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || mScaleDetector.onTouchEvent(event);
+ }
+
+ private class MyGestureListener
+ extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return mListener.onSingleTapUp(e.getX(), e.getY());
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ return mListener.onDoubleTap(e.getX(), e.getY());
+ }
+
+ @Override
+ public boolean onScroll(
+ MotionEvent e1, MotionEvent e2, float dx, float dy) {
+ return mListener.onScroll(e2.getX(), e2.getY(), dx, dy);
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ return mListener.onFling(velocityX, velocityY);
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ mListener.onDown(e.getX(), e.getY());
+ return super.onDown(e);
+ }
+ }
+
+ private class MyScaleListener
+ extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return mListener.onScaleBegin(
+ detector.getFocusX(), detector.getFocusY());
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ return mListener.onScale(detector.getFocusX(),
+ detector.getFocusY(), detector.getScaleFactor());
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ mListener.onScaleEnd();
+ }
+ }
+}
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java
new file mode 100644
index 000000000..c836c9168
--- /dev/null
+++ b/src/com/android/camera/ui/FilmStripView.java
@@ -0,0 +1,846 @@
+/*
+ * Copyright (C) 2013 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.camera.ui;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Scroller;
+
+public class FilmStripView extends ViewGroup {
+ private static final String TAG = FilmStripView.class.getSimpleName();
+ private static final int BUFFER_SIZE = 5;
+ // Horizontal padding of children.
+ private static final int H_PADDING = 50;
+ // Duration to go back to the first.
+ private static final int DURATION_BACK_ANIM = 500;
+ private static final int DURATION_SCROLL_TO_FILMSTRIP = 350;
+ private static final int DURATION_GEOMETRY_ADJUST = 200;
+ private static final float FILM_STRIP_SCALE = 0.6f;
+ private static final float MAX_SCALE = 1f;
+
+ private Context mContext;
+ private FilmStripGestureRecognizer mGestureRecognizer;
+ private DataAdapter mDataAdapter;
+ private final Rect mDrawArea = new Rect();
+
+ private int mCurrentInfo;
+ private float mScale;
+ private GeometryAnimator mGeometryAnimator;
+ private int mCenterPosition = -1;
+ private ViewInfo[] mViewInfo = new ViewInfo[BUFFER_SIZE];
+
+ // This is used to resolve the misalignment problem when the device
+ // orientation is changed. If the current item is in fullscreen, it might
+ // be shifted because mCenterPosition is not adjusted with the orientation.
+ // Set this to true when onSizeChanged is called to make sure we adjust
+ // mCenterPosition accordingly.
+ private boolean mAnchorPending;
+
+ public interface ImageData {
+ public static final int TYPE_NONE = 0;
+ public static final int TYPE_CAMERA_PREVIEW = 1;
+ public static final int TYPE_PHOTO = 2;
+ public static final int TYPE_VIDEO = 3;
+ public static final int TYPE_PHOTOSPHERE = 4;
+
+ // The actions are defined bit-wise so we can use bit operations like
+ // | and &.
+ public static final int ACTION_NONE = 0;
+ public static final int ACTION_PROMOTE = 1;
+ public static final int ACTION_DEMOTE = 2;
+
+ // SIZE_FULL means disgard the width or height when deciding the view size
+ // of this ImageData, just use full screen size.
+ public static final int SIZE_FULL = -2;
+
+ // The values returned by getWidth() and getHeight() will be used for layout.
+ public int getWidth();
+ public int getHeight();
+ public int getType();
+ public boolean isActionSupported(int action);
+
+ // prepare() should be called first time before using it.
+ public void prepare();
+
+ // recycle() should be called before we nullify the reference to this
+ // data.
+ public void recycle();
+ }
+
+ public interface DataAdapter {
+ public interface UpdateReporter {
+ public boolean isDataRemoved(int id);
+ public boolean isDataUpdated(int id);
+ }
+
+ public interface Listener {
+ // Called when the whole data loading is done. No any assumption
+ // on previous data.
+ public void onDataLoaded();
+ // Only some of the data is changed. The listener should check
+ // if any thing needs to be updated.
+ public void onDataUpdated(UpdateReporter reporter);
+ public void onDataInserted(int dataID);
+ public void onDataRemoved(int dataID);
+ }
+
+ public int getTotalNumber();
+ public View getView(Context context, int id);
+ public ImageData getImageData(int id);
+ public void suggestDecodeSize(int w, int h);
+
+ public void setListener(Listener listener);
+ }
+
+ // A helper class to tract and calculate the view coordination.
+ private static class ViewInfo {
+ private int mDataID;
+ // the position of the left of the view in the whole filmstrip.
+ private int mLeftPosition;
+ private View mView;
+ private float mOffsetY;
+
+ public ViewInfo(int id, View v) {
+ v.setPivotX(0f);
+ v.setPivotY(0f);
+ mDataID = id;
+ mView = v;
+ mLeftPosition = -1;
+ mOffsetY = 0;
+ }
+
+ public int getID() {
+ return mDataID;
+ }
+
+ public void setLeftPosition(int pos) {
+ mLeftPosition = pos;
+ }
+
+ public int getLeftPosition() {
+ return mLeftPosition;
+ }
+
+ public float getOffsetY() {
+ return mOffsetY;
+ }
+
+ public void setOffsetY(float offset) {
+ mOffsetY = offset;
+ }
+
+ public int getCenterX() {
+ return mLeftPosition + mView.getWidth() / 2;
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ private void layoutAt(int left, int top) {
+ mView.layout(left, top, left + mView.getMeasuredWidth(),
+ top + mView.getMeasuredHeight());
+ }
+
+ public void layoutIn(Rect drawArea, int refCenter, float scale) {
+ // drawArea is where to layout in.
+ // refCenter is the absolute horizontal position of the center of drawArea.
+ int left = (int) (drawArea.centerX() + (mLeftPosition - refCenter) * scale);
+ int top = (int) (drawArea.centerY() - (mView.getMeasuredHeight() / 2) * scale
+ + mOffsetY);
+ layoutAt(left, top);
+ mView.setScaleX(scale);
+ mView.setScaleY(scale);
+ }
+ }
+
+ public FilmStripView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public FilmStripView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public FilmStripView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ mCurrentInfo = (BUFFER_SIZE - 1) / 2;
+ // This is for positioning camera controller at the same place in
+ // different orientations.
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+
+ setWillNotDraw(false);
+ mContext = context;
+ mScale = 1.0f;
+ mGeometryAnimator = new GeometryAnimator(context);
+ mGestureRecognizer =
+ new FilmStripGestureRecognizer(context, new MyGestureReceiver());
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ public boolean isAnchoredTo(int id) {
+ if (mViewInfo[mCurrentInfo].getID() == id
+ && mViewInfo[mCurrentInfo].getCenterX() == mCenterPosition) {
+ return true;
+ }
+ return false;
+ }
+
+ public int getCurrentType() {
+ if (mDataAdapter == null) return ImageData.TYPE_NONE;
+ ViewInfo curr = mViewInfo[mCurrentInfo];
+ if (curr == null) return ImageData.TYPE_NONE;
+ return mDataAdapter.getImageData(curr.getID()).getType();
+ }
+
+ @Override
+ public void onDraw(Canvas c) {
+ if (mGeometryAnimator.hasNewGeometry()) {
+ layoutChildren();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int boundWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int boundHeight = MeasureSpec.getSize(heightMeasureSpec);
+ if (mDataAdapter != null) {
+ mDataAdapter.suggestDecodeSize(boundWidth / 2, boundHeight / 2);
+ }
+
+ int wMode = View.MeasureSpec.EXACTLY;
+ int hMode = View.MeasureSpec.EXACTLY;
+
+ for (int i = 0; i < mViewInfo.length; i++) {
+ ViewInfo info = mViewInfo[i];
+ if (mViewInfo[i] == null) continue;
+
+ int imageWidth = mDataAdapter.getImageData(info.getID()).getWidth();
+ int imageHeight = mDataAdapter.getImageData(info.getID()).getHeight();
+ if (imageWidth == ImageData.SIZE_FULL) imageWidth = boundWidth;
+ if (imageHeight == ImageData.SIZE_FULL) imageHeight = boundHeight;
+
+ int scaledWidth = boundWidth;
+ int scaledHeight = boundHeight;
+
+ if (imageWidth * scaledHeight > scaledWidth * imageHeight) {
+ scaledHeight = imageHeight * scaledWidth / imageWidth;
+ } else {
+ scaledWidth = imageWidth * scaledHeight / imageHeight;
+ }
+ scaledWidth += H_PADDING * 2;
+ mViewInfo[i].getView().measure(
+ View.MeasureSpec.makeMeasureSpec(scaledWidth, wMode)
+ , View.MeasureSpec.makeMeasureSpec(scaledHeight, hMode));
+ }
+ setMeasuredDimension(boundWidth, boundHeight);
+ }
+
+ private int findTheNearestView(int pointX) {
+
+ int nearest = 0;
+ // find the first non-null ViewInfo.
+ for (; nearest < BUFFER_SIZE
+ && (mViewInfo[nearest] == null || mViewInfo[nearest].getLeftPosition() == -1);
+ nearest++);
+ // no existing available ViewInfo
+ if (nearest == BUFFER_SIZE) return -1;
+ int min = Math.abs(pointX - mViewInfo[nearest].getCenterX());
+
+ for (int infoID = nearest + 1;
+ infoID < BUFFER_SIZE && mViewInfo[infoID] != null; infoID++) {
+ // not measured yet.
+ if (mViewInfo[infoID].getLeftPosition() == -1) continue;
+
+ int c = mViewInfo[infoID].getCenterX();
+ int dist = Math.abs(pointX - c);
+ if (dist < min) {
+ min = dist;
+ nearest = infoID;
+ }
+ }
+ return nearest;
+ }
+
+ private ViewInfo buildInfoFromData(int dataID) {
+ ImageData data = mDataAdapter.getImageData(dataID);
+ if (data == null) return null;
+ data.prepare();
+ View v = mDataAdapter.getView(mContext, dataID);
+ if (v == null) return null;
+ v.setPadding(H_PADDING, 0, H_PADDING, 0);
+ ViewInfo info = new ViewInfo(dataID, v);
+ addView(info.getView());
+ return info;
+ }
+
+ private void removeInfo(int infoID) {
+ if (infoID >= mViewInfo.length || mViewInfo[infoID] == null) return;
+
+ removeView(mViewInfo[infoID].getView());
+ mDataAdapter.getImageData(mViewInfo[infoID].getID()).recycle();
+ mViewInfo[infoID] = null;
+ }
+
+ // We try to keep the one closest to the center of the screen at position mCurrentInfo.
+ private void stepIfNeeded() {
+ int nearest = findTheNearestView(mCenterPosition);
+ // no change made.
+ if (nearest == -1 || nearest == mCurrentInfo) return;
+
+ int adjust = nearest - mCurrentInfo;
+ if (adjust > 0) {
+ for (int k = 0; k < adjust; k++) {
+ removeInfo(k);
+ }
+ for (int k = 0; k + adjust < BUFFER_SIZE; k++) {
+ mViewInfo[k] = mViewInfo[k + adjust];
+ }
+ for (int k = BUFFER_SIZE - adjust; k < BUFFER_SIZE; k++) {
+ mViewInfo[k] = null;
+ if (mViewInfo[k - 1] != null) {
+ mViewInfo[k] = buildInfoFromData(mViewInfo[k - 1].getID() + 1);
+ }
+ }
+ } else {
+ for (int k = BUFFER_SIZE - 1; k >= BUFFER_SIZE + adjust; k--) {
+ removeInfo(k);
+ }
+ for (int k = BUFFER_SIZE - 1; k + adjust >= 0; k--) {
+ mViewInfo[k] = mViewInfo[k + adjust];
+ }
+ for (int k = -1 - adjust; k >= 0; k--) {
+ mViewInfo[k] = null;
+ if (mViewInfo[k + 1] != null) {
+ mViewInfo[k] = buildInfoFromData(mViewInfo[k + 1].getID() - 1);
+ }
+ }
+ }
+ }
+
+ // Don't go beyond the bound.
+ private void adjustCenterPosition() {
+ ViewInfo curr = mViewInfo[mCurrentInfo];
+ if (curr == null) return;
+
+ if (curr.getID() == 0 && mCenterPosition < curr.getCenterX()) {
+ mCenterPosition = curr.getCenterX();
+ mGeometryAnimator.stopScroll();
+ }
+ if (curr.getID() == mDataAdapter.getTotalNumber() - 1
+ && mCenterPosition > curr.getCenterX()) {
+ mCenterPosition = curr.getCenterX();
+ mGeometryAnimator.stopScroll();
+ }
+ }
+
+ private void layoutChildren() {
+ if (mAnchorPending) {
+ mCenterPosition = mViewInfo[mCurrentInfo].getCenterX();
+ mAnchorPending = false;
+ }
+
+ if (mGeometryAnimator.hasNewGeometry()) {
+ mCenterPosition = mGeometryAnimator.getNewPosition();
+ mScale = mGeometryAnimator.getNewScale();
+ }
+
+ adjustCenterPosition();
+
+ mViewInfo[mCurrentInfo].layoutIn(mDrawArea, mCenterPosition, mScale);
+
+ // images on the left
+ for (int infoID = mCurrentInfo - 1; infoID >= 0; infoID--) {
+ ViewInfo curr = mViewInfo[infoID];
+ if (curr != null) {
+ ViewInfo next = mViewInfo[infoID + 1];
+ curr.setLeftPosition(
+ next.getLeftPosition() - curr.getView().getMeasuredWidth());
+ curr.layoutIn(mDrawArea, mCenterPosition, mScale);
+ }
+ }
+
+ // images on the right
+ for (int infoID = mCurrentInfo + 1; infoID < BUFFER_SIZE; infoID++) {
+ ViewInfo curr = mViewInfo[infoID];
+ if (curr != null) {
+ ViewInfo prev = mViewInfo[infoID - 1];
+ curr.setLeftPosition(
+ prev.getLeftPosition() + prev.getView().getMeasuredWidth());
+ curr.layoutIn(mDrawArea, mCenterPosition, mScale);
+ }
+ }
+
+ stepIfNeeded();
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (mViewInfo[mCurrentInfo] == null) return;
+
+ mDrawArea.left = l;
+ mDrawArea.top = t;
+ mDrawArea.right = r;
+ mDrawArea.bottom = b;
+
+ layoutChildren();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ if (w == oldw && h == oldh) return;
+ if (mViewInfo[mCurrentInfo] != null && mScale == 1f
+ && isAnchoredTo(mViewInfo[mCurrentInfo].getID())) {
+ mAnchorPending = true;
+ }
+ }
+
+ public void setDataAdapter(DataAdapter adapter) {
+ mDataAdapter = adapter;
+ mDataAdapter.suggestDecodeSize(getMeasuredWidth(), getMeasuredHeight());
+ mDataAdapter.setListener(new DataAdapter.Listener() {
+ @Override
+ public void onDataLoaded() {
+ reload();
+ }
+
+ @Override
+ public void onDataUpdated(DataAdapter.UpdateReporter reporter) {
+ update(reporter);
+ }
+
+ @Override
+ public void onDataInserted(int dataID) {
+ }
+
+ @Override
+ public void onDataRemoved(int dataID) {
+ }
+ });
+ }
+
+ public boolean isInCameraFullscreen() {
+ return (isAnchoredTo(0) && mScale == 1f
+ && getCurrentType() == ImageData.TYPE_CAMERA_PREVIEW);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (isInCameraFullscreen()) return false;
+ return true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ mGestureRecognizer.onTouchEvent(ev);
+ return true;
+ }
+
+ private void updateViewInfo(int infoID) {
+ ViewInfo info = mViewInfo[infoID];
+ removeView(info.getView());
+ mViewInfo[infoID] = buildInfoFromData(info.getID());
+ }
+
+ // Some of the data is changed.
+ private void update(DataAdapter.UpdateReporter reporter) {
+ // No data yet.
+ if (mViewInfo[mCurrentInfo] == null) {
+ reload();
+ return;
+ }
+
+ // Check the current one.
+ ViewInfo curr = mViewInfo[mCurrentInfo];
+ int dataID = curr.getID();
+ if (reporter.isDataRemoved(dataID)) {
+ mCenterPosition = -1;
+ reload();
+ return;
+ }
+ if (reporter.isDataUpdated(dataID)) {
+ updateViewInfo(mCurrentInfo);
+ }
+
+ // Check left
+ for (int i = mCurrentInfo - 1; i >= 0; i--) {
+ curr = mViewInfo[i];
+ if (curr != null) {
+ dataID = curr.getID();
+ if (reporter.isDataRemoved(dataID) || reporter.isDataUpdated(dataID)) {
+ updateViewInfo(i);
+ }
+ } else {
+ ViewInfo next = mViewInfo[i + 1];
+ if (next != null) mViewInfo[i] = buildInfoFromData(next.getID() - 1);
+ }
+ }
+
+ // Check right
+ for (int i = mCurrentInfo + 1; i < BUFFER_SIZE; i++) {
+ curr = mViewInfo[i];
+ if (curr != null) {
+ dataID = curr.getID();
+ if (reporter.isDataRemoved(dataID) || reporter.isDataUpdated(dataID)) {
+ updateViewInfo(i);
+ }
+ } else {
+ ViewInfo prev = mViewInfo[i - 1];
+ if (prev != null) mViewInfo[i] = buildInfoFromData(prev.getID() + 1);
+ }
+ }
+ }
+
+ // The whole data might be totally different. Flush all and load from the start.
+ private void reload() {
+ removeAllViews();
+ int dataNumber = mDataAdapter.getTotalNumber();
+ if (dataNumber == 0) return;
+
+ int currentData = 0;
+ int currentLeft = 0;
+ mViewInfo[mCurrentInfo] = buildInfoFromData(currentData);
+ mViewInfo[mCurrentInfo].setLeftPosition(currentLeft);
+ if (getCurrentType() == ImageData.TYPE_CAMERA_PREVIEW
+ && currentLeft == 0) {
+ // we are in camera mode by default.
+ mGeometryAnimator.lockPosition(currentLeft);
+ }
+ for (int i = 1; mCurrentInfo + i < BUFFER_SIZE || mCurrentInfo - i >= 0; i++) {
+ int infoID = mCurrentInfo + i;
+ if (infoID < BUFFER_SIZE && mViewInfo[infoID - 1] != null) {
+ mViewInfo[infoID] = buildInfoFromData(mViewInfo[infoID - 1].getID() + 1);
+ }
+ infoID = mCurrentInfo - i;
+ if (infoID >= 0 && mViewInfo[infoID + 1] != null) {
+ mViewInfo[infoID] = buildInfoFromData(mViewInfo[infoID + 1].getID() - 1);
+ }
+ }
+ layoutChildren();
+ }
+
+ // GeometryAnimator controls all the geometry animations. It passively
+ // tells the geometry information on demand.
+ private class GeometryAnimator implements
+ ValueAnimator.AnimatorUpdateListener,
+ Animator.AnimatorListener {
+
+ private ValueAnimator mScaleAnimator;
+ private boolean mHasNewScale;
+ private float mNewScale;
+
+ private Scroller mScroller;
+ private boolean mHasNewPosition;
+ private DecelerateInterpolator mDecelerateInterpolator;
+
+ private boolean mCanStopScroll;
+ private boolean mCanStopScale;
+
+ private boolean mIsPositionLocked;
+ private int mLockedPosition;
+
+ private Runnable mPostAction;
+
+ GeometryAnimator(Context context) {
+ mScroller = new Scroller(context);
+ mHasNewPosition = false;
+ mScaleAnimator = new ValueAnimator();
+ mScaleAnimator.addUpdateListener(GeometryAnimator.this);
+ mScaleAnimator.addListener(GeometryAnimator.this);
+ mDecelerateInterpolator = new DecelerateInterpolator();
+ mCanStopScroll = true;
+ mCanStopScale = true;
+ mHasNewScale = false;
+ }
+
+ boolean hasNewGeometry() {
+ mHasNewPosition = mScroller.computeScrollOffset();
+ if (!mHasNewPosition) {
+ mCanStopScroll = true;
+ }
+ // If the position is locked, then we always return true to force
+ // the position value to use the locked value.
+ return (mHasNewPosition || mHasNewScale || mIsPositionLocked);
+ }
+
+ // Always call hasNewGeometry() before getting the new scale value.
+ float getNewScale() {
+ if (!mHasNewScale) return mScale;
+ mHasNewScale = false;
+ return mNewScale;
+ }
+
+ // Always call hasNewGeometry() before getting the new position value.
+ int getNewPosition() {
+ if (mIsPositionLocked) return mLockedPosition;
+ if (!mHasNewPosition) return mCenterPosition;
+ return mScroller.getCurrX();
+ }
+
+ void lockPosition(int pos) {
+ mIsPositionLocked = true;
+ mLockedPosition = pos;
+ }
+
+ void unlockPosition() {
+ if (mIsPositionLocked) {
+ // only when the position is previously locked we set the current
+ // position to make it consistent.
+ mCenterPosition = mLockedPosition;
+ mIsPositionLocked = false;
+ }
+ }
+
+ void fling(int velocityX, int minX, int maxX) {
+ if (!stopScroll() || mIsPositionLocked) return;
+ mScroller.fling(mCenterPosition, 0, velocityX, 0, minX, maxX, 0, 0);
+ }
+
+ boolean stopScroll() {
+ if (!mCanStopScroll) return false;
+ mScroller.forceFinished(true);
+ mHasNewPosition = false;
+ return true;
+ }
+
+ boolean stopScale() {
+ if (!mCanStopScale) return false;
+ mScaleAnimator.cancel();
+ mHasNewScale = false;
+ return true;
+ }
+
+ void stop() {
+ stopScroll();
+ stopScale();
+ }
+
+ void scrollTo(int position, int duration, boolean interruptible) {
+ if (!stopScroll() || mIsPositionLocked) return;
+ mCanStopScroll = interruptible;
+ stopScroll();
+ mScroller.startScroll(mCenterPosition, 0, position - mCenterPosition,
+ 0, duration);
+ }
+
+ void scrollTo(int position, int duration) {
+ scrollTo(position, duration, true);
+ }
+
+ void scaleTo(float scale, int duration, boolean interruptible) {
+ if (!stopScale()) return;
+ mCanStopScale = interruptible;
+ mScaleAnimator.setDuration(duration);
+ mScaleAnimator.setFloatValues(mScale, scale);
+ mScaleAnimator.setInterpolator(mDecelerateInterpolator);
+ mScaleAnimator.start();
+ mHasNewScale = true;
+ }
+
+ void scaleTo(float scale, int duration) {
+ scaleTo(scale, duration, true);
+ }
+
+ void setPostAction(Runnable act) {
+ mPostAction = act;
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mHasNewScale = true;
+ mNewScale = (Float) animation.getAnimatedValue();
+ layoutChildren();
+ }
+
+ @Override
+ public void onAnimationStart(Animator anim) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ if (mPostAction != null) {
+ mPostAction.run();
+ mPostAction = null;
+ }
+ mCanStopScale = true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator anim) {
+ mPostAction = null;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator anim) {
+ }
+ }
+
+ private class MyGestureReceiver implements FilmStripGestureRecognizer.Listener {
+ // Indicating the current trend of scaling is up (>1) or down (<1).
+ private float mScaleTrend;
+
+ @Override
+ public boolean onSingleTapUp(float x, float y) {
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(float x, float y) {
+ return false;
+ }
+
+ @Override
+ public boolean onDown(float x, float y) {
+ mGeometryAnimator.stop();
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(float x, float y, float dx, float dy) {
+ int deltaX = (int) (dx / mScale);
+ if (deltaX > 0 && isInCameraFullscreen()) {
+ mGeometryAnimator.unlockPosition();
+ mGeometryAnimator.scaleTo(FILM_STRIP_SCALE, DURATION_GEOMETRY_ADJUST, false);
+ }
+
+ mCenterPosition += deltaX;
+
+ // Vertical part. Promote or demote.
+ int scaledDeltaY = (int) (dy / mScale);
+
+ for (int i = 0; i < BUFFER_SIZE; i++) {
+ if (mViewInfo[i] == null) continue;
+ Rect hitRect = new Rect();
+ View v = mViewInfo[i].getView();
+ v.getHitRect(hitRect);
+ if (hitRect.contains((int) x, (int) y)) {
+ ImageData data = mDataAdapter.getImageData(mViewInfo[i].getID());
+ if ((data.isActionSupported(ImageData.ACTION_DEMOTE) && dy > 0)
+ || (data.isActionSupported(ImageData.ACTION_PROMOTE) && dy < 0)) {
+ mViewInfo[i].setOffsetY(mViewInfo[i].getOffsetY() - dy);
+ }
+ break;
+ }
+ }
+
+ layoutChildren();
+ return true;
+ }
+
+ @Override
+ public boolean onFling(float velocityX, float velocityY) {
+ float scaledVelocityX = velocityX / mScale;
+ if (isInCameraFullscreen() && scaledVelocityX < 0) {
+ mGeometryAnimator.unlockPosition();
+ mGeometryAnimator.scaleTo(FILM_STRIP_SCALE, DURATION_GEOMETRY_ADJUST, false);
+ }
+ ViewInfo info = mViewInfo[mCurrentInfo];
+ int w = getWidth();
+ if (info == null) return true;
+ mGeometryAnimator.fling((int) -scaledVelocityX,
+ // estimation of possible length on the left
+ info.getLeftPosition() - info.getID() * w * 2,
+ // estimation of possible length on the right
+ info.getLeftPosition()
+ + (mDataAdapter.getTotalNumber() - info.getID()) * w * 2);
+ layoutChildren();
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(float focusX, float focusY) {
+ if (isInCameraFullscreen()) return false;
+ mScaleTrend = 1f;
+ return true;
+ }
+
+ @Override
+ public boolean onScale(float focusX, float focusY, float scale) {
+ if (isInCameraFullscreen()) return false;
+
+ mScaleTrend = mScaleTrend * 0.5f + scale * 0.5f;
+ mScale *= scale;
+ if (mScale <= FILM_STRIP_SCALE) mScale = FILM_STRIP_SCALE;
+ if (mScale >= MAX_SCALE) mScale = MAX_SCALE;
+ layoutChildren();
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd() {
+ if (mScaleTrend >= 1f) {
+ if (mScale != 1f) {
+ mGeometryAnimator.scaleTo(1f, DURATION_GEOMETRY_ADJUST, false);
+ }
+
+ if (getCurrentType() == ImageData.TYPE_CAMERA_PREVIEW) {
+ if (isAnchoredTo(0)) {
+ mGeometryAnimator.lockPosition(mViewInfo[mCurrentInfo].getCenterX());
+ } else {
+ mGeometryAnimator.scrollTo(
+ mViewInfo[mCurrentInfo].getCenterX(),
+ DURATION_GEOMETRY_ADJUST, false);
+ mGeometryAnimator.setPostAction(mLockPositionRunnable);
+ }
+ }
+ } else {
+ // Scale down to film strip mode.
+ if (mScale == FILM_STRIP_SCALE) {
+ mGeometryAnimator.unlockPosition();
+ return;
+ }
+ mGeometryAnimator.scaleTo(FILM_STRIP_SCALE, DURATION_GEOMETRY_ADJUST, false);
+ mGeometryAnimator.setPostAction(mUnlockPositionRunnable);
+ }
+ }
+
+ private Runnable mLockPositionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mGeometryAnimator.lockPosition(mViewInfo[mCurrentInfo].getCenterX());
+ }
+ };
+
+ private Runnable mUnlockPositionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mGeometryAnimator.unlockPosition();
+ }
+ };
+ }
+}
diff --git a/src/com/android/gallery3d/app/CommonControllerOverlay.java b/src/com/android/gallery3d/app/CommonControllerOverlay.java
index a4f5807ae..9adb4e7a8 100644
--- a/src/com/android/gallery3d/app/CommonControllerOverlay.java
+++ b/src/com/android/gallery3d/app/CommonControllerOverlay.java
@@ -66,6 +66,10 @@ public abstract class CommonControllerOverlay extends FrameLayout implements
protected boolean mCanReplay = true;
+ public void setSeekable(boolean canSeek) {
+ mTimeBar.setSeekable(canSeek);
+ }
+
public CommonControllerOverlay(Context context) {
super(context);
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 00e4cd63b..ce9183483 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -25,7 +25,6 @@ import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
@@ -135,6 +134,17 @@ public class MoviePlayer implements
return true;
}
});
+ mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ @Override
+ public void onPrepared(MediaPlayer player) {
+ if (!mVideoView.canSeekForward() || !mVideoView.canSeekBackward()) {
+ mController.setSeekable(false);
+ } else {
+ mController.setSeekable(true);
+ }
+ setProgress();
+ }
+ });
// The SurfaceView is transparent before drawing the first frame.
// This makes the UI flashing when open a video. (black -> old screen
diff --git a/src/com/android/gallery3d/app/TimeBar.java b/src/com/android/gallery3d/app/TimeBar.java
index 402dfcfab..246346a56 100644
--- a/src/com/android/gallery3d/app/TimeBar.java
+++ b/src/com/android/gallery3d/app/TimeBar.java
@@ -259,4 +259,8 @@ public class TimeBar extends View {
}
}
+ public void setSeekable(boolean canSeek) {
+ mShowScrubber = canSeek;
+ }
+
}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index d13d261a8..4b653780e 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -69,6 +69,8 @@ import com.android.gallery3d.filtershow.provider.SharedImageProvider;
import com.android.gallery3d.filtershow.state.StateAdapter;
import com.android.gallery3d.filtershow.tools.BitmapTask;
import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.filtershow.tools.XmpPresets;
+import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults;
import com.android.gallery3d.filtershow.ui.FramedTextButton;
import com.android.gallery3d.filtershow.ui.Spline;
import com.android.gallery3d.util.GalleryUtils;
@@ -119,6 +121,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
private LoadBitmapTask mLoadBitmapTask;
private boolean mLoading = true;
+ private Uri mOriginalImageUri = null;
+ private ImagePreset mOriginalPreset = null;
+
private CategoryAdapter mCategoryLooksAdapter = null;
private CategoryAdapter mCategoryBordersAdapter = null;
private CategoryAdapter mCategoryGeometryAdapter = null;
@@ -147,6 +152,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
setDefaultPreset();
+ extractXMPData();
processIntent();
}
@@ -285,9 +291,12 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
}
mAction = intent.getAction();
-
- if (intent.getData() != null) {
- startLoadBitmap(intent.getData());
+ Uri srcUri = intent.getData();
+ if (mOriginalImageUri != null) {
+ srcUri = mOriginalImageUri;
+ }
+ if (srcUri != null) {
+ startLoadBitmap(srcUri);
} else {
pickImage();
}
@@ -364,8 +373,9 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
for (int i = 0; i < borders.size(); i++) {
FilterRepresentation filter = borders.elementAt(i);
+ filter.setScrName(getString(R.string.borders));
if (i == 0) {
- filter.setName(getString(R.string.none));
+ filter.setScrName(getString(R.string.none));
}
}
@@ -516,6 +526,11 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
mCategoryFiltersAdapter.imageLoaded();
mLoadBitmapTask = null;
+ if (mOriginalPreset != null) {
+ MasterImage.getImage().setPreset(mOriginalPreset, true);
+ mOriginalPreset = null;
+ }
+
if (mAction == TINY_PLANET_ACTION) {
showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
}
@@ -1049,4 +1064,13 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
System.loadLibrary("jni_filtershow_filters");
}
+ private void extractXMPData() {
+ XMresults res = XmpPresets.extractXMPData(
+ getBaseContext(), mMasterImage, getIntent().getData());
+ if (res == null)
+ return;
+
+ mOriginalImageUri = res.originalimage;
+ mOriginalPreset = res.preset;
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java b/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
index 8760c4a09..1ea40f202 100644
--- a/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
+++ b/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
@@ -29,8 +29,9 @@ import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
import com.android.gallery3d.filtershow.presets.FilterEnvironment;
import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.presets.PipelineInterface;
-public class CachingPipeline {
+public class CachingPipeline implements PipelineInterface {
private static final String LOGTAG = "CachingPipeline";
private boolean DEBUG = false;
@@ -65,22 +66,10 @@ public class CachingPipeline {
mName = name;
}
- public static synchronized Resources getResources() {
- return sResources;
- }
-
- public static synchronized void setResources(Resources resources) {
- sResources = resources;
- }
-
public static synchronized RenderScript getRenderScriptContext() {
return sRS;
}
- public static synchronized void setRenderScriptContext(RenderScript RS) {
- sRS = RS;
- }
-
public static synchronized void createRenderscriptContext(Activity context) {
if (sRS != null) {
Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
@@ -128,6 +117,10 @@ public class CachingPipeline {
}
}
+ public Resources getResources() {
+ return sRS.getApplicationContext().getResources();
+ }
+
private synchronized void destroyPixelAllocations() {
if (DEBUG) {
Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
@@ -167,14 +160,14 @@ public class CachingPipeline {
}
private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
- mEnvironment.setCachingPipeline(this);
+ mEnvironment.setPipeline(this);
mEnvironment.setFiltersManager(mFiltersManager);
if (highResPreview) {
mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
} else {
mEnvironment.setScaleFactor(mPreviewScaleFactor);
}
- mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
+ mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
mEnvironment.setImagePreset(preset);
mEnvironment.setStop(false);
}
@@ -293,11 +286,11 @@ public class CachingPipeline {
|| request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
if (request.getType() == RenderingRequest.ICON_RENDERING) {
- mEnvironment.setQuality(ImagePreset.QUALITY_ICON);
+ mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
} else if (request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
mEnvironment.setQuality(ImagePreset.STYLE_ICON);
} else {
- mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
+ mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
}
Bitmap bmp = preset.apply(bitmap, mEnvironment);
@@ -317,8 +310,11 @@ public class CachingPipeline {
setupEnvironment(preset, false);
mFiltersManager.freeFilterResources(preset);
preset.applyFilters(-1, -1, in, out, mEnvironment);
- // TODO: we should render the border onto a different bitmap instead
- preset.applyBorder(in, out, mEnvironment);
+ boolean copyOut = false;
+ if (preset.nbFilters() > 0) {
+ copyOut = true;
+ }
+ preset.applyBorder(in, out, copyOut, mEnvironment);
}
}
@@ -328,7 +324,7 @@ public class CachingPipeline {
return bitmap;
}
setupEnvironment(preset, false);
- mEnvironment.setQuality(ImagePreset.QUALITY_FINAL);
+ mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
mEnvironment.setScaleFactor(1.0f);
mFiltersManager.freeFilterResources(preset);
bitmap = preset.applyGeometry(bitmap, mEnvironment);
@@ -345,7 +341,7 @@ public class CachingPipeline {
}
mGeometry.useRepresentation(preset.getGeometry());
return mGeometry.apply(bitmap, mPreviewScaleFactor,
- ImagePreset.QUALITY_PREVIEW);
+ FilterEnvironment.QUALITY_PREVIEW);
}
public synchronized void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) {
@@ -458,4 +454,7 @@ public class CachingPipeline {
return mName;
}
+ public RenderScript getRSContext() {
+ return CachingPipeline.getRenderScriptContext();
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/controller/BasicParameterStyle.java b/src/com/android/gallery3d/filtershow/controller/BasicParameterStyle.java
index 072edd72a..25169c2d8 100644
--- a/src/com/android/gallery3d/filtershow/controller/BasicParameterStyle.java
+++ b/src/com/android/gallery3d/filtershow/controller/BasicParameterStyle.java
@@ -109,5 +109,4 @@ public class BasicParameterStyle implements ParameterStyles {
public void setFilterView(FilterView editor) {
mEditor = editor;
}
-
}
diff --git a/src/com/android/gallery3d/filtershow/controller/BasicSlider.java b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java
index df5b6ae73..9d8278d52 100644
--- a/src/com/android/gallery3d/filtershow/controller/BasicSlider.java
+++ b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java
@@ -84,5 +84,4 @@ public class BasicSlider implements Control {
mSeekBar.setMax(mParameter.getMaximum() - mParameter.getMinimum());
mSeekBar.setProgress(mParameter.getValue() - mParameter.getMinimum());
}
-
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropMath.java b/src/com/android/gallery3d/filtershow/crop/CropMath.java
index 849ac60ef..671554f16 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropMath.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropMath.java
@@ -196,14 +196,13 @@ public class CropMath {
float finalH = origH;
if (origA < a) {
finalH = origW / a;
+ r.top = r.centerY() - finalH / 2;
+ r.bottom = r.top + finalH;
} else {
finalW = origH * a;
+ r.left = r.centerX() - finalW / 2;
+ r.right = r.left + finalW;
}
- float centX = r.centerX();
- float centY = r.centerY();
- float hw = finalW / 2;
- float hh = finalH / 2;
- r.set(centX - hw, centY - hh, centX + hw, centY + hh);
}
/**
diff --git a/src/com/android/gallery3d/filtershow/data/FilterStackDBHelper.java b/src/com/android/gallery3d/filtershow/data/FilterStackDBHelper.java
new file mode 100644
index 000000000..e18d3104f
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/data/FilterStackDBHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.data;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+public class FilterStackDBHelper extends SQLiteOpenHelper {
+
+ public static final int DATABASE_VERSION = 1;
+ public static final String DATABASE_NAME = "filterstacks.db";
+ private static final String SQL_CREATE_TABLE = "CREATE TABLE ";
+
+ public static interface FilterStack {
+ /** The row uid */
+ public static final String _ID = "_id";
+ /** The table name */
+ public static final String TABLE = "filterstack";
+ /** The stack name */
+ public static final String STACK_ID = "stack_id";
+ /** A serialized stack of filters. */
+ public static final String FILTER_STACK= "stack";
+ }
+
+ private static final String[][] CREATE_FILTER_STACK = {
+ { FilterStack._ID, "INTEGER PRIMARY KEY AUTOINCREMENT" },
+ { FilterStack.STACK_ID, "TEXT" },
+ { FilterStack.FILTER_STACK, "BLOB" },
+ };
+
+ public FilterStackDBHelper(Context context, String name, int version) {
+ super(context, name, null, version);
+ }
+
+ public FilterStackDBHelper(Context context, String name) {
+ this(context, name, DATABASE_VERSION);
+ }
+
+ public FilterStackDBHelper(Context context) {
+ this(context, DATABASE_NAME);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createTable(db, FilterStack.TABLE, CREATE_FILTER_STACK);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ dropTable(db, FilterStack.TABLE);
+ onCreate(db);
+ }
+
+ protected static void createTable(SQLiteDatabase db, String table, String[][] columns) {
+ StringBuilder create = new StringBuilder(SQL_CREATE_TABLE);
+ create.append(table).append('(');
+ boolean first = true;
+ for (String[] column : columns) {
+ if (!first) {
+ create.append(',');
+ }
+ first = false;
+ for (String val : column) {
+ create.append(val).append(' ');
+ }
+ }
+ create.append(')');
+ db.beginTransaction();
+ try {
+ db.execSQL(create.toString());
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ protected static void dropTable(SQLiteDatabase db, String table) {
+ db.beginTransaction();
+ try {
+ db.execSQL("drop table if exists " + table);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/data/FilterStackSource.java b/src/com/android/gallery3d/filtershow/data/FilterStackSource.java
new file mode 100644
index 000000000..4e343777d
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/data/FilterStackSource.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.gallery3d.filtershow.data;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.gallery3d.filtershow.data.FilterStackDBHelper.FilterStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FilterStackSource {
+ private static final String LOGTAG = "FilterStackSource";
+
+ private SQLiteDatabase database = null;;
+ private final FilterStackDBHelper dbHelper;
+
+ public FilterStackSource(Context context) {
+ dbHelper = new FilterStackDBHelper(context);
+ }
+
+ public void open() {
+ try {
+ database = dbHelper.getWritableDatabase();
+ } catch (SQLiteException e) {
+ Log.w(LOGTAG, "could not open database", e);
+ }
+ }
+
+ public void close() {
+ database = null;
+ dbHelper.close();
+ }
+
+ public boolean insertStack(String stackName, byte[] stackBlob) {
+ boolean ret = true;
+ ContentValues val = new ContentValues();
+ val.put(FilterStack.STACK_ID, stackName);
+ val.put(FilterStack.FILTER_STACK, stackBlob);
+ database.beginTransaction();
+ try {
+ ret = (-1 != database.insert(FilterStack.TABLE, null, val));
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ }
+ return ret;
+ }
+
+ public boolean removeStack(String stackName) {
+ boolean ret = true;
+ database.beginTransaction();
+ try {
+ ret = (0 != database.delete(FilterStack.TABLE, FilterStack.STACK_ID + " = ?",
+ new String[] { stackName}));
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ }
+ return ret;
+ }
+
+ public void removeAllStacks() {
+ database.beginTransaction();
+ try {
+ database.delete(FilterStack.TABLE, null, null);
+ database.setTransactionSuccessful();
+ } finally {
+ database.endTransaction();
+ }
+ }
+
+ public byte[] getStack(String stackName) {
+ byte[] ret = null;
+ Cursor c = null;
+ database.beginTransaction();
+ try {
+ c = database.query(FilterStack.TABLE,
+ new String[] { FilterStack.FILTER_STACK },
+ FilterStack.STACK_ID + " = ?",
+ new String[] { stackName }, null, null, null, null);
+ if (c != null && c.moveToFirst() && !c.isNull(0)) {
+ ret = c.getBlob(0);
+ }
+ database.setTransactionSuccessful();
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ database.endTransaction();
+ }
+ return ret;
+ }
+
+ public List<Pair<String, byte[]>> getAllStacks() {
+ List<Pair<String, byte[]>> ret = new ArrayList<Pair<String, byte[]>>();
+ Cursor c = null;
+ database.beginTransaction();
+ try {
+ c = database.query(FilterStack.TABLE,
+ new String[] { FilterStack.STACK_ID, FilterStack.FILTER_STACK },
+ null, null, null, null, null, null);
+ if (c != null) {
+ boolean loopCheck = c.moveToFirst();
+ while (loopCheck) {
+ String name = (c.isNull(0)) ? null : c.getString(0);
+ byte[] b = (c.isNull(1)) ? null : c.getBlob(1);
+ ret.add(new Pair<String, byte[]>(name, b));
+ loopCheck = c.moveToNext();
+ }
+ }
+ database.setTransactionSuccessful();
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ database.endTransaction();
+ }
+ if (ret.size() <= 0) {
+ return null;
+ }
+ return ret;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index 9927a0a5e..1c7294c3a 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -17,6 +17,8 @@ package com.android.gallery3d.filtershow.filters;
import android.content.Context;
import android.content.res.Resources;
+import android.util.Log;
+
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -24,11 +26,14 @@ import com.android.gallery3d.filtershow.presets.ImagePreset;
import java.util.HashMap;
import java.util.Vector;
-public abstract class BaseFiltersManager {
+public abstract class BaseFiltersManager implements FiltersManagerInterface {
protected HashMap<Class, ImageFilter> mFilters = null;
+ protected HashMap<String, FilterRepresentation> mRepresentationLookup = null;
+ private static final String LOGTAG = "BaseFiltersManager";
protected void init() {
mFilters = new HashMap<Class, ImageFilter>();
+ mRepresentationLookup = new HashMap<String, FilterRepresentation>();
Vector<Class> filters = new Vector<Class>();
addFilterClasses(filters);
for (Class filterClass : filters) {
@@ -36,6 +41,12 @@ public abstract class BaseFiltersManager {
Object filterInstance = filterClass.newInstance();
if (filterInstance instanceof ImageFilter) {
mFilters.put(filterClass, (ImageFilter) filterInstance);
+
+ FilterRepresentation rep =
+ ((ImageFilter) filterInstance).getDefaultRepresentation();
+ if (rep != null) {
+ addRepresentation(rep);
+ }
}
} catch (InstantiationException e) {
e.printStackTrace();
@@ -45,6 +56,20 @@ public abstract class BaseFiltersManager {
}
}
+ public void addRepresentation(FilterRepresentation rep) {
+ mRepresentationLookup.put(rep.getSerializationName(), rep);
+ }
+
+ public FilterRepresentation createFilterFromName(String name) {
+ try {
+ return mRepresentationLookup.get(name).clone();
+ } catch (Exception e) {
+ Log.v(LOGTAG, "unable to generate a filter representation for \"" + name + "\"");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
public ImageFilter getFilter(Class c) {
return mFilters.get(c);
}
@@ -53,10 +78,6 @@ public abstract class BaseFiltersManager {
return mFilters.get(representation.getFilterClass());
}
- public void addFilter(Class filterClass, ImageFilter filter) {
- mFilters.put(filterClass, filter);
- }
-
public FilterRepresentation getRepresentation(Class c) {
ImageFilter filter = mFilters.get(c);
if (filter != null) {
@@ -89,7 +110,7 @@ public abstract class BaseFiltersManager {
protected void addFilterClasses(Vector<Class> filters) {
filters.add(ImageFilterTinyPlanet.class);
- //filters.add(ImageFilterRedEye.class);
+ filters.add(ImageFilterRedEye.class);
filters.add(ImageFilterWBalance.class);
filters.add(ImageFilterExposure.class);
filters.add(ImageFilterVignette.class);
@@ -99,7 +120,7 @@ public abstract class BaseFiltersManager {
filters.add(ImageFilterVibrance.class);
filters.add(ImageFilterSharpen.class);
filters.add(ImageFilterCurves.class);
- // filters.add(ImageFilterDraw.class);
+ filters.add(ImageFilterDraw.class);
filters.add(ImageFilterHue.class);
filters.add(ImageFilterSaturated.class);
filters.add(ImageFilterBwFilter.class);
@@ -168,8 +189,8 @@ public abstract class BaseFiltersManager {
}
public void addTools(Vector<FilterRepresentation> representations) {
- //representations.add(getRepresentation(ImageFilterRedEye.class));
- // representations.add(getRepresentation(ImageFilterDraw.class));
+ representations.add(getRepresentation(ImageFilterRedEye.class));
+ representations.add(getRepresentation(ImageFilterDraw.class));
}
public void setFilterResources(Resources resources) {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
index 4d0651edb..368e5c029 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
@@ -31,6 +31,8 @@ public class FilterBasicRepresentation extends FilterRepresentation implements P
private int mMaximum;
private int mDefaultValue;
private int mPreviewValue;
+ public static final String SERIAL_NAME = "Name";
+ public static final String SERIAL_VALUE = "Value";
private boolean mLogVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE);
public FilterBasicRepresentation(String name, int minimum, int value, int maximum) {
@@ -171,4 +173,23 @@ public class FilterBasicRepresentation extends FilterRepresentation implements P
public void copyFrom(Parameter src) {
useParametersFrom((FilterBasicRepresentation) src);
}
+
+ @Override
+ public String[][] serializeRepresentation() {
+ String[][] ret = {
+ {SERIAL_NAME , getName() },
+ {SERIAL_VALUE , Integer.toString(mValue)}};
+ return ret;
+ }
+
+ @Override
+ public void deSerializeRepresentation(String[][] rep) {
+ super.deSerializeRepresentation(rep);
+ for (int i = 0; i < rep.length; i++) {
+ if (SERIAL_VALUE.equals(rep[i][0])) {
+ mValue = Integer.parseInt(rep[i][1]);
+ break;
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java
index cbcae4b37..a32068aeb 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java
@@ -15,6 +15,7 @@ public class FilterCurvesRepresentation extends FilterRepresentation {
public FilterCurvesRepresentation() {
super("Curves");
+ setSerializationName("CURVES");
setFilterClass(ImageFilterCurves.class);
setTextId(R.string.curvesRGB);
setButtonId(R.id.curvesButtonRGB);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
index dc59b0cfc..9b144b9e9 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
@@ -49,6 +49,7 @@ public class FilterDrawRepresentation extends FilterRepresentation {
public FilterDrawRepresentation() {
super("Draw");
+ setSerializationName("DRAW");
setFilterClass(ImageFilterDraw.class);
setPriority(FilterRepresentation.TYPE_VIGNETTE);
setTextId(R.string.imageDraw);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java
index 6e2e7ea16..1ceffb4a2 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java
@@ -21,7 +21,8 @@ import com.android.gallery3d.app.Log;
import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
public class FilterFxRepresentation extends FilterRepresentation {
- private static final String LOGTAG = "FilterFxRepresentation";
+ private static final String SERIALIZATION_NAME = "LUT3D";
+ private static final String LOGTAG = "FilterFxRepresentation";
// TODO: When implementing serialization, we should find a unique way of
// specifying bitmaps / names (the resource IDs being random)
private int mBitmapResource = 0;
@@ -29,6 +30,8 @@ public class FilterFxRepresentation extends FilterRepresentation {
public FilterFxRepresentation(String name, int bitmapResource, int nameResource) {
super(name);
+ setSerializationName(SERIALIZATION_NAME);
+
mBitmapResource = bitmapResource;
mNameResource = nameResource;
setFilterClass(ImageFilterFx.class);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java
index 3f823ea1e..8a878415c 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java
@@ -28,6 +28,7 @@ public class FilterRedEyeRepresentation extends FilterPointRepresentation {
public FilterRedEyeRepresentation() {
super("RedEye",R.string.redeye,EditorRedEye.ID);
+ setSerializationName("REDEYE");
setFilterClass(ImageFilterRedEye.class);
setOverlayId(R.drawable.photoeditor_effect_redeye);
setOverlayOnly(true);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
index 82012b992..91bf676f3 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
@@ -16,7 +16,7 @@
package com.android.gallery3d.filtershow.filters;
-import com.android.gallery3d.app.Log;
+import android.util.Log;
import com.android.gallery3d.filtershow.editors.BasicEditor;
public class FilterRepresentation implements Cloneable {
@@ -34,7 +34,7 @@ public class FilterRepresentation implements Cloneable {
private boolean mShowEditingControls = true;
private boolean mShowParameterValue = true;
private boolean mShowUtilityPanel = true;
-
+ private String mSerializationName;
public static final byte TYPE_BORDER = 1;
public static final byte TYPE_FX = 2;
public static final byte TYPE_WBALANCE = 3;
@@ -63,6 +63,8 @@ public class FilterRepresentation implements Cloneable {
representation.setShowEditingControls(showEditingControls());
representation.setShowParameterValue(showParameterValue());
representation.setShowUtilityPanel(showUtilityPanel());
+ representation.mSerializationName = mSerializationName;
+
representation.mTempRepresentation =
mTempRepresentation != null ? mTempRepresentation.clone() : null;
if (DEBUG) {
@@ -96,6 +98,10 @@ public class FilterRepresentation implements Cloneable {
return mName;
}
+ public void setScrName(String name) {
+ mName = name;
+ }
+
public void setName(String name) {
mName = name;
}
@@ -104,6 +110,14 @@ public class FilterRepresentation implements Cloneable {
return mName;
}
+ public void setSerializationName(String sname) {
+ mSerializationName = sname;
+ }
+
+ public String getSerializationName() {
+ return mSerializationName;
+ }
+
public void setPriority(int priority) {
mPriority = priority;
}
@@ -241,4 +255,17 @@ public class FilterRepresentation implements Cloneable {
return "";
}
+ public String[][] serializeRepresentation() {
+ String[][] ret = { { "Name" , getName() }};
+ return ret;
+ }
+
+ public void deSerializeRepresentation(String[][] rep) {
+ for (int i = 0; i < rep.length; i++) {
+ if ("Name".equals(rep[i][0])) {
+ mName = rep[i][0];
+ break;
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java
index ac5e04601..48c8b380e 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java
@@ -20,11 +20,14 @@ import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
public class FilterTinyPlanetRepresentation extends FilterBasicRepresentation {
+ private static final String SERIALIZATION_NAME = "TINYPLANET";
private static final String LOGTAG = "FilterTinyPlanetRepresentation";
+ private static final String SERIAL_ANGLE = "Angle";
private float mAngle = 0;
public FilterTinyPlanetRepresentation() {
super("TinyPlanet", 0, 50, 100);
+ setSerializationName(SERIALIZATION_NAME);
setShowParameterValue(true);
setFilterClass(ImageFilterTinyPlanet.class);
setPriority(FilterRepresentation.TYPE_TINYPLANET);
@@ -71,4 +74,25 @@ public class FilterTinyPlanetRepresentation extends FilterBasicRepresentation {
// TinyPlanet always has an effect
return false;
}
+
+ @Override
+ public String[][] serializeRepresentation() {
+ String[][] ret = {
+ {SERIAL_NAME , getName() },
+ {SERIAL_VALUE , Integer.toString(getValue())},
+ {SERIAL_ANGLE , Float.toString(mAngle)}};
+ return ret;
+ }
+
+ @Override
+ public void deSerializeRepresentation(String[][] rep) {
+ super.deSerializeRepresentation(rep);
+ for (int i = 0; i < rep.length; i++) {
+ if (SERIAL_VALUE.equals(rep[i][0])) {
+ setValue(Integer.parseInt(rep[i][1]));
+ } else if (SERIAL_ANGLE.equals(rep[i][0])) {
+ setAngle(Float.parseFloat(rep[i][1]));
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
index eef54ef58..9827088ff 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
@@ -29,6 +29,7 @@ public class FilterVignetteRepresentation extends FilterBasicRepresentation impl
public FilterVignetteRepresentation() {
super("Vignette", -100, 50, 100);
+ setSerializationName("VIGNETTE");
setShowParameterValue(true);
setPriority(FilterRepresentation.TYPE_VIGNETTE);
setTextId(R.string.vignette);
@@ -111,4 +112,44 @@ public class FilterVignetteRepresentation extends FilterBasicRepresentation impl
public boolean isNil() {
return getValue() == 0;
}
+
+ private static final String[] sParams = {
+ "Name", "value", "mCenterX", "mCenterY", "mRadiusX",
+ "mRadiusY"
+ };
+
+ @Override
+ public String[][] serializeRepresentation() {
+ String[][] ret = {
+ { sParams[0], getName() },
+ { sParams[1], Integer.toString(getValue()) },
+ { sParams[2], Float.toString(mCenterX) },
+ { sParams[3], Float.toString(mCenterY) },
+ { sParams[4], Float.toString(mRadiusX) },
+ { sParams[5], Float.toString(mRadiusY) }
+ };
+ return ret;
+ }
+
+ @Override
+ public void deSerializeRepresentation(String[][] rep) {
+ super.deSerializeRepresentation(rep);
+ for (int i = 0; i < rep.length; i++) {
+ String key = rep[i][0];
+ String value = rep[i][1];
+ if (sParams[0].equals(key)) {
+ setName(value);
+ } else if (sParams[1].equals(key)) {
+ setValue(Integer.parseInt(value));
+ } else if (sParams[2].equals(key)) {
+ mCenterX = Float.parseFloat(value);
+ } else if (sParams[3].equals(key)) {
+ mCenterY = Float.parseFloat(value);
+ } else if (sParams[4].equals(key)) {
+ mRadiusX = Float.parseFloat(value);
+ } else if (sParams[5].equals(key)) {
+ mRadiusY = Float.parseFloat(value);
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/FiltersManagerInterface.java b/src/com/android/gallery3d/filtershow/filters/FiltersManagerInterface.java
new file mode 100644
index 000000000..710128f99
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/filters/FiltersManagerInterface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.filters;
+
+public interface FiltersManagerInterface {
+ ImageFilter getFilterForRepresentation(FilterRepresentation representation);
+}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
index 96ab84f9d..b80fc7f15 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
@@ -16,12 +16,12 @@
package com.android.gallery3d.filtershow.filters;
+import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.support.v8.renderscript.Allocation;
import android.widget.Toast;
-import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
import com.android.gallery3d.filtershow.presets.FilterEnvironment;
import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -35,9 +35,9 @@ public abstract class ImageFilter implements Cloneable {
// TODO: Temporary, for dogfood note memory issues with toasts for better
// feedback. Remove this when filters actually work in low memory
// situations.
- private static FilterShowActivity sActivity = null;
+ private static Activity sActivity = null;
- public static void setActivityForMemoryToasts(FilterShowActivity activity) {
+ public static void setActivityForMemoryToasts(Activity activity) {
sActivity = activity;
}
@@ -76,10 +76,6 @@ public abstract class ImageFilter implements Cloneable {
return bitmap;
}
- public ImagePreset getImagePreset() {
- return getEnvironment().getImagePreset();
- }
-
public abstract void useRepresentation(FilterRepresentation representation);
native protected void nativeApplyGradientFilter(Bitmap bitmap, int w, int h,
@@ -90,10 +86,11 @@ public abstract class ImageFilter implements Cloneable {
}
protected Matrix getOriginalToScreenMatrix(int w, int h) {
- GeometryMetadata geo = getImagePreset().mGeoData;
+ ImagePreset preset = getEnvironment().getImagePreset();
+ GeometryMetadata geo = getEnvironment().getImagePreset().mGeoData;
Matrix originalToScreen = geo.getOriginalToScreen(true,
- getImagePreset().getImageLoader().getOriginalBounds().width(),
- getImagePreset().getImageLoader().getOriginalBounds().height(),
+ preset.getImageLoader().getOriginalBounds().width(),
+ preset.getImageLoader().getOriginalBounds().height(),
w, h);
return originalToScreen;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
index a4626cdb2..64c48dffa 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
@@ -23,6 +23,7 @@ import android.graphics.Color;
public class ImageFilterBwFilter extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "BWFILTER";
public ImageFilterBwFilter() {
mName = "BW Filter";
@@ -31,6 +32,8 @@ public class ImageFilterBwFilter extends SimpleImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("BW Filter");
+ representation.setSerializationName(SERIALIZATION_NAME);
+
representation.setFilterClass(ImageFilterBwFilter.class);
representation.setMaximum(180);
representation.setMinimum(-180);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
index 2097f0d6e..c8b41c248 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
@@ -21,6 +21,7 @@ import com.android.gallery3d.R;
import android.graphics.Bitmap;
public class ImageFilterContrast extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "CONTRAST";
public ImageFilterContrast() {
mName = "Contrast";
@@ -30,6 +31,8 @@ public class ImageFilterContrast extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Contrast");
+ representation.setSerializationName(SERIALIZATION_NAME);
+
representation.setFilterClass(ImageFilterContrast.class);
representation.setTextId(R.string.contrast);
representation.setButtonId(R.id.contrastButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java
index 0b02fc4f6..ea2ff351d 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java
@@ -24,6 +24,7 @@ import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.cache.ImageLoader;
public class ImageFilterDownsample extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "DOWNSAMPLE";
private static final int ICON_DOWNSAMPLE_FRACTION = 8;
private ImageLoader mImageLoader;
@@ -35,6 +36,8 @@ public class ImageFilterDownsample extends SimpleImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Downsample");
+ representation.setSerializationName(SERIALIZATION_NAME);
+
representation.setFilterClass(ImageFilterDownsample.class);
representation.setMaximum(100);
representation.setMinimum(1);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java
index 1fd9071f7..812ab02f0 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java
@@ -31,6 +31,7 @@ import android.graphics.PorterDuffColorFilter;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.presets.FilterEnvironment;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import java.util.Vector;
@@ -204,7 +205,7 @@ public class ImageFilterDraw extends ImageFilter {
public void drawData(Canvas canvas, Matrix originalRotateToScreen, int quality) {
Paint paint = new Paint();
- if (quality == ImagePreset.QUALITY_FINAL) {
+ if (quality == FilterEnvironment.QUALITY_FINAL) {
paint.setAntiAlias(true);
}
paint.setStyle(Style.STROKE);
@@ -214,7 +215,7 @@ public class ImageFilterDraw extends ImageFilter {
if (mParameters.getDrawing().isEmpty() && mParameters.getCurrentDrawing() == null) {
return;
}
- if (quality == ImagePreset.QUALITY_FINAL) {
+ if (quality == FilterEnvironment.QUALITY_FINAL) {
for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) {
paint(strokeData, canvas, originalRotateToScreen, quality);
}
@@ -248,17 +249,17 @@ public class ImageFilterDraw extends ImageFilter {
int n = v.size();
for (int i = mCachedStrokes; i < n; i++) {
- paint(v.get(i), drawCache, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW);
+ paint(v.get(i), drawCache, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW);
}
mCachedStrokes = n;
}
public void draw(Canvas canvas, Matrix originalRotateToScreen) {
for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) {
- paint(strokeData, canvas, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW);
+ paint(strokeData, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW);
}
mDrawingsTypes[mCurrentStyle].paint(
- null, canvas, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW);
+ null, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW);
}
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java
index 46a9a294c..82de2b73a 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java
@@ -21,7 +21,7 @@ import android.graphics.Bitmap;
import com.android.gallery3d.R;
public class ImageFilterEdge extends SimpleImageFilter {
-
+ private static final String SERIALIZATION_NAME = "EDGE";
public ImageFilterEdge() {
mName = "Edge";
}
@@ -29,6 +29,7 @@ public class ImageFilterEdge extends SimpleImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterRepresentation representation = super.getDefaultRepresentation();
representation.setName("Edge");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterEdge.class);
representation.setTextId(R.string.edge);
representation.setButtonId(R.id.edgeButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
index b0b0b2dd8..6fdcd249b 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
@@ -21,7 +21,7 @@ import com.android.gallery3d.R;
import android.graphics.Bitmap;
public class ImageFilterExposure extends SimpleImageFilter {
-
+ private static final String SERIALIZATION_NAME = "EXPOSURE";
public ImageFilterExposure() {
mName = "Exposure";
}
@@ -30,6 +30,7 @@ public class ImageFilterExposure extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Exposure");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterExposure.class);
representation.setTextId(R.string.exposure);
representation.setButtonId(R.id.exposureButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
index 68e8a7c9d..51c66127b 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java
@@ -37,6 +37,11 @@ public class ImageFilterFx extends ImageFilter {
mFxBitmap = null;
}
+ @Override
+ public FilterRepresentation getDefaultRepresentation() {
+ return null;
+ }
+
public void useRepresentation(FilterRepresentation representation) {
FilterFxRepresentation parameters = (FilterFxRepresentation) representation;
mParameters = parameters;
@@ -87,4 +92,5 @@ public class ImageFilterFx extends ImageFilter {
public void setResources(Resources resources) {
mResources = resources;
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java
index 0022a9eae..0725dd1c4 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java
@@ -21,6 +21,7 @@ import android.graphics.Bitmap;
import com.android.gallery3d.R;
public class ImageFilterHighlights extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "HIGHLIGHTS";
private static final String LOGTAG = "ImageFilterVignette";
public ImageFilterHighlights() {
@@ -33,7 +34,8 @@ public class ImageFilterHighlights extends SimpleImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
- representation.setName("Shadows");
+ representation.setName("Highlights");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterHighlights.class);
representation.setTextId(R.string.highlight_recovery);
representation.setButtonId(R.id.highlightRecoveryButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
index b1f9f7365..7e6f68548 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
@@ -22,6 +22,7 @@ import com.android.gallery3d.filtershow.editors.BasicEditor;
import android.graphics.Bitmap;
public class ImageFilterHue extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "HUE";
private ColorSpaceMatrix cmatrix = null;
public ImageFilterHue() {
@@ -33,6 +34,7 @@ public class ImageFilterHue extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Hue");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterHue.class);
representation.setMinimum(-180);
representation.setMaximum(180);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java
index 29e6d162f..93813813f 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java
@@ -22,6 +22,7 @@ import android.text.format.Time;
import com.android.gallery3d.R;
public class ImageFilterKMeans extends SimpleImageFilter {
+ private static final String SERIALIZATION_NAME = "KMEANS";
private int mSeed = 0;
public ImageFilterKMeans() {
@@ -36,6 +37,7 @@ public class ImageFilterKMeans extends SimpleImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("KMeans");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterKMeans.class);
representation.setMaximum(20);
representation.setMinimum(2);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
index c256686fb..0747190fa 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
@@ -6,13 +6,14 @@ import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
public class ImageFilterNegative extends ImageFilter {
-
+ private static final String SERIALIZATION_NAME = "NEGATIVE";
public ImageFilterNegative() {
mName = "Negative";
}
public FilterRepresentation getDefaultRepresentation() {
FilterRepresentation representation = new FilterDirectRepresentation("Negative");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterNegative.class);
representation.setTextId(R.string.negative);
representation.setButtonId(R.id.negativeButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
index cfbb560c7..69d18f805 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
@@ -22,13 +22,14 @@ import android.support.v8.renderscript.*;
import android.util.Log;
import android.content.res.Resources;
import com.android.gallery3d.R;
-import com.android.gallery3d.filtershow.cache.CachingPipeline;
+import com.android.gallery3d.filtershow.presets.PipelineInterface;
public abstract class ImageFilterRS extends ImageFilter {
private static final String LOGTAG = "ImageFilterRS";
private boolean DEBUG = false;
private int mLastInputWidth = 0;
private int mLastInputHeight = 0;
+ private long mLastTimeCalled;
public static boolean PERF_LOGGING = false;
@@ -51,26 +52,36 @@ public abstract class ImageFilterRS extends ImageFilter {
}
protected RenderScript getRenderScriptContext() {
- return CachingPipeline.getRenderScriptContext();
+ PipelineInterface pipeline = getEnvironment().getPipeline();
+ return pipeline.getRSContext();
}
protected Allocation getInPixelsAllocation() {
- CachingPipeline pipeline = getEnvironment().getCachingPipeline();
+ PipelineInterface pipeline = getEnvironment().getPipeline();
return pipeline.getInPixelsAllocation();
}
protected Allocation getOutPixelsAllocation() {
- CachingPipeline pipeline = getEnvironment().getCachingPipeline();
+ PipelineInterface pipeline = getEnvironment().getPipeline();
return pipeline.getOutPixelsAllocation();
}
@Override
public void apply(Allocation in, Allocation out) {
long startOverAll = System.nanoTime();
+ if (PERF_LOGGING) {
+ long delay = (startOverAll - mLastTimeCalled) / 1000;
+ String msg = String.format("%s; image size %dx%d; ", getName(),
+ in.getType().getX(), in.getType().getY());
+ msg += String.format("called after %.2f ms (%.2f FPS); ",
+ delay / 1000.f, 1000000.f / delay);
+ Log.i(LOGTAG, msg);
+ }
+ mLastTimeCalled = startOverAll;
long startFilter = 0;
long endFilter = 0;
if (!mResourcesLoaded) {
- CachingPipeline pipeline = getEnvironment().getCachingPipeline();
+ PipelineInterface pipeline = getEnvironment().getPipeline();
createFilter(pipeline.getResources(), getEnvironment().getScaleFactor(),
getEnvironment().getQuality(), in);
mResourcesLoaded = true;
@@ -102,7 +113,7 @@ public abstract class ImageFilterRS extends ImageFilter {
return bitmap;
}
try {
- CachingPipeline pipeline = getEnvironment().getCachingPipeline();
+ PipelineInterface pipeline = getEnvironment().getPipeline();
if (DEBUG) {
Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName());
}
@@ -137,18 +148,16 @@ public abstract class ImageFilterRS extends ImageFilter {
displayLowMemoryToast();
Log.e(LOGTAG, "not enough memory for filter " + getName(), e);
}
-
return bitmap;
}
- protected static Allocation convertBitmap(Bitmap bitmap) {
- return Allocation.createFromBitmap(CachingPipeline.getRenderScriptContext(), bitmap,
+ protected static Allocation convertBitmap(RenderScript RS, Bitmap bitmap) {
+ return Allocation.createFromBitmap(RS, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_TEXTURE);
}
- private static Allocation convertRGBAtoA(Bitmap bitmap) {
- RenderScript RS = CachingPipeline.getRenderScriptContext();
+ private static Allocation convertRGBAtoA(RenderScript RS, Bitmap bitmap) {
if (RS != mRScache || mGreyConvert == null) {
mGreyConvert = new ScriptC_grey(RS, RS.getApplicationContext().getResources(),
R.raw.grey);
@@ -157,7 +166,7 @@ public abstract class ImageFilterRS extends ImageFilter {
Type.Builder tb_a8 = new Type.Builder(RS, Element.A_8(RS));
- Allocation bitmapTemp = convertBitmap(bitmap);
+ Allocation bitmapTemp = convertBitmap(RS, bitmap);
if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(RS))) {
return bitmapTemp;
}
@@ -173,20 +182,20 @@ public abstract class ImageFilterRS extends ImageFilter {
}
public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) {
- Resources res = CachingPipeline.getResources();
+ Resources res = getEnvironment().getPipeline().getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ALPHA_8;
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeResource(
res,
resource, options);
- Allocation ret = convertRGBAtoA(bitmap);
+ Allocation ret = convertRGBAtoA(getRenderScriptContext(), bitmap);
bitmap.recycle();
return ret;
}
public Allocation loadScaledResourceAlpha(int resource, int w, int h, int inSampleSize) {
- Resources res = CachingPipeline.getResources();
+ Resources res = getEnvironment().getPipeline().getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ALPHA_8;
options.inSampleSize = inSampleSize;
@@ -194,7 +203,7 @@ public abstract class ImageFilterRS extends ImageFilter {
res,
resource, options);
Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
- Allocation ret = convertRGBAtoA(resizeBitmap);
+ Allocation ret = convertRGBAtoA(getRenderScriptContext(), resizeBitmap);
resizeBitmap.recycle();
bitmap.recycle();
return ret;
@@ -205,13 +214,13 @@ public abstract class ImageFilterRS extends ImageFilter {
}
public Allocation loadResource(int resource) {
- Resources res = CachingPipeline.getResources();
+ Resources res = getEnvironment().getPipeline().getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeResource(
res,
resource, options);
- Allocation ret = convertBitmap(bitmap);
+ Allocation ret = convertBitmap(getRenderScriptContext(), bitmap);
bitmap.recycle();
return ret;
}
@@ -232,7 +241,7 @@ public abstract class ImageFilterRS extends ImageFilter {
/**
* RS Script objects (and all other RS objects) should be cleared here
*/
- abstract protected void resetScripts();
+ public abstract void resetScripts();
/**
* Scripts values should be bound here
@@ -244,6 +253,8 @@ public abstract class ImageFilterRS extends ImageFilter {
return;
}
resetAllocations();
+ mLastInputWidth = 0;
+ mLastInputHeight = 0;
setResourcesLoaded(false);
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
index 0febe4957..adc74c5ec 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
@@ -21,7 +21,7 @@ import com.android.gallery3d.R;
import android.graphics.Bitmap;
public class ImageFilterSaturated extends SimpleImageFilter {
-
+ private static final String SERIALIZATION_NAME = "SATURATED";
public ImageFilterSaturated() {
mName = "Saturated";
}
@@ -31,6 +31,7 @@ public class ImageFilterSaturated extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Saturated");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterSaturated.class);
representation.setTextId(R.string.saturation);
representation.setButtonId(R.id.saturationButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
index fd67ee8fc..845290b80 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
@@ -21,7 +21,7 @@ import com.android.gallery3d.R;
import android.graphics.Bitmap;
public class ImageFilterShadows extends SimpleImageFilter {
-
+ private static final String SERIALIZATION_NAME = "SHADOWS";
public ImageFilterShadows() {
mName = "Shadows";
@@ -31,6 +31,7 @@ public class ImageFilterShadows extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Shadows");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterShadows.class);
representation.setTextId(R.string.shadow_recovery);
representation.setButtonId(R.id.shadowRecoveryButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
index 76ae475ac..1dc2c0516 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
@@ -19,7 +19,7 @@ package com.android.gallery3d.filtershow.filters;
import com.android.gallery3d.R;
public class ImageFilterSharpen extends ImageFilterRS {
-
+ private static final String SERIALIZATION_NAME = "SHARPEN";
private static final String LOGTAG = "ImageFilterSharpen";
private ScriptC_convolve3x3 mScript;
@@ -31,6 +31,7 @@ public class ImageFilterSharpen extends ImageFilterRS {
public FilterRepresentation getDefaultRepresentation() {
FilterRepresentation representation = new FilterBasicRepresentation("Sharpen", 0, 0, 100);
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setShowParameterValue(true);
representation.setFilterClass(ImageFilterSharpen.class);
representation.setTextId(R.string.sharpness);
@@ -52,7 +53,7 @@ public class ImageFilterSharpen extends ImageFilterRS {
}
@Override
- protected void resetScripts() {
+ public void resetScripts() {
if (mScript != null) {
mScript.destroy();
mScript = null;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
index 37d5739a0..f265c4dcc 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
@@ -76,7 +76,7 @@ public class ImageFilterTinyPlanet extends SimpleImageFilter {
int w = bitmapIn.getWidth();
int h = bitmapIn.getHeight();
int outputSize = (int) (w / 2f);
- ImagePreset preset = getImagePreset();
+ ImagePreset preset = getEnvironment().getImagePreset();
Bitmap mBitmapOut = null;
if (preset != null) {
XMPMeta xmp = preset.getImageLoader().getXmpObject();
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
index ea315d326..900fd906c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
@@ -21,7 +21,7 @@ import com.android.gallery3d.R;
import android.graphics.Bitmap;
public class ImageFilterVibrance extends SimpleImageFilter {
-
+ private static final String SERIALIZATION_NAME = "VIBRANCE";
public ImageFilterVibrance() {
mName = "Vibrance";
}
@@ -30,6 +30,7 @@ public class ImageFilterVibrance extends SimpleImageFilter {
FilterBasicRepresentation representation =
(FilterBasicRepresentation) super.getDefaultRepresentation();
representation.setName("Vibrance");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterVibrance.class);
representation.setTextId(R.string.vibrance);
representation.setButtonId(R.id.vibranceButton);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
index e06f54493..cfe135033 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
@@ -22,7 +22,7 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import com.android.gallery3d.R;
-import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.presets.FilterEnvironment;
public class ImageFilterVignette extends SimpleImageFilter {
private static final String LOGTAG = "ImageFilterVignette";
@@ -57,9 +57,9 @@ public class ImageFilterVignette extends SimpleImageFilter {
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) {
- if (SIMPLE_ICONS && ImagePreset.QUALITY_ICON == quality) {
+ if (SIMPLE_ICONS && FilterEnvironment.QUALITY_ICON == quality) {
if (mOverlayBitmap == null) {
- Resources res = getEnvironment().getCachingPipeline().getResources();
+ Resources res = getEnvironment().getPipeline().getResources();
mOverlayBitmap = IconUtilities.getFXBitmap(res,
R.drawable.filtershow_icon_vignette);
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index c4c293a4b..84a14c902 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -22,6 +22,7 @@ import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
import android.graphics.Bitmap;
public class ImageFilterWBalance extends ImageFilter {
+ private static final String SERIALIZATION_NAME = "WBALANCE";
private static final String TAG = "ImageFilterWBalance";
public ImageFilterWBalance() {
@@ -30,6 +31,7 @@ public class ImageFilterWBalance extends ImageFilter {
public FilterRepresentation getDefaultRepresentation() {
FilterRepresentation representation = new FilterDirectRepresentation("WBalance");
+ representation.setSerializationName(SERIALIZATION_NAME);
representation.setFilterClass(ImageFilterWBalance.class);
representation.setPriority(FilterRepresentation.TYPE_WBALANCE);
representation.setTextId(R.string.wbalance);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index e5820a8f2..77dbd5e7b 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -20,6 +20,7 @@ import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.Log;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.crop.CropExtras;
@@ -30,7 +31,14 @@ import com.android.gallery3d.filtershow.editors.EditorStraighten;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+
public class GeometryMetadata extends FilterRepresentation {
+ private static final String SERIALIZATION_NAME = "GEOM";
private static final String LOGTAG = "GeometryMetadata";
private float mScaleFactor = 1.0f;
private float mRotation = 0;
@@ -40,7 +48,26 @@ public class GeometryMetadata extends FilterRepresentation {
private FLIP mFlip = FLIP.NONE;
public enum FLIP {
- NONE, VERTICAL, HORIZONTAL, BOTH
+ NONE("N"), VERTICAL("V"), HORIZONTAL("H"), BOTH("B");
+ String mValue;
+
+ FLIP(String name) {
+ mValue = name;
+ }
+
+ public static FLIP parse(String name){
+ switch (name.charAt(0)) {
+ case 'N':
+ return NONE;
+ case 'V':
+ return VERTICAL;
+ case 'H':
+ return HORIZONTAL;
+ case 'B':
+ return BOTH;
+ };
+ return NONE;
+ }
}
// Output format data from intent extras
@@ -64,6 +91,7 @@ public class GeometryMetadata extends FilterRepresentation {
public GeometryMetadata() {
super("GeometryMetadata");
+ setSerializationName(SERIALIZATION_NAME);
setFilterClass(ImageFilterGeometry.class);
setEditorId(EditorCrop.ID);
setTextId(0);
@@ -492,4 +520,87 @@ public class GeometryMetadata extends FilterRepresentation {
representation.useParametersFrom(this);
return representation;
}
+
+ private static final String[] sParams = {
+ "Name", "ScaleFactor", "Rotation", "StraightenRotation", "CropBoundsLeft",
+ "CropBoundsTop", "CropBoundsRight", "CropBoundsBottom", "PhotoBoundsLeft",
+ "PhotoBoundsTop", "PhotoBoundsRight", "PhotoBoundsBottom", "Flip"
+ };
+
+ @Override
+ public String[][] serializeRepresentation() {
+ String[][] ret = {
+ { "Name", getName() },
+ { "ScaleFactor", Float.toString(mScaleFactor) },
+ { "Rotation", Float.toString(mRotation) },
+ { "StraightenRotation", Float.toString(mStraightenRotation) },
+ { "CropBoundsLeft", Float.toString(mCropBounds.left) },
+ { "CropBoundsTop", Float.toString(mCropBounds.top) },
+ { "CropBoundsRight", Float.toString(mCropBounds.right) },
+ { "CropBoundsBottom", Float.toString(mCropBounds.bottom) },
+ { "PhotoBoundsLeft", Float.toString(mPhotoBounds.left) },
+ { "PhotoBoundsTop", Float.toString(mPhotoBounds.top) },
+ { "PhotoBoundsRight", Float.toString(mPhotoBounds.right) },
+ { "PhotoBoundsBottom", Float.toString(mPhotoBounds.bottom) },
+ { "Flip", mFlip.mValue } };
+ return ret;
+ }
+
+ @Override
+ public void deSerializeRepresentation(String[][] rep) {
+ HashMap<String, Integer> map = new HashMap<String, Integer>();
+ for (int i = 0; i < sParams.length; i++) {
+ map.put(sParams[i], i);
+ }
+ for (int i = 0; i < rep.length; i++) {
+ String key = rep[i][0];
+ String value = rep[i][1];
+
+ switch (map.get(key)) {
+ case -1: // Unknown
+ break;
+ case 0:
+ if (!getName().equals(value)) {
+ throw new IllegalArgumentException("Not a "+getName());
+ }
+ break;
+ case 1: // "ScaleFactor", Float
+ mScaleFactor = Float.parseFloat(value);
+ break;
+ case 2: // "Rotation", Float
+ mRotation = Float.parseFloat(value);
+ break;
+ case 3: // "StraightenRotation", Float
+ mStraightenRotation = Float.parseFloat(value);
+ break;
+ case 4: // "mCropBoundsLeft", Float
+ mCropBounds.left = Float.parseFloat(value);
+ break;
+ case 5: // "mCropBoundsTop", Float
+ mCropBounds.top = Float.parseFloat(value);
+ break;
+ case 6: // "mCropBoundsRight", Float
+ mCropBounds.right = Float.parseFloat(value);
+ break;
+ case 7: // "mCropBoundsBottom", Float
+ mCropBounds.bottom = Float.parseFloat(value);
+ break;
+ case 8: // "mPhotoBoundsLeft", Float
+ mPhotoBounds.left = Float.parseFloat(value);
+ break;
+ case 9: // "mPhotoBoundsTop", Float
+ mPhotoBounds.top = Float.parseFloat(value);
+ break;
+ case 10: // "mPhotoBoundsRight", Float
+ mPhotoBounds.right = Float.parseFloat(value);
+ break;
+ case 11: // "mPhotoBoundsBottom", Float
+ mPhotoBounds.bottom = Float.parseFloat(value);
+ break;
+ case 12: // "Flip", enum
+ mFlip = FLIP.parse(value);
+ break;
+ }
+ }
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
index 5e5d70b7c..7e086b0ca 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
@@ -20,6 +20,7 @@ import android.graphics.*;
import android.os.Handler;
import android.os.Message;
+import android.util.Log;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.HistoryAdapter;
import com.android.gallery3d.filtershow.cache.*;
@@ -139,6 +140,7 @@ public class MasterImage implements RenderingRequestCaller {
}
public synchronized void setPreset(ImagePreset preset, boolean addToHistory) {
+ preset.showFilters();
mPreset = preset;
mPreset.setImageLoader(mLoader);
setGeometry();
diff --git a/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java b/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
index c454c1ab6..47f8dfccb 100644
--- a/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
+++ b/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
@@ -18,11 +18,9 @@ package com.android.gallery3d.filtershow.presets;
import android.graphics.Bitmap;
import android.support.v8.renderscript.Allocation;
-import android.util.Log;
-import com.android.gallery3d.filtershow.cache.CachingPipeline;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
-import com.android.gallery3d.filtershow.filters.FiltersManager;
+import com.android.gallery3d.filtershow.filters.FiltersManagerInterface;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import java.lang.ref.WeakReference;
@@ -33,10 +31,14 @@ public class FilterEnvironment {
private ImagePreset mImagePreset;
private float mScaleFactor;
private int mQuality;
- private FiltersManager mFiltersManager;
- private CachingPipeline mCachingPipeline;
+ private FiltersManagerInterface mFiltersManager;
+ private PipelineInterface mPipeline;
private volatile boolean mStop = false;
+ public static final int QUALITY_ICON = 0;
+ public static final int QUALITY_PREVIEW = 1;
+ public static final int QUALITY_FINAL = 2;
+
public synchronized boolean needsStop() {
return mStop;
}
@@ -98,11 +100,11 @@ public class FilterEnvironment {
return mQuality;
}
- public void setFiltersManager(FiltersManager filtersManager) {
+ public void setFiltersManager(FiltersManagerInterface filtersManager) {
mFiltersManager = filtersManager;
}
- public FiltersManager getFiltersManager() {
+ public FiltersManagerInterface getFiltersManager() {
return mFiltersManager;
}
@@ -126,12 +128,12 @@ public class FilterEnvironment {
return ret;
}
- public CachingPipeline getCachingPipeline() {
- return mCachingPipeline;
+ public PipelineInterface getPipeline() {
+ return mPipeline;
}
- public void setCachingPipeline(CachingPipeline cachingPipeline) {
- mCachingPipeline = cachingPipeline;
+ public void setPipeline(PipelineInterface cachingPipeline) {
+ mPipeline = cachingPipeline;
}
}
diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
index 2a7e601ee..84766958d 100644
--- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
@@ -18,12 +18,19 @@ package com.android.gallery3d.filtershow.presets;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.net.Uri;
import android.support.v8.renderscript.Allocation;
+import android.util.JsonReader;
+import android.util.JsonWriter;
import android.util.Log;
+import com.adobe.xmp.XMPException;
+import com.adobe.xmp.XMPMeta;
+import com.adobe.xmp.options.PropertyOptions;
import com.android.gallery3d.filtershow.cache.CachingPipeline;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
+import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
@@ -32,6 +39,10 @@ import com.android.gallery3d.filtershow.state.State;
import com.android.gallery3d.filtershow.state.StateAdapter;
import com.android.gallery3d.util.UsageStatistics;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Vector;
public class ImagePreset {
@@ -39,10 +50,8 @@ public class ImagePreset {
private static final String LOGTAG = "ImagePreset";
private FilterRepresentation mBorder = null;
- public static final int QUALITY_ICON = 0;
- public static final int QUALITY_PREVIEW = 1;
- public static final int QUALITY_FINAL = 2;
public static final int STYLE_ICON = 3;
+ public static final String PRESET_NAME = "PresetName";
private ImageLoader mImageLoader = null;
@@ -210,11 +219,11 @@ public class ImagePreset {
}
for (FilterRepresentation representation : mFilters) {
if (representation.getPriority() == FilterRepresentation.TYPE_VIGNETTE
- && !representation.isNil()) {
+ && !representation.isNil()) {
return false;
}
if (representation.getPriority() == FilterRepresentation.TYPE_TINYPLANET
- && !representation.isNil()) {
+ && !representation.isNil()) {
return false;
}
}
@@ -460,7 +469,7 @@ public class ImagePreset {
if (mBorder != null && mDoApplyGeometry) {
mBorder.synchronizeRepresentation();
bitmap = environment.applyRepresentation(mBorder, bitmap);
- if (environment.getQuality() == QUALITY_FINAL) {
+ if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
"SaveBorder", mBorder.getName(), 1);
}
@@ -468,6 +477,10 @@ public class ImagePreset {
return bitmap;
}
+ public int nbFilters() {
+ return mFilters.size();
+ }
+
public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
if (mDoApplyFilters) {
if (from < 0) {
@@ -483,7 +496,7 @@ public class ImagePreset {
representation.synchronizeRepresentation();
}
bitmap = environment.applyRepresentation(representation, bitmap);
- if (environment.getQuality() == QUALITY_FINAL) {
+ if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
"SaveFilter", representation.getName(), 1);
}
@@ -496,17 +509,23 @@ public class ImagePreset {
return bitmap;
}
- public void applyBorder(Allocation in, Allocation out, FilterEnvironment environment) {
+ public void applyBorder(Allocation in, Allocation out,
+ boolean copyOut, FilterEnvironment environment) {
if (mBorder != null && mDoApplyGeometry) {
mBorder.synchronizeRepresentation();
// TODO: should keep the bitmap around
- Allocation bitmapIn = Allocation.createTyped(CachingPipeline.getRenderScriptContext(), in.getType());
- bitmapIn.copyFrom(out);
+ Allocation bitmapIn = in;
+ if (copyOut) {
+ bitmapIn = Allocation.createTyped(
+ CachingPipeline.getRenderScriptContext(), in.getType());
+ bitmapIn.copyFrom(out);
+ }
environment.applyRepresentation(mBorder, bitmapIn, out);
}
}
- public void applyFilters(int from, int to, Allocation in, Allocation out, FilterEnvironment environment) {
+ public void applyFilters(int from, int to, Allocation in, Allocation out,
+ FilterEnvironment environment) {
if (mDoApplyFilters) {
if (from < 0) {
from = 0;
@@ -605,4 +624,109 @@ public class ImagePreset {
return usedFilters;
}
+ public String getJsonString(String name) {
+ StringWriter swriter = new StringWriter();
+ try {
+ JsonWriter writer = new JsonWriter(swriter);
+ writeJson(writer, name);
+ writer.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return swriter.toString();
+ }
+
+ public void writeJson(JsonWriter writer, String name) {
+ int numFilters = mFilters.size();
+ try {
+ writer.beginObject();
+ writer.name(PRESET_NAME).value(name);
+ writer.name(mGeoData.getSerializationName());
+ writer.beginObject();
+ {
+ String[][] rep = mGeoData.serializeRepresentation();
+ for (int i = 0; i < rep.length; i++) {
+ writer.name(rep[i][0]);
+ writer.value(rep[i][1]);
+ }
+ }
+ writer.endObject();
+
+ for (int i = 0; i < numFilters; i++) {
+ FilterRepresentation filter = mFilters.get(i);
+ String sname = filter.getSerializationName();
+ writer.name(sname);
+ writer.beginObject();
+ {
+ String[][] rep = filter.serializeRepresentation();
+ for (int k = 0; k < rep.length; k++) {
+ writer.name(rep[k][0]);
+ writer.value(rep[k][1]);
+ }
+ }
+ writer.endObject();
+ }
+ writer.endObject();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean readJsonFromString(String filterString) {
+ StringReader sreader = new StringReader(filterString);
+ try {
+ JsonReader reader = new JsonReader(sreader);
+ boolean ok = readJson(reader);
+ if (!ok) {
+ return false;
+ }
+ reader.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ public boolean readJson(JsonReader sreader) throws IOException {
+ sreader.beginObject();
+ sreader.nextName();
+ mName = sreader.nextString();
+
+ while (sreader.hasNext()) {
+ String name = sreader.nextName();
+
+ if (mGeoData.getSerializationName().equals(name)) {
+ mGeoData.deSerializeRepresentation(read(sreader));
+ } else {
+ FilterRepresentation filter = creatFilterFromName(name);
+ if (filter == null)
+ return false;
+ filter.deSerializeRepresentation(read(sreader));
+ addFilter(filter);
+ }
+ }
+ sreader.endObject();
+ return true;
+ }
+
+ FilterRepresentation creatFilterFromName(String name) {
+ FiltersManager filtersManager = FiltersManager.getManager();
+ return filtersManager.createFilterFromName(name);
+ }
+
+ String[][] read(JsonReader reader) throws IOException {
+ ArrayList <String[]> al = new ArrayList<String[]>();
+
+ reader.beginObject();
+
+ while (reader.hasNext()) {
+ String[]kv = { reader.nextName(),reader.nextString()};
+ al.add(kv);
+
+ }
+ reader.endObject();
+ return al.toArray(new String[al.size()][]);
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/presets/PipelineInterface.java b/src/com/android/gallery3d/filtershow/presets/PipelineInterface.java
new file mode 100644
index 000000000..05f0a1aa8
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/presets/PipelineInterface.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.presets;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.support.v8.renderscript.Allocation;
+import android.support.v8.renderscript.RenderScript;
+
+public interface PipelineInterface {
+ public String getName();
+ public Resources getResources();
+ public Allocation getInPixelsAllocation();
+ public Allocation getOutPixelsAllocation();
+ public boolean prepareRenderscriptAllocations(Bitmap bitmap);
+ public RenderScript getRSContext();
+}
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
index c5851c476..b5de6929a 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
@@ -206,6 +206,8 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
uri = insertContent(context, sourceUri, this.destinationFile, saveFileName,
time);
}
+ XmpPresets.writeFilterXMP(context, sourceUri, this.destinationFile, preset);
+
noBitmap = false;
} catch (java.lang.OutOfMemoryError e) {
// Try 5 times before failing for good.
@@ -219,6 +221,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
return uri;
}
+
@Override
protected void onPostExecute(Uri result) {
if (callback != null) {
diff --git a/src/com/android/gallery3d/filtershow/tools/XmpPresets.java b/src/com/android/gallery3d/filtershow/tools/XmpPresets.java
new file mode 100644
index 000000000..be75f0253
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/tools/XmpPresets.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.filtershow.tools;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.adobe.xmp.XMPException;
+import com.adobe.xmp.XMPMeta;
+import com.adobe.xmp.XMPMetaFactory;
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.util.XmpUtilHelper;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public class XmpPresets {
+ public static final String
+ XMP_GOOGLE_FILTER_NAMESPACE = "http://ns.google.com/photos/1.0/filter/";
+ public static final String XMP_GOOGLE_FILTER_PREFIX = "AFltr";
+ public static final String XMP_SRC_FILE_URI = "SourceFileUri";
+ public static final String XMP_FILTERSTACK = "filterstack";
+ private static final String LOGTAG = "XmpPresets";
+
+ public static class XMresults {
+ public String presetString;
+ public ImagePreset preset;
+ public Uri originalimage;
+ }
+
+ static {
+ try {
+ XMPMetaFactory.getSchemaRegistry().registerNamespace(
+ XMP_GOOGLE_FILTER_NAMESPACE, XMP_GOOGLE_FILTER_PREFIX);
+ } catch (XMPException e) {
+ Log.e(LOGTAG, "Register XMP name space failed", e);
+ }
+ }
+
+ public static void writeFilterXMP(
+ Context context, Uri srcUri, File dstFile, ImagePreset preset) {
+ InputStream is = null;
+ XMPMeta xmpMeta = null;
+ try {
+ is = context.getContentResolver().openInputStream(srcUri);
+ xmpMeta = XmpUtilHelper.extractXMPMeta(is);
+ } catch (FileNotFoundException e) {
+
+ } finally {
+ Utils.closeSilently(is);
+ }
+
+ if (xmpMeta == null) {
+ xmpMeta = XMPMetaFactory.create();
+ }
+ try {
+ xmpMeta.setProperty(XMP_GOOGLE_FILTER_NAMESPACE,
+ XMP_SRC_FILE_URI, srcUri.toString());
+ xmpMeta.setProperty(XMP_GOOGLE_FILTER_NAMESPACE,
+ XMP_FILTERSTACK, preset.getJsonString(context.getString(R.string.saved)));
+ } catch (XMPException e) {
+ Log.v(LOGTAG, "Write XMP meta to file failed:" + dstFile.getAbsolutePath());
+ return;
+ }
+
+ if (!XmpUtilHelper.writeXMPMeta(dstFile.getAbsolutePath(), xmpMeta)) {
+ Log.v(LOGTAG, "Write XMP meta to file failed:" + dstFile.getAbsolutePath());
+ }
+ }
+
+ public static XMresults extractXMPData(
+ Context context, MasterImage mMasterImage, Uri uriToEdit) {
+ XMresults ret = new XMresults();
+
+ InputStream is = null;
+ XMPMeta xmpMeta = null;
+ try {
+ is = context.getContentResolver().openInputStream(uriToEdit);
+ xmpMeta = XmpUtilHelper.extractXMPMeta(is);
+ } catch (FileNotFoundException e) {
+ } finally {
+ Utils.closeSilently(is);
+ }
+
+ if (xmpMeta == null) {
+ return null;
+ }
+
+ try {
+ String strSrcUri = xmpMeta.getPropertyString(XMP_GOOGLE_FILTER_NAMESPACE,
+ XMP_SRC_FILE_URI);
+
+ if (strSrcUri != null) {
+ String filterString = xmpMeta.getPropertyString(XMP_GOOGLE_FILTER_NAMESPACE,
+ XMP_FILTERSTACK);
+
+ Uri srcUri = Uri.parse(strSrcUri);
+ ret.originalimage = srcUri;
+
+ ret.preset = new ImagePreset(mMasterImage.getPreset());
+ ret.presetString = filterString;
+ boolean ok = ret.preset.readJsonFromString(filterString);
+ if (!ok) {
+ return null;
+ }
+ return ret;
+ }
+ } catch (XMPException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+}
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index e5e77beb4..f5d3dc397 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -190,7 +190,7 @@ public class MenuExecutor {
setMenuItemVisible(menu, R.id.action_setas, supportSetAs);
setMenuItemVisible(menu, R.id.action_show_on_map, supportShowOnMap);
setMenuItemVisible(menu, R.id.action_edit, supportEdit);
- // setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit);
+ setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit);
setMenuItemVisible(menu, R.id.action_details, supportInfo);
}
diff --git a/src/com/android/gallery3d/util/SaveVideoFileUtils.java b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
index c281dd3e7..e2c5f51b9 100644
--- a/src/com/android/gallery3d/util/SaveVideoFileUtils.java
+++ b/src/com/android/gallery3d/util/SaveVideoFileUtils.java
@@ -19,6 +19,7 @@ package com.android.gallery3d.util;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore.Video;
@@ -95,7 +96,7 @@ public class SaveVideoFileUtils {
ContentResolver contentResolver, Uri uri ) {
long nowInMs = System.currentTimeMillis();
long nowInSec = nowInMs / 1000;
- final ContentValues values = new ContentValues(12);
+ final ContentValues values = new ContentValues(13);
values.put(Video.Media.TITLE, mDstFileInfo.mFileName);
values.put(Video.Media.DISPLAY_NAME, mDstFileInfo.mFile.getName());
values.put(Video.Media.MIME_TYPE, "video/mp4");
@@ -104,6 +105,8 @@ public class SaveVideoFileUtils {
values.put(Video.Media.DATE_ADDED, nowInSec);
values.put(Video.Media.DATA, mDstFileInfo.mFile.getAbsolutePath());
values.put(Video.Media.SIZE, mDstFileInfo.mFile.length());
+ int durationMs = retriveVideoDurationMs(mDstFileInfo.mFile.getPath());
+ values.put(Video.Media.DURATION, durationMs);
// Copy the data taken and location info from src.
String[] projection = new String[] {
VideoColumns.DATE_TAKEN,
@@ -138,4 +141,18 @@ public class SaveVideoFileUtils {
return contentResolver.insert(Video.Media.EXTERNAL_CONTENT_URI, values);
}
+ public static int retriveVideoDurationMs(String path) {
+ int durationMs = 0;
+ // Calculate the duration of the destination file.
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(path);
+ String duration = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_DURATION);
+ if (duration != null) {
+ durationMs = Integer.parseInt(duration);
+ }
+ retriever.release();
+ return durationMs;
+ }
+
}
diff --git a/src/com/android/photos/data/BitmapDecoder.java b/src/com/android/photos/data/BitmapDecoder.java
index a0ab4105a..f19808d06 100644
--- a/src/com/android/photos/data/BitmapDecoder.java
+++ b/src/com/android/photos/data/BitmapDecoder.java
@@ -41,7 +41,7 @@ public class BitmapDecoder {
private static final String TAG = BitmapDecoder.class.getSimpleName();
private static final int POOL_SIZE = 4;
private static final int TEMP_STORAGE_SIZE_BYTES = 16 * 1024;
- private static final int HEADER_MAX_SIZE = 16 * 1024;
+ private static final int HEADER_MAX_SIZE = 128 * 1024;
private static final Pool<BitmapFactory.Options> sOptions =
new SynchronizedPool<BitmapFactory.Options>(POOL_SIZE);