Merge "Add initial implementation of MediaCache." into gb-ub-photos-bryce
diff --git a/src/com/android/camera/CameraManager.java b/src/com/android/camera/CameraManager.java
index 854e105..65b2bb8 100644
--- a/src/com/android/camera/CameraManager.java
+++ b/src/com/android/camera/CameraManager.java
@@ -34,22 +34,24 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.view.SurfaceHolder;
import android.util.Log;
+import android.view.SurfaceHolder;
import com.android.gallery3d.common.ApiHelper;
import java.io.IOException;
+import java.util.concurrent.SynchronousQueue;
public class CameraManager {
private static final String TAG = "CameraManager";
private static CameraManager sCameraManager = new CameraManager();
- // Thread progress signals
- private ConditionVariable mSig = new ConditionVariable();
-
private Parameters mParameters;
- private IOException mReconnectException;
+ private boolean mParametersIsDirty;
+ private SynchronousQueue<Parameters> mParametersQueue =
+ new SynchronousQueue<Parameters>();
+ private SynchronousQueue<IOExceptionHolder> mReconnectExceptionQueue =
+ new SynchronousQueue<IOExceptionHolder>();
private static final int RELEASE = 1;
private static final int RECONNECT = 2;
@@ -72,15 +74,21 @@
private static final int SET_PARAMETERS = 19;
private static final int GET_PARAMETERS = 20;
private static final int SET_PARAMETERS_ASYNC = 21;
- private static final int WAIT_FOR_IDLE = 22;
- private static final int SET_PREVIEW_DISPLAY_ASYNC = 23;
- private static final int SET_PREVIEW_CALLBACK = 24;
- private static final int ENABLE_SHUTTER_SOUND = 25;
+ private static final int SET_PREVIEW_DISPLAY_ASYNC = 22;
+ private static final int SET_PREVIEW_CALLBACK = 23;
+ private static final int ENABLE_SHUTTER_SOUND = 24;
private Handler mCameraHandler;
private CameraProxy mCameraProxy;
private android.hardware.Camera mCamera;
+ // This holder is used when we need to pass the exception
+ // back to the calling thread. SynchornousQueue doesn't
+ // allow we to pass a null object thus a holder is needed.
+ private class IOExceptionHolder {
+ public IOException ex;
+ }
+
public static CameraManager instance() {
return sCameraManager;
}
@@ -115,7 +123,7 @@
private void setPreviewTexture(Object surfaceTexture) {
try {
mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
- } catch(IOException e) {
+ } catch (IOException e) {
throw new RuntimeException(e);
}
}
@@ -137,114 +145,119 @@
mCamera.release();
mCamera = null;
mCameraProxy = null;
- break;
+ return;
case RECONNECT:
- mReconnectException = null;
+ IOExceptionHolder holder = new IOExceptionHolder();
+ holder.ex = null;
try {
mCamera.reconnect();
} catch (IOException ex) {
- mReconnectException = ex;
+ holder.ex = ex;
}
- break;
+ try {
+ mReconnectExceptionQueue.put(holder);
+ } catch (InterruptedException ex) {
+ }
+ return;
case UNLOCK:
mCamera.unlock();
- break;
+ return;
case LOCK:
mCamera.lock();
- break;
+ return;
case SET_PREVIEW_TEXTURE_ASYNC:
setPreviewTexture(msg.obj);
- return; // no need to call mSig.open()
+ return;
case SET_PREVIEW_DISPLAY_ASYNC:
try {
mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
- } catch(IOException e) {
+ } catch (IOException e) {
throw new RuntimeException(e);
}
- return; // no need to call mSig.open()
+ return;
case START_PREVIEW_ASYNC:
mCamera.startPreview();
- return; // no need to call mSig.open()
+ return;
case STOP_PREVIEW:
mCamera.stopPreview();
- break;
+ return;
case SET_PREVIEW_CALLBACK_WITH_BUFFER:
mCamera.setPreviewCallbackWithBuffer(
(PreviewCallback) msg.obj);
- break;
+ return;
case ADD_CALLBACK_BUFFER:
mCamera.addCallbackBuffer((byte[]) msg.obj);
- break;
+ return;
case AUTO_FOCUS:
mCamera.autoFocus((AutoFocusCallback) msg.obj);
- break;
+ return;
case CANCEL_AUTO_FOCUS:
mCamera.cancelAutoFocus();
- break;
+ return;
case SET_AUTO_FOCUS_MOVE_CALLBACK:
setAutoFocusMoveCallback(mCamera, msg.obj);
- break;
+ return;
case SET_DISPLAY_ORIENTATION:
mCamera.setDisplayOrientation(msg.arg1);
- break;
+ return;
case SET_ZOOM_CHANGE_LISTENER:
mCamera.setZoomChangeListener(
(OnZoomChangeListener) msg.obj);
- break;
+ return;
case SET_FACE_DETECTION_LISTENER:
setFaceDetectionListener((FaceDetectionListener) msg.obj);
- break;
+ return;
case START_FACE_DETECTION:
startFaceDetection();
- break;
+ return;
case STOP_FACE_DETECTION:
stopFaceDetection();
- break;
+ return;
case SET_ERROR_CALLBACK:
mCamera.setErrorCallback((ErrorCallback) msg.obj);
- break;
+ return;
case SET_PARAMETERS:
mCamera.setParameters((Parameters) msg.obj);
- break;
+ return;
case GET_PARAMETERS:
- mParameters = mCamera.getParameters();
- break;
+ try {
+ mParametersQueue.put(mCamera.getParameters());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return;
case SET_PARAMETERS_ASYNC:
mCamera.setParameters((Parameters) msg.obj);
- return; // no need to call mSig.open()
+ return;
case SET_PREVIEW_CALLBACK:
mCamera.setPreviewCallback((PreviewCallback) msg.obj);
- break;
+ return;
case ENABLE_SHUTTER_SOUND:
enableShutterSound((msg.arg1 == 1) ? true : false);
- break;
-
- case WAIT_FOR_IDLE:
- // do nothing
- break;
+ return;
default:
throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
@@ -261,7 +274,6 @@
}
throw e;
}
- mSig.open();
}
}
@@ -283,6 +295,7 @@
mCamera = android.hardware.Camera.open(cameraId);
if (mCamera != null) {
mCameraProxy = new CameraProxy();
+ mParametersIsDirty = true;
return mCameraProxy;
} else {
return null;
@@ -290,6 +303,15 @@
}
public class CameraProxy {
+ private ConditionVariable mWaitDoneLock = new ConditionVariable();
+ private Runnable mUnlockRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mWaitDoneLock.open();
+ }
+ };
+
+
private CameraProxy() {
Assert(mCamera != null);
}
@@ -299,30 +321,30 @@
}
public void release() {
- mSig.close();
+ // release() must be synchronous so we know exactly when the camera
+ // is released and can continue on.
mCameraHandler.sendEmptyMessage(RELEASE);
- mSig.block();
+ waitDone();
}
public void reconnect() throws IOException {
- mSig.close();
mCameraHandler.sendEmptyMessage(RECONNECT);
- mSig.block();
- if (mReconnectException != null) {
- throw mReconnectException;
+ IOExceptionHolder holder = null;
+ try {
+ holder = mReconnectExceptionQueue.take();
+ } catch (InterruptedException ex) {
+ }
+ if (holder == null || holder.ex != null) {
+ throw holder.ex;
}
}
public void unlock() {
- mSig.close();
mCameraHandler.sendEmptyMessage(UNLOCK);
- mSig.block();
}
public void lock() {
- mSig.close();
mCameraHandler.sendEmptyMessage(LOCK);
- mSig.block();
}
@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
@@ -339,66 +361,49 @@
}
public void stopPreview() {
- mSig.close();
mCameraHandler.sendEmptyMessage(STOP_PREVIEW);
- mSig.block();
}
public void setPreviewCallback(final PreviewCallback cb) {
- mSig.close();
mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK, cb).sendToTarget();
- mSig.block();
}
public void setPreviewCallbackWithBuffer(final PreviewCallback cb) {
- mSig.close();
mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK_WITH_BUFFER, cb).sendToTarget();
- mSig.block();
}
public void addCallbackBuffer(byte[] callbackBuffer) {
- mSig.close();
mCameraHandler.obtainMessage(ADD_CALLBACK_BUFFER, callbackBuffer).sendToTarget();
- mSig.block();
}
public void autoFocus(AutoFocusCallback cb) {
- mSig.close();
mCameraHandler.obtainMessage(AUTO_FOCUS, cb).sendToTarget();
- mSig.block();
}
public void cancelAutoFocus() {
- mSig.close();
+ mCameraHandler.removeMessages(AUTO_FOCUS);
mCameraHandler.sendEmptyMessage(CANCEL_AUTO_FOCUS);
- mSig.block();
}
@TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) {
- mSig.close();
mCameraHandler.obtainMessage(SET_AUTO_FOCUS_MOVE_CALLBACK, cb).sendToTarget();
- mSig.block();
}
public void takePicture(final ShutterCallback shutter, final PictureCallback raw,
final PictureCallback postview, final PictureCallback jpeg) {
- mSig.close();
// Too many parameters, so use post for simplicity
mCameraHandler.post(new Runnable() {
@Override
public void run() {
mCamera.takePicture(shutter, raw, postview, jpeg);
- mSig.open();
}
});
- mSig.block();
}
public void takePicture2(final ShutterCallback shutter, final PictureCallback raw,
final PictureCallback postview, final PictureCallback jpeg,
final int cameraState, final int focusState) {
- mSig.close();
// Too many parameters, so use post for simplicity
mCameraHandler.post(new Runnable() {
@Override
@@ -410,81 +415,69 @@
+ ", focusState:" + focusState);
throw e;
}
- mSig.open();
}
});
- mSig.block();
}
public void setDisplayOrientation(int degrees) {
- mSig.close();
mCameraHandler.obtainMessage(SET_DISPLAY_ORIENTATION, degrees, 0)
.sendToTarget();
- mSig.block();
}
public void setZoomChangeListener(OnZoomChangeListener listener) {
- mSig.close();
mCameraHandler.obtainMessage(SET_ZOOM_CHANGE_LISTENER, listener).sendToTarget();
- mSig.block();
}
@TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setFaceDetectionListener(FaceDetectionListener listener) {
- mSig.close();
mCameraHandler.obtainMessage(SET_FACE_DETECTION_LISTENER, listener).sendToTarget();
- mSig.block();
}
public void startFaceDetection() {
- mSig.close();
mCameraHandler.sendEmptyMessage(START_FACE_DETECTION);
- mSig.block();
}
public void stopFaceDetection() {
- mSig.close();
mCameraHandler.sendEmptyMessage(STOP_FACE_DETECTION);
- mSig.block();
}
public void setErrorCallback(ErrorCallback cb) {
- mSig.close();
mCameraHandler.obtainMessage(SET_ERROR_CALLBACK, cb).sendToTarget();
- mSig.block();
}
public void setParameters(Parameters params) {
- mSig.close();
+ // TODO: check if this synchronous version is necessary
+ mParametersIsDirty = true;
mCameraHandler.obtainMessage(SET_PARAMETERS, params).sendToTarget();
- mSig.block();
}
public void setParametersAsync(Parameters params) {
+ mParametersIsDirty = true;
mCameraHandler.removeMessages(SET_PARAMETERS_ASYNC);
mCameraHandler.obtainMessage(SET_PARAMETERS_ASYNC, params).sendToTarget();
}
public Parameters getParameters() {
- mSig.close();
- mCameraHandler.sendEmptyMessage(GET_PARAMETERS);
- mSig.block();
- Parameters parameters = mParameters;
- mParameters = null;
- return parameters;
+ if (mParametersIsDirty || mParameters == null) {
+ mCameraHandler.sendEmptyMessage(GET_PARAMETERS);
+ try {
+ mParameters = mParametersQueue.take();
+ mParametersIsDirty = false;
+ } catch (InterruptedException ex) {
+ }
+ }
+ return mParameters;
}
public void enableShutterSound(boolean enable) {
- mSig.close();
mCameraHandler.obtainMessage(
ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0).sendToTarget();
- mSig.block();
}
- public void waitForIdle() {
- mSig.close();
- mCameraHandler.sendEmptyMessage(WAIT_FOR_IDLE);
- mSig.block();
+ public void waitDone() {
+ mWaitDoneLock.close();
+ mCameraHandler.post(mUnlockRunnable);
+ mWaitDoneLock.block();
}
}
}
diff --git a/src/com/android/camera/Exif.java b/src/com/android/camera/Exif.java
index ee39d67..c6ec6af 100644
--- a/src/com/android/camera/Exif.java
+++ b/src/com/android/camera/Exif.java
@@ -20,32 +20,35 @@
import com.android.gallery3d.exif.ExifInterface;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
public class Exif {
private static final String TAG = "CameraExif";
- // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
- public static int getOrientation(byte[] jpeg) {
- if (jpeg == null) {
- return 0;
- }
-
+ public static ExifInterface getExif(byte[] jpegData) {
ExifInterface exif = new ExifInterface();
- InputStream is = new ByteArrayInputStream(jpeg);
try {
- exif.readExif(is);
- Integer val = exif.getTagIntValue(ExifInterface.TAG_ORIENTATION);
- if (val == null) {
- return 0;
- } else {
- return ExifInterface.getRotationForOrientationValue(val.shortValue());
- }
+ exif.readExif(jpegData);
} catch (IOException e) {
- Log.w(TAG, "Failed to read EXIF orientation", e);
- return 0;
+ Log.w(TAG, "Failed to read EXIF data", e);
}
+ return exif;
+ }
+
+ // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
+ public static int getOrientation(ExifInterface exif) {
+ Integer val = exif.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+ if (val == null) {
+ return 0;
+ } else {
+ return ExifInterface.getRotationForOrientationValue(val.shortValue());
+ }
+ }
+
+ public static int getOrientation(byte[] jpegData) {
+ if (jpegData == null) return 0;
+
+ ExifInterface exif = getExif(jpegData);
+ return getOrientation(exif);
}
}
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
index 48fb629..b1f47df 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -26,6 +26,8 @@
import android.os.IBinder;
import android.util.Log;
+import com.android.gallery3d.exif.ExifInterface;
+
/*
* Service for saving images in the background thread.
*/
@@ -77,14 +79,14 @@
// Runs in main thread
public void addImage(final byte[] data, String title, long date, Location loc,
- int width, int height, int orientation,
+ int width, int height, int orientation, ExifInterface exif,
OnMediaSavedListener l, ContentResolver resolver) {
if (isQueueFull()) {
Log.e(TAG, "Cannot add image when the queue is full");
return;
}
SaveTask t = new SaveTask(data, title, date, (loc == null) ? null : new Location(loc),
- width, height, orientation, resolver, l);
+ width, height, orientation, exif, resolver, l);
mTaskNumber++;
if (isQueueFull()) {
@@ -114,12 +116,13 @@
private Location loc;
private int width, height;
private int orientation;
+ private ExifInterface exif;
private ContentResolver resolver;
private OnMediaSavedListener listener;
public SaveTask(byte[] data, String title, long date, Location loc,
- int width, int height, int orientation, ContentResolver resolver,
- OnMediaSavedListener listener) {
+ int width, int height, int orientation, ExifInterface exif,
+ ContentResolver resolver, OnMediaSavedListener listener) {
this.data = data;
this.title = title;
this.date = date;
@@ -127,6 +130,7 @@
this.width = width;
this.height = height;
this.orientation = orientation;
+ this.exif = exif;
this.resolver = resolver;
this.listener = listener;
}
@@ -139,7 +143,7 @@
@Override
protected Uri doInBackground(Void... v) {
return Storage.addImage(
- resolver, title, date, loc, orientation, data, width, height);
+ resolver, title, date, loc, orientation, exif, data, width, height);
}
@Override
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 037e0fd..f4bd4ce 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -21,6 +21,7 @@
import android.app.AlertDialog;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
@@ -31,6 +32,10 @@
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.location.Location;
import android.media.CameraProfile;
import android.net.Uri;
@@ -56,6 +61,9 @@
import com.android.camera.ui.RotateTextToast;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.exif.ExifInterface;
+import com.android.gallery3d.exif.ExifTag;
+import com.android.gallery3d.exif.Rational;
import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.util.UsageStatistics;
@@ -77,7 +85,8 @@
CameraPreference.OnPreferenceChangedListener,
ShutterButton.OnShutterButtonListener,
MediaSaveService.Listener,
- OnCountDownFinishedListener {
+ OnCountDownFinishedListener,
+ SensorEventListener {
private static final String TAG = "CAM_PhotoModule";
@@ -164,6 +173,13 @@
}
};
+ private Runnable mFlashRunnable = new Runnable() {
+ @Override
+ public void run() {
+ animateFlash();
+ }
+ };
+
private final StringBuilder mBuilder = new StringBuilder();
private final Formatter mFormatter = new Formatter(mBuilder);
private final Object[] mFormatterArgs = new Object[1];
@@ -196,7 +212,6 @@
private LocationManager mLocationManager;
- private final ShutterCallback mShutterCallback = new ShutterCallback();
private final PostViewPictureCallback mPostViewPictureCallback =
new PostViewPictureCallback();
private final RawPictureCallback mRawPictureCallback =
@@ -239,6 +254,12 @@
CameraStartUpThread mCameraStartUpThread;
ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
+ private SensorManager mSensorManager;
+ private float[] mGData = new float[3];
+ private float[] mMData = new float[3];
+ private float[] mR = new float[16];
+ private int mHeading = -1;
+
private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
new MediaSaveService.OnMediaSavedListener() {
@Override
@@ -417,6 +438,8 @@
initializeControlByIntent();
mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
mLocationManager = new LocationManager(mActivity, mUI);
+ mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
+
}
private void initializeControlByIntent() {
@@ -704,11 +727,21 @@
private final class ShutterCallback
implements android.hardware.Camera.ShutterCallback {
+
+ private boolean mAnimateFlash;
+
+ public ShutterCallback(boolean animateFlash) {
+ mAnimateFlash = animateFlash;
+ }
+
@Override
public void onShutter() {
mShutterCallbackTime = System.currentTimeMillis();
mShutterLag = mShutterCallbackTime - mCaptureStartTime;
Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
+ if (mAnimateFlash) {
+ mActivity.runOnUiThread(mFlashRunnable);
+ }
}
}
@@ -792,7 +825,8 @@
if (!mIsImageCaptureIntent) {
// Calculate the width and the height of the jpeg.
Size s = mParameters.getPictureSize();
- int orientation = Exif.getOrientation(jpegData);
+ ExifInterface exif = Exif.getExif(jpegData);
+ int orientation = Exif.getOrientation(exif);
int width, height;
if ((mJpegRotation + orientation) % 180 == 0) {
width = s.width;
@@ -807,9 +841,20 @@
Log.e(TAG, "Unbalanced name/data pair");
} else {
if (date == -1) date = mCaptureStartTime;
+ if (mHeading >= 0) {
+ // heading direction has been updated by the sensor.
+ ExifTag directionRefTag = exif.buildTag(
+ ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
+ ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
+ ExifTag directionTag = exif.buildTag(
+ ExifInterface.TAG_GPS_IMG_DIRECTION,
+ new Rational(mHeading, 1));
+ exif.setTag(directionRefTag);
+ exif.setTag(directionTag);
+ }
mActivity.getMediaSaveService().addImage(
jpegData, title, date, mLocation, width, height,
- orientation, mOnMediaSavedListener, mContentResolver);
+ orientation, exif, mOnMediaSavedListener, mContentResolver);
}
} else {
mJpegImageData = jpegData;
@@ -959,13 +1004,10 @@
Util.setGpsParameters(mParameters, loc);
mCameraDevice.setParameters(mParameters);
- mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback,
- mPostViewPictureCallback, new JpegPictureCallback(loc),
- mCameraState, mFocusManager.getFocusState());
-
- if (!animateBefore) {
- animateFlash();
- }
+ mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
+ mRawPictureCallback, mPostViewPictureCallback,
+ new JpegPictureCallback(loc), mCameraState,
+ mFocusManager.getFocusState());
mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
@@ -1091,7 +1133,8 @@
Util.closeSilently(outputStream);
}
} else {
- int orientation = Exif.getOrientation(data);
+ ExifInterface exif = Exif.getExif(data);
+ int orientation = Exif.getOrientation(exif);
Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
bitmap = Util.rotate(bitmap, orientation);
mActivity.setResultEx(Activity.RESULT_OK,
@@ -1258,6 +1301,16 @@
PopupManager.getInstance(mActivity).notifyShowPopup(null);
UsageStatistics.onContentViewChanged(
UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
+
+ Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (gsensor != null) {
+ mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ if (msensor != null) {
+ mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
}
void waitCameraStartUpThread() {
@@ -1276,6 +1329,15 @@
@Override
public void onPauseBeforeSuper() {
mPaused = true;
+ Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (gsensor != null) {
+ mSensorManager.unregisterListener(this, gsensor);
+ }
+
+ Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ if (msensor != null) {
+ mSensorManager.unregisterListener(this, msensor);
+ }
}
@Override
@@ -1975,4 +2037,32 @@
s.setListener(this);
}
}
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ int type = event.sensor.getType();
+ float[] data;
+ if (type == Sensor.TYPE_ACCELEROMETER) {
+ data = mGData;
+ } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
+ data = mMData;
+ } else {
+ // we should not be here.
+ return;
+ }
+ for (int i = 0; i < 3 ; i++) {
+ data[i] = event.values[i];
+ }
+ float[] orientation = new float[3];
+ SensorManager.getRotationMatrix(mR, null, mGData, mMData);
+ SensorManager.getOrientation(mR, orientation);
+ mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
+ if (mHeading < 0) {
+ mHeading += 360;
+ }
+ }
}
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index 648fa7d..ba995ed 100644
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -30,6 +30,7 @@
import android.util.Log;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.exif.ExifInterface;
import java.io.File;
import java.io.FileOutputStream;
@@ -49,7 +50,7 @@
public static final long UNAVAILABLE = -1L;
public static final long PREPARING = -2L;
public static final long UNKNOWN_SIZE = -3L;
- public static final long LOW_STORAGE_THRESHOLD= 50000000;
+ public static final long LOW_STORAGE_THRESHOLD = 50000000;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static void setImageSize(ContentValues values, int width, int height) {
@@ -77,11 +78,19 @@
// Save the image and add it to media store.
public static Uri addImage(ContentResolver resolver, String title,
- long date, Location location, int orientation, byte[] jpeg,
- int width, int height) {
+ long date, Location location, int orientation, ExifInterface exif,
+ byte[] jpeg, int width, int height) {
// Save the image.
String path = generateFilepath(title);
- writeFile(path, jpeg);
+ if (exif != null) {
+ try {
+ exif.writeExif(jpeg, path);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to write data", e);
+ }
+ } else {
+ writeFile(path, jpeg);
+ }
return addImage(resolver, title, date, location, orientation,
jpeg.length, path, width, height);
}
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 998bbf7..1f31778 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -58,6 +58,7 @@
import com.android.camera.ui.RotateTextToast;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.util.AccessibilityUtils;
import com.android.gallery3d.util.UsageStatistics;
@@ -587,7 +588,7 @@
@Override
public void onShutterButtonFocus(boolean pressed) {
- // Do nothing (everything happens in onShutterButtonClick).
+ mUI.setShutterPressed(pressed);
}
private void readVideoPreferences() {
@@ -1122,6 +1123,7 @@
setupMediaRecorderPreviewDisplay();
// Unlock the camera object before passing it to media recorder.
mActivity.mCameraDevice.unlock();
+ mActivity.mCameraDevice.waitDone();
mMediaRecorder.setCamera(mActivity.mCameraDevice.getCamera());
if (!mCaptureTimeLapse) {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
@@ -1646,6 +1648,7 @@
releaseMediaRecorder();
if (!mPaused) {
mActivity.mCameraDevice.lock();
+ mActivity.mCameraDevice.waitDone();
if (ApiHelper.HAS_SURFACE_TEXTURE &&
!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {
stopPreview();
@@ -2213,11 +2216,12 @@
private void storeImage(final byte[] data, Location loc) {
long dateTaken = System.currentTimeMillis();
String title = Util.createJpegName(dateTaken);
- int orientation = Exif.getOrientation(data);
+ ExifInterface exif = Exif.getExif(data);
+ int orientation = Exif.getOrientation(exif);
Size s = mParameters.getPictureSize();
mActivity.getMediaSaveService().addImage(
- data, title, dateTaken, loc, s.width, s.height,
- orientation, mOnMediaSavedListener, mContentResolver);
+ data, title, dateTaken, loc, s.width, s.height, orientation,
+ exif, mOnMediaSavedListener, mContentResolver);
}
private boolean resetEffect() {
diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java
index 63c1909..0eac49d 100644
--- a/src/com/android/camera/VideoUI.java
+++ b/src/com/android/camera/VideoUI.java
@@ -320,6 +320,12 @@
return false;
}
+ // disable preview gestures after shutter is pressed
+ public void setShutterPressed(boolean pressed) {
+ if (mGestures == null) return;
+ mGestures.setEnabled(!pressed);
+ }
+
public void enableShutter(boolean enable) {
if (mShutterButton != null) {
mShutterButton.setEnabled(enable);
diff --git a/src/com/android/camera/ui/CameraSwitcher.java b/src/com/android/camera/ui/CameraSwitcher.java
index e9551ad..b046ff7 100644
--- a/src/com/android/camera/ui/CameraSwitcher.java
+++ b/src/com/android/camera/ui/CameraSwitcher.java
@@ -313,6 +313,8 @@
// Verify that we weren't canceled
if (showsPopup()) {
setVisibility(View.INVISIBLE);
+ // request layout to make sure popup is laid out correctly on ICS
+ mPopup.requestLayout();
}
}
};
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index a642839..409f1e3 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -50,6 +50,7 @@
import com.android.gallery3d.R;
import com.android.gallery3d.data.LocalAlbum;
+import com.android.gallery3d.filtershow.cache.CachingPipeline;
import com.android.gallery3d.filtershow.cache.FilteringPipeline;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.editors.BasicEditor;
@@ -68,7 +69,6 @@
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet;
-import com.android.gallery3d.filtershow.imageshow.ImageZoom;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.provider.SharedImageProvider;
@@ -136,8 +136,8 @@
clearGalleryBitmapPool();
+ CachingPipeline.createRenderscriptContext(this);
setupMasterImage();
- ImageFilterRS.createRenderscriptContext(this);
setDefaultValues();
fillEditors();
@@ -356,7 +356,6 @@
ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen));
// TODO: get those values from XML.
- ImageZoom.setZoomedSize(getPixelsFromDip(256));
FramedTextButton.setTextSize((int) getPixelsFromDip(14));
FramedTextButton.setTrianglePadding((int) getPixelsFromDip(4));
FramedTextButton.setTriangleSize((int) getPixelsFromDip(10));
@@ -528,7 +527,7 @@
FiltersManager.getPreviewManager().freeRSFilterScripts();
FiltersManager.getManager().freeRSFilterScripts();
FiltersManager.reset();
- ImageFilterRS.destroyRenderScriptContext();
+ CachingPipeline.destroyRenderScriptContext();
super.onDestroy();
}
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index 8352032..6b20fe1 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -151,7 +151,7 @@
private final View mView;
private final LinearLayout mAccessoryViewList;
private Vector<View> mAccessoryViews = new Vector<View>();
- private final TextView mTextView;
+ private final Button mTextView;
private boolean mSelected = false;
private String mEffectName = null;
private int mParameterValue = 0;
@@ -160,10 +160,9 @@
public UtilityPanel(Context context, View utilityPanel) {
mView = utilityPanel;
View accessoryViewList = mView.findViewById(R.id.panelAccessoryViewList);
- Button textView = (Button) mView.findViewById(R.id.applyEffect);
+ mTextView = (Button) mView.findViewById(R.id.applyEffect);
mContext = context;
mAccessoryViewList = (LinearLayout) accessoryViewList;
- mTextView = (TextView) textView;
}
public boolean selected() {
@@ -212,6 +211,16 @@
public View getEditControl() {
return mView.findViewById(R.id.controlArea);
}
+
+ public void removeControlChildren() {
+ LinearLayout controlArea = (LinearLayout) mView.findViewById(R.id.controlArea);
+ controlArea.removeAllViews();
+ }
+
+ public Button getEditTitle() {
+ return mTextView;
+ }
+
public void updateText() {
String s;
if (mCurrentEditor == null) {
@@ -589,8 +598,10 @@
if (mEditorPlaceHolder.contains(representation.getEditorId())) {
mCurrentEditor = mEditorPlaceHolder.showEditor(
representation.getEditorId());
- mCurrentEditor.setUtilityPanelUI(
- mUtilityPanel.getActionControl(), mUtilityPanel.getEditControl());
+ mUtilityPanel.removeControlChildren();
+ mCurrentEditor.setUpEditorUI(
+ mUtilityPanel.getActionControl(), mUtilityPanel.getEditControl(),
+ mUtilityPanel.getEditTitle());
mCurrentImage = mCurrentEditor.getImageShow();
mCurrentEditor.setPanelController(this);
diff --git a/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java b/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
index 1190ea4..bc1d450 100644
--- a/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
+++ b/src/com/android/gallery3d/filtershow/cache/CachingPipeline.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.cache;
+import android.app.Activity;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.RenderScript;
@@ -24,6 +26,7 @@
import com.android.gallery3d.filtershow.filters.ImageFilterRS;
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;
public class CachingPipeline {
@@ -32,10 +35,15 @@
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
+ private static volatile RenderScript sRS = null;
+ private static volatile Resources sResources = null;
+
private FiltersManager mFiltersManager = null;
private volatile Bitmap mOriginalBitmap = null;
private volatile Bitmap mResizedOriginalBitmap = null;
+ private FilterEnvironment mEnvironment = new FilterEnvironment();
+
private volatile Allocation mOriginalAllocation = null;
private volatile Allocation mFiltersOnlyOriginalAllocation = null;
@@ -53,24 +61,60 @@
mName = name;
}
- public synchronized void reset() {
- mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
- if (mResizedOriginalBitmap != null) {
- mResizedOriginalBitmap.recycle();
- mResizedOriginalBitmap = null;
- }
- if (mOriginalAllocation != null) {
- mOriginalAllocation.destroy();
- mOriginalAllocation = null;
- }
- if (mFiltersOnlyOriginalAllocation != null) {
- mFiltersOnlyOriginalAllocation.destroy();
- mFiltersOnlyOriginalAllocation = null;
- }
- mPreviousGeometry = null;
- mPreviewScaleFactor = 1.0f;
+ public static synchronized Resources getResources() {
+ return sResources;
+ }
- destroyPixelAllocations();
+ 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");
+ destroyRenderScriptContext();
+ }
+ sRS = RenderScript.create(context);
+ sResources = context.getResources();
+ }
+
+ public static synchronized void destroyRenderScriptContext() {
+ sRS.destroy();
+ sRS = null;
+ sResources = null;
+ }
+
+ public synchronized void reset() {
+ synchronized (CachingPipeline.class) {
+ if (getRenderScriptContext() == null) {
+ return;
+ }
+ mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
+ if (mResizedOriginalBitmap != null) {
+ mResizedOriginalBitmap.recycle();
+ mResizedOriginalBitmap = null;
+ }
+ if (mOriginalAllocation != null) {
+ mOriginalAllocation.destroy();
+ mOriginalAllocation = null;
+ }
+ if (mFiltersOnlyOriginalAllocation != null) {
+ mFiltersOnlyOriginalAllocation.destroy();
+ mFiltersOnlyOriginalAllocation = null;
+ }
+ mPreviousGeometry = null;
+ mPreviewScaleFactor = 1.0f;
+
+ destroyPixelAllocations();
+ }
}
private synchronized void destroyPixelAllocations() {
@@ -108,18 +152,19 @@
return "UNKNOWN TYPE!";
}
- private void setPresetParameters(ImagePreset preset) {
- preset.setScaleFactor(mPreviewScaleFactor);
- preset.setQuality(ImagePreset.QUALITY_PREVIEW);
- preset.setupEnvironment(mFiltersManager);
- preset.getEnvironment().setCachingPipeline(this);
+ private void setupEnvironment(ImagePreset preset) {
+ mEnvironment.setCachingPipeline(this);
+ mEnvironment.setFiltersManager(mFiltersManager);
+ mEnvironment.setScaleFactor(mPreviewScaleFactor);
+ mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
+ mEnvironment.setImagePreset(preset);
}
public void setOriginal(Bitmap bitmap) {
mOriginalBitmap = bitmap;
Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
ImagePreset preset = MasterImage.getImage().getPreset();
- preset.setupEnvironment(mFiltersManager);
+ setupEnvironment(preset);
updateOriginalAllocation(preset);
}
@@ -139,7 +184,7 @@
Log.v(LOGTAG, "geometry has changed");
}
- RenderScript RS = ImageFilterRS.getRenderScriptContext();
+ RenderScript RS = getRenderScriptContext();
Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
@@ -149,7 +194,7 @@
}
Allocation originalAllocation = mOriginalAllocation;
- mResizedOriginalBitmap = preset.applyGeometry(originalBitmap);
+ mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
if (originalAllocation != null) {
@@ -161,109 +206,138 @@
}
public synchronized void render(RenderingRequest request) {
- if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
- && request.getBitmap() == null)
- || request.getImagePreset() == null) {
- return;
- }
-
- if (DEBUG) {
- Log.v(LOGTAG, "render image of type " + getType(request));
- }
-
- Bitmap bitmap = request.getBitmap();
- ImagePreset preset = request.getImagePreset();
- setPresetParameters(preset);
- mFiltersManager.freeFilterResources(preset);
-
- if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
- ImageLoader loader = MasterImage.getImage().getImageLoader();
- if (loader == null) {
- Log.w(LOGTAG, "loader not yet setup, cannot handle: " + getType(request));
+ synchronized (CachingPipeline.class) {
+ if (getRenderScriptContext() == null) {
return;
}
- bitmap = loader.getScaleOneImageForPreset(null, preset,
- request.getBounds(), request.getDestination(), false);
- if (bitmap == null) {
- Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
+ if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
+ && request.getBitmap() == null)
+ || request.getImagePreset() == null) {
return;
}
- }
- if (request.getType() == RenderingRequest.FULL_RENDERING
- || request.getType() == RenderingRequest.GEOMETRY_RENDERING
- || request.getType() == RenderingRequest.FILTERS_RENDERING) {
- updateOriginalAllocation(preset);
- }
+ if (DEBUG) {
+ Log.v(LOGTAG, "render image of type " + getType(request));
+ }
- if (DEBUG) {
- Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
- +" ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
- + mResizedOriginalBitmap.getHeight());
- }
-
- if (request.getType() == RenderingRequest.FULL_RENDERING
- || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
- mOriginalAllocation.copyTo(bitmap);
- } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
- mFiltersOnlyOriginalAllocation.copyTo(bitmap);
- }
-
- if (request.getType() == RenderingRequest.FULL_RENDERING
- || request.getType() == RenderingRequest.FILTERS_RENDERING
- || request.getType() == RenderingRequest.ICON_RENDERING
- || request.getType() == RenderingRequest.PARTIAL_RENDERING) {
- Bitmap bmp = preset.apply(bitmap);
- request.setBitmap(bmp);
+ Bitmap bitmap = request.getBitmap();
+ ImagePreset preset = request.getImagePreset();
+ setupEnvironment(preset);
mFiltersManager.freeFilterResources(preset);
- }
+ if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+ ImageLoader loader = MasterImage.getImage().getImageLoader();
+ if (loader == null) {
+ Log.w(LOGTAG, "loader not yet setup, cannot handle: " + getType(request));
+ return;
+ }
+ bitmap = loader.getScaleOneImageForPreset(request.getBounds(),
+ request.getDestination());
+ if (bitmap == null) {
+ Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
+ return;
+ }
+ }
+
+ if (request.getType() == RenderingRequest.FULL_RENDERING
+ || request.getType() == RenderingRequest.GEOMETRY_RENDERING
+ || request.getType() == RenderingRequest.FILTERS_RENDERING) {
+ updateOriginalAllocation(preset);
+ }
+
+ if (DEBUG) {
+ Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
+ + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
+ + mResizedOriginalBitmap.getHeight());
+ }
+
+ if (request.getType() == RenderingRequest.FULL_RENDERING
+ || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
+ mOriginalAllocation.copyTo(bitmap);
+ } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
+ mFiltersOnlyOriginalAllocation.copyTo(bitmap);
+ }
+
+ if (request.getType() == RenderingRequest.FULL_RENDERING
+ || request.getType() == RenderingRequest.FILTERS_RENDERING
+ || request.getType() == RenderingRequest.ICON_RENDERING
+ || request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+ Bitmap bmp = preset.apply(bitmap, mEnvironment);
+ request.setBitmap(bmp);
+ mFiltersManager.freeFilterResources(preset);
+ }
+ }
}
public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
- setPresetParameters(preset);
- mFiltersManager.freeFilterResources(preset);
- bitmap = preset.applyGeometry(bitmap);
- bitmap = preset.apply(bitmap);
- return bitmap;
+ synchronized (CachingPipeline.class) {
+ if (getRenderScriptContext() == null) {
+ return bitmap;
+ }
+ setupEnvironment(preset);
+ mEnvironment.setQuality(ImagePreset.QUALITY_FINAL);
+ mEnvironment.setScaleFactor(1.0f);
+ mFiltersManager.freeFilterResources(preset);
+ bitmap = preset.applyGeometry(bitmap, mEnvironment);
+ bitmap = preset.apply(bitmap, mEnvironment);
+ return bitmap;
+ }
+ }
+
+ public synchronized Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
+ synchronized (CachingPipeline.class) {
+ if (getRenderScriptContext() == null) {
+ return bitmap;
+ }
+ setupEnvironment(preset);
+ mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
+ mFiltersManager.freeFilterResources(preset);
+ bitmap = preset.applyGeometry(bitmap, mEnvironment);
+ return bitmap;
+ }
}
public synchronized void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) {
- if (DEBUG) {
- Log.v(LOGTAG, "compute preset " + preset);
- preset.showFilters();
- }
+ synchronized (CachingPipeline.class) {
+ if (getRenderScriptContext() == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.v(LOGTAG, "compute preset " + preset);
+ preset.showFilters();
+ }
- String thread = Thread.currentThread().getName();
- long time = System.currentTimeMillis();
- setPresetParameters(preset);
- mFiltersManager.freeFilterResources(preset);
+ String thread = Thread.currentThread().getName();
+ long time = System.currentTimeMillis();
+ setupEnvironment(preset);
+ mFiltersManager.freeFilterResources(preset);
- Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
- if (updateOriginalAllocation(preset)) {
- resizedOriginalBitmap = mResizedOriginalBitmap;
- buffer.updateBitmaps(resizedOriginalBitmap);
- }
- Bitmap bitmap = buffer.getProducer();
- long time2 = System.currentTimeMillis();
+ Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
+ if (updateOriginalAllocation(preset)) {
+ resizedOriginalBitmap = mResizedOriginalBitmap;
+ buffer.updateBitmaps(resizedOriginalBitmap);
+ }
+ Bitmap bitmap = buffer.getProducer();
+ long time2 = System.currentTimeMillis();
- if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
- || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
- buffer.updateBitmaps(resizedOriginalBitmap);
- bitmap = buffer.getProducer();
- }
- mOriginalAllocation.copyTo(bitmap);
+ if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
+ || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
+ buffer.updateBitmaps(resizedOriginalBitmap);
+ bitmap = buffer.getProducer();
+ }
+ mOriginalAllocation.copyTo(bitmap);
- bitmap = preset.apply(bitmap);
+ bitmap = preset.apply(bitmap, mEnvironment);
- mFiltersManager.freeFilterResources(preset);
+ mFiltersManager.freeFilterResources(preset);
- time = System.currentTimeMillis() - time;
- time2 = System.currentTimeMillis() - time2;
- if (DEBUG) {
- Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
- + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
- + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
+ time = System.currentTimeMillis() - time;
+ time2 = System.currentTimeMillis() - time2;
+ if (DEBUG) {
+ Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
+ + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
+ + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
+ }
}
}
@@ -278,11 +352,11 @@
}
public synchronized boolean isInitialized() {
- return mOriginalBitmap != null;
+ return getRenderScriptContext() != null && mOriginalBitmap != null;
}
public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
- RenderScript RS = ImageFilterRS.getRenderScriptContext();
+ RenderScript RS = getRenderScriptContext();
boolean needsUpdate = false;
if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
@@ -321,4 +395,5 @@
public String getName() {
return mName;
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index ef3e3d5..a6a0bcf 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -40,6 +40,7 @@
import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.HistoryAdapter;
+import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.tools.BitmapTask;
@@ -358,51 +359,28 @@
}
};
- // FIXME: this currently does the loading + filtering on the UI thread --
- // need to move this to a background thread.
- public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
- Rect destination, boolean force) {
+ public Bitmap getScaleOneImageForPreset(Rect bounds, Rect destination) {
mLoadingLock.lock();
- Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
- if (force || bmp == null) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inMutable = true;
- if (destination != null) {
- if (bounds.width() > destination.width()) {
- int sampleSize = 1;
- int w = bounds.width();
- while (w > destination.width()) {
- sampleSize *= 2;
- w /= sampleSize;
- }
- options.inSampleSize = sampleSize;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inMutable = true;
+ if (destination != null) {
+ if (bounds.width() > destination.width()) {
+ int sampleSize = 1;
+ int w = bounds.width();
+ while (w > destination.width()) {
+ sampleSize *= 2;
+ w /= sampleSize;
}
- }
- bmp = loadRegionBitmap(mUri, options, bounds);
- if (destination != null) {
- mLoadingLock.unlock();
- return bmp;
- }
- if (bmp != null) {
- float scaleFactor = imagePreset.getScaleFactor();
- float scale = (float) bmp.getWidth() / (float) getOriginalBounds().width();
- imagePreset.setScaleFactor(scale);
- imagePreset.setupEnvironment();
- bmp = imagePreset.apply(bmp);
- imagePreset.setScaleFactor(scaleFactor);
- mZoomCache.setImage(imagePreset, bounds, bmp);
- mLoadingLock.unlock();
- return bmp;
+ options.inSampleSize = sampleSize;
}
}
+ Bitmap bmp = loadRegionBitmap(mUri, options, bounds);
mLoadingLock.unlock();
return bmp;
}
public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
File destination) {
- preset.setQuality(ImagePreset.QUALITY_FINAL);
- preset.setScaleFactor(1.0f);
new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() {
@Override
@@ -510,9 +488,6 @@
public void returnFilteredResult(ImagePreset preset,
final FilterShowActivity filterShowActivity) {
- preset.setQuality(ImagePreset.QUALITY_FINAL);
- preset.setScaleFactor(1.0f);
-
BitmapTask.Callbacks<ImagePreset> cb = new BitmapTask.Callbacks<ImagePreset>() {
@Override
@@ -545,9 +520,9 @@
Log.w(LOGTAG, "Failed to save image!");
return null;
}
- param.setupEnvironment();
- bitmap = param.applyGeometry(bitmap);
- bitmap = param.apply(bitmap);
+ CachingPipeline pipeline = new CachingPipeline(
+ FiltersManager.getManager(), "Saving");
+ bitmap = pipeline.renderFinalImage(bitmap, param);
noBitmap = false;
} catch (java.lang.OutOfMemoryError e) {
// Try 5 times before failing for good.
diff --git a/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java
index 3cc61f9..138abb0 100644
--- a/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java
+++ b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java
@@ -19,7 +19,9 @@
import android.graphics.Bitmap;
import android.graphics.Rect;
import com.android.gallery3d.app.Log;
+import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.presets.FilterEnvironment;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public class RenderingRequest {
@@ -54,8 +56,9 @@
if (type == FULL_RENDERING
|| type == GEOMETRY_RENDERING
|| type == ICON_RENDERING) {
- preset.setupEnvironment();
- bitmap = preset.applyGeometry(source);
+ CachingPipeline pipeline = new CachingPipeline(
+ FiltersManager.getManager(), "Icon");
+ bitmap = pipeline.renderGeometryIcon(source, preset);
} else if (type != PARTIAL_RENDERING) {
bitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), mConfig);
}
diff --git a/src/com/android/gallery3d/filtershow/controller/ActionSlider.java b/src/com/android/gallery3d/filtershow/controller/ActionSlider.java
new file mode 100644
index 0000000..6ed2467
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/ActionSlider.java
@@ -0,0 +1,56 @@
+/*
+ * 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.controller;
+
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.editors.Editor;
+
+public class ActionSlider extends TitledSlider {
+ private static final String LOGTAG = "ActionSlider";
+ ImageButton mActionButton;
+ public ActionSlider() {
+ mLayoutID = R.layout.filtershow_control_action_slider;
+ }
+
+ @Override
+ public void setUp(ViewGroup container, Parameter parameter, Editor editor) {
+ super.setUp(container, parameter, editor);
+ mActionButton = (ImageButton) mTopView.findViewById(R.id.actionButton);
+ mActionButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ ((ParameterActionAndInt) mParameter).fireAction();
+ }
+ });
+ }
+
+ @Override
+ public void updateUI() {
+ super.updateUI();
+ if (mActionButton != null) {
+ int iconId = ((ParameterActionAndInt) mParameter).getActionIcon();
+ mActionButton.setImageResource(iconId);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/BasicSlider.java b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java
new file mode 100644
index 0000000..c54fe77
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java
@@ -0,0 +1,87 @@
+/*
+ * 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.controller;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.editors.Editor;
+
+public class BasicSlider implements Control {
+ private SeekBar mSeekBar;
+ private ParameterInteger mParameter;
+ Editor mEditor;
+
+ @Override
+ public void setUp(ViewGroup container, Parameter parameter, Editor editor) {
+ mEditor = editor;
+ Context context = container.getContext();
+ mParameter = (ParameterInteger) parameter;
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout lp = (LinearLayout) inflater.inflate(
+ R.layout.filtershow_seekbar, container, true);
+ mSeekBar = (SeekBar) lp.findViewById(R.id.primarySeekBar);
+
+ updateUI();
+ mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mParameter != null) {
+ mParameter.setValue(progress + mParameter.getMinimum());
+ mEditor.commitLocalRepresentation();
+
+ }
+ }
+ });
+ }
+
+ @Override
+ public View getTopView() {
+ return mSeekBar;
+ }
+
+ @Override
+ public void setPrameter(Parameter parameter) {
+ mParameter = (ParameterInteger) parameter;
+ if (mSeekBar != null) {
+ updateUI();
+ }
+ }
+
+ @Override
+ public void updateUI() {
+ mSeekBar.setMax(mParameter.getMaximum() - mParameter.getMinimum());
+ mSeekBar.setProgress(mParameter.getValue() - mParameter.getMinimum());
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/Control.java b/src/com/android/gallery3d/filtershow/controller/Control.java
new file mode 100644
index 0000000..4342290
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/Control.java
@@ -0,0 +1,32 @@
+/*
+ * 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.controller;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.gallery3d.filtershow.editors.Editor;
+
+public interface Control {
+ public void setUp(ViewGroup container, Parameter parameter, Editor editor);
+
+ public View getTopView();
+
+ public void setPrameter(Parameter parameter);
+
+ public void updateUI();
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/Parameter.java b/src/com/android/gallery3d/filtershow/controller/Parameter.java
new file mode 100644
index 0000000..1e8694a
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/Parameter.java
@@ -0,0 +1,27 @@
+/*
+ * 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.controller;
+
+public interface Parameter {
+ String getParameterName();
+
+ String getParameterType();
+
+ String getValueString();
+
+ public void setController(Control c);
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/ParameterActionAndInt.java b/src/com/android/gallery3d/filtershow/controller/ParameterActionAndInt.java
new file mode 100644
index 0000000..04567e2
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/ParameterActionAndInt.java
@@ -0,0 +1,25 @@
+/*
+ * 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.controller;
+
+public interface ParameterActionAndInt extends ParameterInteger {
+ static String sParameterType = "ParameterActionAndInt";
+
+ public void fireAction();
+
+ public int getActionIcon();
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/ParameterInteger.java b/src/com/android/gallery3d/filtershow/controller/ParameterInteger.java
new file mode 100644
index 0000000..0bfd201
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/ParameterInteger.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.controller;
+
+public interface ParameterInteger extends Parameter {
+ static String sParameterType = "ParameterInteger";
+
+ int getMaximum();
+
+ int getMinimum();
+
+ int getDefaultValue();
+
+ int getValue();
+
+ void setValue(int value);
+}
diff --git a/src/com/android/gallery3d/filtershow/controller/TitledSlider.java b/src/com/android/gallery3d/filtershow/controller/TitledSlider.java
new file mode 100644
index 0000000..30b6fdb
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/controller/TitledSlider.java
@@ -0,0 +1,105 @@
+/*
+ * 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.controller;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.editors.Editor;
+
+public class TitledSlider implements Control {
+ private final String LOGTAG = "ParametricEditor";
+ private SeekBar mSeekBar;
+ private TextView mControlName;
+ private TextView mControlValue;
+ protected ParameterInteger mParameter;
+ Editor mEditor;
+ View mTopView;
+ protected int mLayoutID = R.layout.filtershow_control_title_slider;
+
+ @Override
+ public void setUp(ViewGroup container, Parameter parameter, Editor editor) {
+ mEditor = editor;
+ Context context = container.getContext();
+ mParameter = (ParameterInteger) parameter;
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mTopView = inflater.inflate(mLayoutID, container, true);
+ mTopView.setVisibility(View.VISIBLE);
+ mSeekBar = (SeekBar) mTopView.findViewById(R.id.controlValueSeekBar);
+ mControlName = (TextView) mTopView.findViewById(R.id.controlName);
+ mControlValue = (TextView) mTopView.findViewById(R.id.controlValue);
+ updateUI();
+ mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mParameter != null) {
+ mParameter.setValue(progress + mParameter.getMinimum());
+ if (mControlName != null) {
+ mControlName.setText(mParameter.getParameterName());
+ }
+ if (mControlValue != null) {
+ mControlValue.setText(Integer.toString(mParameter.getValue()));
+ }
+ mEditor.commitLocalRepresentation();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setPrameter(Parameter parameter) {
+ mParameter = (ParameterInteger) parameter;
+ if (mSeekBar != null)
+ updateUI();
+ }
+
+ @Override
+ public void updateUI() {
+ if (mControlName != null) {
+ mControlName.setText(mParameter.getParameterName());
+ }
+ if (mControlValue != null) {
+ mControlValue.setText(
+ Integer.toString(mParameter.getValue()));
+ }
+ mSeekBar.setMax(mParameter.getMaximum() - mParameter.getMinimum());
+ mSeekBar.setProgress(mParameter.getValue() - mParameter.getMinimum());
+ mEditor.commitLocalRepresentation();
+ }
+
+ @Override
+ public View getTopView() {
+ return mTopView;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/editors/Editor.java b/src/com/android/gallery3d/filtershow/editors/Editor.java
index 69266ff..013590c 100644
--- a/src/com/android/gallery3d/filtershow/editors/Editor.java
+++ b/src/com/android/gallery3d/filtershow/editors/Editor.java
@@ -17,18 +17,22 @@
package com.android.gallery3d.filtershow.editors;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.text.Html;
+import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.PopupMenu;
+import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.PanelController;
import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.controller.Control;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
@@ -43,6 +47,7 @@
protected ImageShow mImageShow;
protected FrameLayout mFrameLayout;
protected SeekBar mSeekBar;
+ Button mEditTitle;
protected PanelController mPanelController;
protected int mID;
private final String LOGTAG = "Editor";
@@ -57,7 +62,7 @@
}
public String calculateUserMessage(Context context, String effectName, Object parameterValue) {
- String apply = context.getString(R.string.apply_effect);
+ String apply = "";
if (mShowParameter == SHOW_VALUE_INT) {
apply += " " + effectName + " " + parameterValue;
} else {
@@ -83,6 +88,12 @@
return true;
}
+ public void setUpEditorUI(View actionButton, View editControl, Button editTitle) {
+ this.mEditTitle = editTitle;
+ setMenuIcon(true);
+ setUtilityPanelUI(actionButton, editControl);
+ }
+
public boolean showsPopupIndicator() {
return true;
}
@@ -92,17 +103,28 @@
* @param editControl this is the black area for sliders etc
*/
public void setUtilityPanelUI(View actionButton, View editControl) {
- mSeekBar = (SeekBar) editControl.findViewById(R.id.primarySeekBar);
+
+ AttributeSet aset;
+ Context context = editControl.getContext();
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout lp = (LinearLayout) inflater.inflate(
+ R.layout.filtershow_seekbar, (ViewGroup) editControl, true);
+ mSeekBar = (SeekBar) lp.findViewById(R.id.primarySeekBar);
+ mSeekBar.setOnSeekBarChangeListener(this);
+
if (showsSeekBar()) {
mSeekBar.setOnSeekBarChangeListener(this);
mSeekBar.setVisibility(View.VISIBLE);
} else {
mSeekBar.setVisibility(View.INVISIBLE);
}
+
Button button = (Button) actionButton.findViewById(R.id.applyEffect);
if (button != null) {
if (showsPopupIndicator()) {
- button.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.filtershow_menu_marker, 0);
+ button.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0,
+ R.drawable.filtershow_menu_marker, 0);
} else {
button.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
}
@@ -205,19 +227,29 @@
}
public void openUtilityPanel(LinearLayout mAccessoryViewList) {
+ setMenuIcon(false);
if (mImageShow != null) {
mImageShow.openUtilityPanel(mAccessoryViewList);
}
}
+ protected void setMenuIcon(boolean on) {
+ mEditTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ 0, 0, on ? R.drawable.filtershow_menu_marker : 0, 0);
+ }
protected void createMenu(int[] strId, View button) {
PopupMenu pmenu = new PopupMenu(mContext, button);
Menu menu = pmenu.getMenu();
for (int i = 0; i < strId.length; i++) {
menu.add(Menu.NONE, Menu.FIRST + i, 0, mContext.getString(strId[i]));
}
+ setMenuIcon(true);
+
}
+ public Control[] getControls() {
+ return null;
+ }
@Override
public void onStartTrackingTouch(SeekBar arg0) {
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
index a7d99e4..7127b21 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
@@ -24,7 +24,7 @@
import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation;
import com.android.gallery3d.filtershow.imageshow.ImageVignette;
-public class EditorVignette extends BasicEditor {
+public class EditorVignette extends ParametricEditor {
public static final int ID = R.id.vignetteEditor;
private static final String LOGTAG = "EditorVignettePlanet";
ImageVignette mImageVignette;
diff --git a/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java b/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java
new file mode 100644
index 0000000..cf00f4a
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java
@@ -0,0 +1,197 @@
+/*
+ * 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.editors;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.controller.ActionSlider;
+import com.android.gallery3d.filtershow.controller.BasicSlider;
+import com.android.gallery3d.filtershow.controller.Control;
+import com.android.gallery3d.filtershow.controller.Parameter;
+import com.android.gallery3d.filtershow.controller.ParameterActionAndInt;
+import com.android.gallery3d.filtershow.controller.ParameterInteger;
+import com.android.gallery3d.filtershow.controller.TitledSlider;
+import com.android.gallery3d.filtershow.filters.FilterBasicRepresentation;
+import com.android.gallery3d.filtershow.filters.FilterRepresentation;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+public class ParametricEditor extends Editor {
+ private int mLayoutID;
+ private int mViewID;
+ public static int ID = R.id.editorParametric;
+ private final String LOGTAG = "ParametricEditor";
+ protected Control mControl;
+ public static final int MINIMUM_WIDTH = 600;
+ public static final int MINIMUM_HEIGHT = 800;
+
+ static HashMap<String, Class> portraitMap = new HashMap<String, Class>();
+ static HashMap<String, Class> landscapeMap = new HashMap<String, Class>();
+ static {
+ portraitMap.put(ParameterInteger.sParameterType, BasicSlider.class);
+ landscapeMap.put(ParameterInteger.sParameterType, TitledSlider.class);
+ portraitMap.put(ParameterActionAndInt.sParameterType, ActionSlider.class);
+ landscapeMap.put(ParameterActionAndInt.sParameterType, ActionSlider.class);
+ }
+
+ static Constructor getConstructor(Class cl) {
+ try {
+ return cl.getConstructor(Context.class, ViewGroup.class);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public ParametricEditor() {
+ super(ID);
+ }
+
+ protected ParametricEditor(int id) {
+ super(id);
+ }
+
+ protected ParametricEditor(int id, int layoutID, int viewID) {
+ super(id);
+ mLayoutID = layoutID;
+ mViewID = viewID;
+ }
+
+ @Override
+ public String calculateUserMessage(Context context, String effectName, Object parameterValue) {
+ String apply = "";
+
+ if (mShowParameter == SHOW_VALUE_INT & useCompact(context)) {
+ if (getLocalRepresentation() instanceof FilterBasicRepresentation) {
+ FilterBasicRepresentation interval = (FilterBasicRepresentation) getLocalRepresentation();
+ apply += " " + effectName + " " + interval.getStateRepresentation();
+ } else {
+ apply += " " + effectName + " " + parameterValue;
+ }
+ } else {
+ apply += " " + effectName;
+ }
+ return apply;
+ }
+
+ @Override
+ public void createEditor(Context context, FrameLayout frameLayout) {
+ super.createEditor(context, frameLayout);
+ unpack(mViewID, mLayoutID);
+ }
+
+ @Override
+ public void reflectCurrentFilter() {
+ super.reflectCurrentFilter();
+ if (getLocalRepresentation() != null
+ && getLocalRepresentation() instanceof FilterBasicRepresentation) {
+ FilterBasicRepresentation interval = (FilterBasicRepresentation) getLocalRepresentation();
+ mControl.setPrameter(interval);
+ }
+ }
+
+ @Override
+ public Control[] getControls() {
+ BasicSlider slider = new BasicSlider();
+ return new Control[] {
+ slider
+ };
+ }
+
+ // TODO: need a better way to decide when which representation
+ static boolean useCompact(Context context) {
+ WindowManager w = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
+ Point size = new Point();
+ w.getDefaultDisplay().getSize(size);
+ if (size.x < size.y) { // if tall than wider
+ return true;
+ }
+ if (size.x < MINIMUM_WIDTH) {
+ return true;
+ }
+ if (size.y < MINIMUM_HEIGHT) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void setUtilityPanelUI(View actionButton, View editControl) {
+ FilterRepresentation rep = getLocalRepresentation();
+ if (this instanceof Parameter) {
+ control((Parameter) this, editControl);
+ } else if (rep instanceof Parameter) {
+ control((Parameter) rep, editControl);
+ } else {
+ mSeekBar = new SeekBar(editControl.getContext());
+ LayoutParams lp = new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ mSeekBar.setLayoutParams(lp);
+ ((LinearLayout) editControl).addView(mSeekBar);
+ mSeekBar.setOnSeekBarChangeListener(this);
+ }
+ }
+
+ protected void control(Parameter p, View editControl) {
+ String pType = p.getParameterType();
+ Context context = editControl.getContext();
+ Class c = ((useCompact(context)) ? portraitMap : landscapeMap).get(pType);
+
+ if (c != null) {
+ try {
+ mControl = (Control) c.newInstance();
+ mControl.setUp((ViewGroup) editControl, p, this);
+
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Error in loading Control ", e);
+ }
+ } else {
+ Log.e(LOGTAG, "Unable to find class for " + pType);
+ for (String string : portraitMap.keySet()) {
+ Log.e(LOGTAG, "for " + string + " use " + portraitMap.get(string));
+ }
+ }
+ }
+
+ @Override
+ public void commitLocalRepresentation() {
+ super.commitLocalRepresentation();
+ FilterRepresentation rep = getLocalRepresentation();
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar sbar, int progress, boolean arg2) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar arg0) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar arg0) {
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index d84c9f2..b5c4de0 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -65,7 +65,7 @@
if (preset == null) {
return;
}
- Vector<ImageFilter> usedFilters = preset.getUsedFilters();
+ Vector<ImageFilter> usedFilters = preset.getUsedFilters(this);
for (Class c : mFilters.keySet()) {
ImageFilter filter = mFilters.get(c);
if (!usedFilters.contains(filter)) {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
index 2410ebe..34323c4 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java
@@ -17,8 +17,10 @@
package com.android.gallery3d.filtershow.filters;
import com.android.gallery3d.app.Log;
+import com.android.gallery3d.filtershow.controller.Control;
+import com.android.gallery3d.filtershow.controller.ParameterInteger;
-public class FilterBasicRepresentation extends FilterRepresentation {
+public class FilterBasicRepresentation extends FilterRepresentation implements ParameterInteger {
private static final String LOGTAG = "FilterBasicRepresentation";
private int mMinimum;
private int mValue;
@@ -33,6 +35,7 @@
setValue(value);
}
+ @Override
public String toString() {
return getName() + " : " + mMinimum + " < " + mValue + " < " + mMaximum;
}
@@ -47,6 +50,7 @@
return representation;
}
+ @Override
public void useParametersFrom(FilterRepresentation a) {
if (a instanceof FilterBasicRepresentation) {
FilterBasicRepresentation representation = (FilterBasicRepresentation) a;
@@ -76,6 +80,7 @@
return false;
}
+ @Override
public int getMinimum() {
return mMinimum;
}
@@ -84,10 +89,12 @@
mMinimum = minimum;
}
+ @Override
public int getValue() {
return mValue;
}
+ @Override
public void setValue(int value) {
mValue = value;
if (mValue < mMinimum) {
@@ -98,6 +105,7 @@
}
}
+ @Override
public int getMaximum() {
return mMaximum;
}
@@ -110,6 +118,7 @@
mDefaultValue = defaultValue;
}
+ @Override
public int getDefaultValue() {
return mDefaultValue;
}
@@ -122,7 +131,27 @@
mPreviewValue = previewValue;
}
+ @Override
public String getStateRepresentation() {
return "" + getValue();
}
+
+ @Override
+ public String getParameterType(){
+ return sParameterType;
+ }
+
+ @Override
+ public void setController(Control control) {
+ }
+
+ @Override
+ public String getValueString() {
+ return getStateRepresentation();
+ }
+
+ @Override
+ public String getParameterName() {
+ return getName();
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
index 56e848a..2aeaed8 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
@@ -29,22 +29,21 @@
private static final String LOGTAG = "ImageFilterRS";
private boolean DEBUG = false;
- private static volatile RenderScript sRS = null;
- private static volatile Resources sResources = null;
private volatile boolean mResourcesLoaded = false;
- // This must be used inside block synchronized on ImageFilterRS class object
protected abstract void createFilter(android.content.res.Resources res,
float scaleFactor, int quality);
- // This must be used inside block synchronized on ImageFilterRS class object
protected abstract void runFilter();
- // This must be used inside block synchronized on ImageFilterRS class object
protected void update(Bitmap bitmap) {
getOutPixelsAllocation().copyTo(bitmap);
}
+ protected RenderScript getRenderScriptContext() {
+ return CachingPipeline.getRenderScriptContext();
+ }
+
protected Allocation getInPixelsAllocation() {
CachingPipeline pipeline = getEnvironment().getCachingPipeline();
return pipeline.getInPixelsAllocation();
@@ -61,27 +60,22 @@
return bitmap;
}
try {
- synchronized(ImageFilterRS.class) {
- if (sRS == null) {
- Log.w(LOGTAG, "Cannot apply before calling createRenderScriptContext");
- return bitmap;
- }
- CachingPipeline pipeline = getEnvironment().getCachingPipeline();
- if (DEBUG) {
- Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName());
- }
- boolean needsUpdate = pipeline.prepareRenderscriptAllocations(bitmap);
- if (needsUpdate || !isResourcesLoaded()) {
- // the allocations changed size
- freeResources();
- createFilter(sResources, scaleFactor, quality);
- setResourcesLoaded(true);
- }
- runFilter();
- update(bitmap);
- if (DEBUG) {
- Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName());
- }
+ CachingPipeline pipeline = getEnvironment().getCachingPipeline();
+ if (DEBUG) {
+ Log.v(LOGTAG, "apply filter " + getName() + " in pipeline " + pipeline.getName());
+ }
+ Resources rsc = pipeline.getResources();
+ if (pipeline.prepareRenderscriptAllocations(bitmap)
+ || !isResourcesLoaded()) {
+ freeResources();
+ createFilter(rsc, scaleFactor, quality);
+ setResourcesLoaded(true);
+ }
+ bindScriptValues();
+ runFilter();
+ update(bitmap);
+ if (DEBUG) {
+ Log.v(LOGTAG, "DONE apply filter " + getName() + " in pipeline " + pipeline.getName());
}
} catch (android.renderscript.RSIllegalArgumentException e) {
Log.e(LOGTAG, "Illegal argument? " + e);
@@ -96,53 +90,32 @@
return bitmap;
}
- public static synchronized RenderScript getRenderScriptContext() {
- return sRS;
- }
-
- public static synchronized void createRenderscriptContext(Activity context) {
- if( sRS != null) {
- Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
- destroyRenderScriptContext();
- }
- sRS = RenderScript.create(context);
- sResources = context.getResources();
- }
-
- public static synchronized void destroyRenderScriptContext() {
- sRS.destroy();
- sRS = null;
- sResources = null;
- }
-
- private static synchronized Allocation convertBitmap(Bitmap bitmap) {
- return Allocation.createFromBitmap(sRS, bitmap,
+ private static Allocation convertBitmap(Bitmap bitmap) {
+ return Allocation.createFromBitmap(CachingPipeline.getRenderScriptContext(), bitmap,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
}
- private static synchronized Allocation convertRGBAtoA(Bitmap bitmap) {
- Type.Builder tb_a8 = new Type.Builder(sRS, Element.A_8(sRS));
- ScriptC_grey greyConvert = new ScriptC_grey(sRS,
- sRS.getApplicationContext().getResources(), R.raw.grey);
+ private static Allocation convertRGBAtoA(Bitmap bitmap) {
+ RenderScript RS = CachingPipeline.getRenderScriptContext();
+ Type.Builder tb_a8 = new Type.Builder(RS, Element.A_8(RS));
+ ScriptC_grey greyConvert = new ScriptC_grey(RS,
+ RS.getApplicationContext().getResources(), R.raw.grey);
Allocation bitmapTemp = convertBitmap(bitmap);
- if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(sRS))) {
+ if (bitmapTemp.getType().getElement().isCompatible(Element.A_8(RS))) {
return bitmapTemp;
}
tb_a8.setX(bitmapTemp.getType().getX());
tb_a8.setY(bitmapTemp.getType().getY());
- Allocation bitmapAlloc = Allocation.createTyped(sRS, tb_a8.create());
+ Allocation bitmapAlloc = Allocation.createTyped(RS, tb_a8.create());
greyConvert.forEach_RGBAtoA(bitmapTemp, bitmapAlloc);
return bitmapAlloc;
}
public Allocation loadScaledResourceAlpha(int resource, int inSampleSize) {
- Resources res = null;
- synchronized(ImageFilterRS.class) {
- res = sRS.getApplicationContext().getResources();
- }
+ Resources res = CachingPipeline.getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ALPHA_8;
options.inSampleSize = inSampleSize;
@@ -159,10 +132,7 @@
}
public Allocation loadResource(int resource) {
- Resources res = null;
- synchronized(ImageFilterRS.class) {
- res = sRS.getApplicationContext().getResources();
- }
+ Resources res = CachingPipeline.getResources();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeResource(
@@ -191,13 +161,16 @@
*/
abstract protected void resetScripts();
+ /**
+ * Scripts values should be bound here
+ */
+ abstract protected void bindScriptValues();
+
public void freeResources() {
if (!isResourcesLoaded()) {
return;
}
- synchronized(ImageFilterRS.class) {
- resetAllocations();
- setResourcesLoaded(false);
- }
+ resetAllocations();
+ setResourcesLoaded(false);
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
index f545cd9..76ae475 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
@@ -22,7 +22,6 @@
private static final String LOGTAG = "ImageFilterSharpen";
private ScriptC_convolve3x3 mScript;
- float mScaleFactor;
private FilterBasicRepresentation mParameters;
@@ -63,19 +62,14 @@
@Override
protected void createFilter(android.content.res.Resources res, float scaleFactor,
int quality) {
- int w = getInPixelsAllocation().getType().getX();
- int h = getInPixelsAllocation().getType().getY();
- mScaleFactor = scaleFactor;
-
if (mScript == null) {
mScript = new ScriptC_convolve3x3(getRenderScriptContext(), res, R.raw.convolve3x3);
}
- mScript.set_gWidth(w);
- mScript.set_gHeight(h);
}
private void computeKernel() {
- float p1 = mParameters.getValue() * mScaleFactor;
+ float scaleFactor = getEnvironment().getScaleFactor();
+ float p1 = mParameters.getValue() * scaleFactor;
float value = p1 / 100.0f;
float f[] = new float[9];
float p = value;
@@ -92,6 +86,14 @@
}
@Override
+ protected void bindScriptValues() {
+ int w = getInPixelsAllocation().getType().getX();
+ int h = getInPixelsAllocation().getType().getY();
+ mScript.set_gWidth(w);
+ mScript.set_gHeight(h);
+ }
+
+ @Override
protected void runFilter() {
if (mParameters == null) {
return;
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
index 1149263..7ce9e51 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
@@ -65,10 +65,6 @@
mActiveHandle = -1;
break;
case MotionEvent.ACTION_DOWN:
- if (event.getPointerCount() == 1) {
- Log.v(LOGTAG, "################### ACTION_DOWN odd " + mActiveHandle
- + " touches=1");
- }
break;
}
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java b/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java
deleted file mode 100644
index eb568c3..0000000
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.filtershow.imageshow;
-
-import android.content.Context;
-import android.graphics.*;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-
-import com.android.gallery3d.filtershow.cache.ImageLoader;
-
-public class ImageZoom extends ImageShow {
- private static final String LOGTAG = "ImageZoom";
- private boolean mTouchDown = false;
- private boolean mZoomedIn = false;
- private Rect mZoomBounds = null;
- private static float mMaxSize = 512;
-
- public ImageZoom(Context context) {
- super(context);
- }
-
- public ImageZoom(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public static void setZoomedSize(float size) {
- mMaxSize = size;
- }
-
- @Override
- public void resetParameter() {
- super.resetParameter();
- mZoomedIn = false;
- mTouchDown = false;
- }
-
- public void onTouchDown(float x, float y) {
- if (mZoomedIn || mTouchDown) {
- return;
- }
- mTouchDown = true;
- GeometryMetadata geo = getImagePreset().mGeoData;
- Matrix originalToScreen = geo.getOriginalToScreen(true,
- mImageLoader.getOriginalBounds().width(),
- mImageLoader.getOriginalBounds().height(),
- getWidth(), getHeight());
- float[] point = new float[2];
- point[0] = x;
- point[1] = y;
- Matrix inverse = new Matrix();
- originalToScreen.invert(inverse);
- inverse.mapPoints(point);
-
- float ratio = (float) getWidth() / (float) getHeight();
- float mh = mMaxSize;
- float mw = ratio * mh;
- Point touch = getTouchPoint();
- RectF zoomRect = new RectF(touch.x - mw, touch.y - mh, touch.x + mw, touch.y + mw);
- inverse.mapRect(zoomRect);
- zoomRect.set(zoomRect.centerX() - mw, zoomRect.centerY() - mh,
- zoomRect.centerX() + mw, zoomRect.centerY() + mh);
- mZoomBounds = new Rect((int) zoomRect.left, (int) zoomRect.top,
- (int) zoomRect.right, (int) zoomRect.bottom);
- invalidate();
- }
-
- public void onTouchUp() {
- mTouchDown = false;
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- drawBackground(canvas);
-
- Bitmap filteredImage = null;
- if ((mZoomedIn || mTouchDown) && mImageLoader != null) {
- filteredImage = mImageLoader.getScaleOneImageForPreset(this, getImagePreset(),
- mZoomBounds, null, false);
- } else {
- filteredImage = getFilteredImage();
- }
- canvas.save();
- if (mZoomedIn || mTouchDown) {
- int orientation = ImageLoader.getZoomOrientation();
- switch (orientation) {
- case ImageLoader.ORI_ROTATE_90: {
- canvas.rotate(90, getWidth() / 2, getHeight() / 2);
- break;
- }
- case ImageLoader.ORI_ROTATE_270: {
- canvas.rotate(270, getWidth() / 2, getHeight() / 2);
- break;
- }
- case ImageLoader.ORI_TRANSPOSE: {
- canvas.rotate(90, getWidth() / 2, getHeight() / 2);
- canvas.scale(1, -1);
- break;
- }
- case ImageLoader.ORI_TRANSVERSE: {
- canvas.rotate(270, getWidth() / 2, getHeight() / 2);
- canvas.scale(1, -1);
- break;
- }
- }
- }
- drawImage(canvas, filteredImage);
- canvas.restore();
-
- drawToast(canvas);
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent event) {
-
- if (!mZoomedIn) {
- onTouchDown(event.getX(), event.getY());
- } else {
- onTouchUp();
- }
- mZoomedIn = !mZoomedIn;
- invalidate();
- return false;
- }
-}
diff --git a/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java b/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
index c450360..b474b84 100644
--- a/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
+++ b/src/com/android/gallery3d/filtershow/presets/FilterEnvironment.java
@@ -77,4 +77,5 @@
public void setCachingPipeline(CachingPipeline cachingPipeline) {
mCachingPipeline = cachingPipeline;
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
index b75ac64..b2dd6b7 100644
--- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java
@@ -22,6 +22,7 @@
import com.android.gallery3d.filtershow.ImageStateAdapter;
import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.filters.BaseFiltersManager;
import com.android.gallery3d.filtershow.filters.FilterRepresentation;
import com.android.gallery3d.filtershow.filters.FiltersManager;
import com.android.gallery3d.filtershow.filters.ImageFilter;
@@ -35,11 +36,9 @@
private static final String LOGTAG = "ImagePreset";
private FilterRepresentation mBorder = null;
- private float mScaleFactor = 1.0f;
public static final int QUALITY_ICON = 0;
public static final int QUALITY_PREVIEW = 1;
public static final int QUALITY_FINAL = 2;
- private int mQuality = QUALITY_PREVIEW;
private ImageLoader mImageLoader = null;
private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
@@ -57,8 +56,6 @@
private Bitmap mPreviewImage;
- private FilterEnvironment mEnvironment = new FilterEnvironment();
-
public ImagePreset() {
setup();
}
@@ -412,42 +409,31 @@
// do nothing here
}
- public void setupEnvironment() {
- setupEnvironment(FiltersManager.getManager());
- }
-
- public void setupEnvironment(FiltersManager filtersManager) {
- getEnvironment().setImagePreset(this);
- getEnvironment().setScaleFactor(mScaleFactor);
- getEnvironment().setQuality(mQuality);
- getEnvironment().setFiltersManager(filtersManager);
- }
-
- public Bitmap apply(Bitmap original) {
+ public Bitmap apply(Bitmap original, FilterEnvironment environment) {
Bitmap bitmap = original;
- bitmap = applyFilters(bitmap, -1, -1);
- return applyBorder(bitmap);
+ bitmap = applyFilters(bitmap, -1, -1, environment);
+ return applyBorder(bitmap, environment);
}
- public Bitmap applyGeometry(Bitmap bitmap) {
+ public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
// Apply any transform -- 90 rotate, flip, straighten, crop
// Returns a new bitmap.
if (mDoApplyGeometry) {
mGeoData.synchronizeRepresentation();
- bitmap = mEnvironment.applyRepresentation(mGeoData, bitmap);
+ bitmap = environment.applyRepresentation(mGeoData, bitmap);
}
return bitmap;
}
- public Bitmap applyBorder(Bitmap bitmap) {
+ public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) {
if (mBorder != null && mDoApplyGeometry) {
mBorder.synchronizeRepresentation();
- bitmap = mEnvironment.applyRepresentation(mBorder, bitmap);
+ bitmap = environment.applyRepresentation(mBorder, bitmap);
}
return bitmap;
}
- public Bitmap applyFilters(Bitmap bitmap, int from, int to) {
+ public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) {
if (mDoApplyFilters) {
if (from < 0) {
from = 0;
@@ -461,7 +447,7 @@
representation = mFilters.elementAt(i);
representation.synchronizeRepresentation();
}
- bitmap = mEnvironment.applyRepresentation(representation, bitmap);
+ bitmap = environment.applyRepresentation(representation, bitmap);
}
}
@@ -500,22 +486,6 @@
imageStateAdapter.notifyDataSetChanged();
}
- public float getScaleFactor() {
- return mScaleFactor;
- }
-
- public int getQuality() {
- return mQuality;
- }
-
- public void setQuality(int value) {
- mQuality = value;
- }
-
- public void setScaleFactor(float value) {
- mScaleFactor = value;
- }
-
public void setPartialRendering(boolean partialRendering, Rect bounds) {
mPartialRendering = partialRendering;
mPartialRenderingBounds = bounds;
@@ -537,18 +507,14 @@
mPreviewImage = previewImage;
}
- public Vector<ImageFilter> getUsedFilters() {
+ public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) {
Vector<ImageFilter> usedFilters = new Vector<ImageFilter>();
for (int i = 0; i < mFilters.size(); i++) {
FilterRepresentation representation = mFilters.elementAt(i);
- FiltersManager filtersManager = getEnvironment().getFiltersManager();
ImageFilter filter = filtersManager.getFilterForRepresentation(representation);
usedFilters.add(filter);
}
return usedFilters;
}
- public FilterEnvironment getEnvironment() {
- return mEnvironment;
- }
}
diff --git a/src/com/android/photos/views/HeaderGridView.java b/src/com/android/photos/views/HeaderGridView.java
new file mode 100644
index 0000000..45a5eaf
--- /dev/null
+++ b/src/com/android/photos/views/HeaderGridView.java
@@ -0,0 +1,466 @@
+/*
+ * 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.photos.views;
+
+import android.content.Context;
+import android.database.DataSetObservable;
+import android.database.DataSetObserver;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.FrameLayout;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+import android.widget.WrapperListAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link GridView} that supports adding header rows in a
+ * very similar way to {@link ListView}.
+ * See {@link HeaderGridView#addHeaderView(View, Object, boolean)}
+ */
+public class HeaderGridView extends GridView {
+ private static final String TAG = "HeaderGridView";
+
+ /**
+ * A class that represents a fixed view in a list, for example a header at the top
+ * or a footer at the bottom.
+ */
+ private static class FixedViewInfo {
+ /** The view to add to the grid */
+ public View view;
+ public ViewGroup viewContainer;
+ /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
+ public Object data;
+ /** <code>true</code> if the fixed view should be selectable in the grid */
+ public boolean isSelectable;
+ }
+
+ private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
+
+ private void initHeaderGridView() {
+ super.setClipChildren(false);
+ }
+
+ public HeaderGridView(Context context) {
+ super(context);
+ initHeaderGridView();
+ }
+
+ public HeaderGridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initHeaderGridView();
+ }
+
+ public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initHeaderGridView();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
+ ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumns());
+ }
+ }
+
+ @Override
+ public void setClipChildren(boolean clipChildren) {
+ // Ignore, since the header rows depend on not being clipped
+ }
+
+ /**
+ * Add a fixed view to appear at the top of the grid. If addHeaderView is
+ * called more than once, the views will appear in the order they were
+ * added. Views added using this call can take focus if they want.
+ * <p>
+ * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
+ * the supplied cursor with one that will also account for header views.
+ *
+ * @param v The view to add.
+ * @param data Data to associate with this view
+ * @param isSelectable whether the item is selectable
+ */
+ public void addHeaderView(View v, Object data, boolean isSelectable) {
+ ListAdapter adapter = getAdapter();
+
+ if (adapter != null && ! (adapter instanceof HeaderViewGridAdapter)) {
+ throw new IllegalStateException(
+ "Cannot add header view to grid -- setAdapter has already been called.");
+ }
+
+ FixedViewInfo info = new FixedViewInfo();
+ FrameLayout fl = new FullWidthFixedViewLayout(getContext());
+ fl.addView(v);
+ info.view = v;
+ info.viewContainer = fl;
+ info.data = data;
+ info.isSelectable = isSelectable;
+ mHeaderViewInfos.add(info);
+
+ // in the case of re-adding a header view, or adding one later on,
+ // we need to notify the observer
+ if (adapter != null) {
+ ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Add a fixed view to appear at the top of the grid. If addHeaderView is
+ * called more than once, the views will appear in the order they were
+ * added. Views added using this call can take focus if they want.
+ * <p>
+ * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
+ * the supplied cursor with one that will also account for header views.
+ *
+ * @param v The view to add.
+ */
+ public void addHeaderView(View v) {
+ addHeaderView(v, null, true);
+ }
+
+ public int getHeaderViewCount() {
+ return mHeaderViewInfos.size();
+ }
+
+ /**
+ * Removes a previously-added header view.
+ *
+ * @param v The view to remove
+ * @return true if the view was removed, false if the view was not a header
+ * view
+ */
+ public boolean removeHeaderView(View v) {
+ if (mHeaderViewInfos.size() > 0) {
+ boolean result = false;
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {
+ result = true;
+ }
+ removeFixedViewInfo(v, mHeaderViewInfos);
+ return result;
+ }
+ return false;
+ }
+
+ private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
+ int len = where.size();
+ for (int i = 0; i < len; ++i) {
+ FixedViewInfo info = where.get(i);
+ if (info.view == v) {
+ where.remove(i);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ if (mHeaderViewInfos.size() > 0) {
+ HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter);
+ int numColumns = getNumColumns();
+ if (numColumns > 1) {
+ hadapter.setNumColumns(numColumns);
+ }
+ super.setAdapter(hadapter);
+ } else {
+ super.setAdapter(adapter);
+ }
+ }
+
+ private class FullWidthFixedViewLayout extends FrameLayout {
+ public FullWidthFixedViewLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int targetWidth = HeaderGridView.this.getMeasuredWidth()
+ - HeaderGridView.this.getPaddingLeft()
+ - HeaderGridView.this.getPaddingRight();
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
+ MeasureSpec.getMode(widthMeasureSpec));
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ /**
+ * ListAdapter used when a HeaderGridView has header views. This ListAdapter
+ * wraps another one and also keeps track of the header views and their
+ * associated data objects.
+ *<p>This is intended as a base class; you will probably not need to
+ * use this class directly in your own code.
+ */
+ private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {
+
+ // This is used to notify the container of updates relating to number of columns
+ // or headers changing, which changes the number of placeholders needed
+ private final DataSetObservable mDataSetObservable = new DataSetObservable();
+
+ private final ListAdapter mAdapter;
+ private int mNumColumns = 1;
+
+ // This ArrayList is assumed to NOT be null.
+ ArrayList<FixedViewInfo> mHeaderViewInfos;
+
+ boolean mAreAllFixedViewsSelectable;
+
+ private final boolean mIsFilterable;
+
+ public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ListAdapter adapter) {
+ mAdapter = adapter;
+ mIsFilterable = adapter instanceof Filterable;
+
+ if (headerViewInfos == null) {
+ throw new IllegalArgumentException("headerViewInfos cannot be null");
+ }
+ mHeaderViewInfos = headerViewInfos;
+
+ mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);
+ }
+
+ public int getHeadersCount() {
+ return mHeaderViewInfos.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0;
+ }
+
+ public void setNumColumns(int numColumns) {
+ if (numColumns < 1) {
+ throw new IllegalArgumentException("Number of columns must be 1 or more");
+ }
+ if (mNumColumns != numColumns) {
+ mNumColumns = numColumns;
+ notifyDataSetChanged();
+ }
+ }
+
+ private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {
+ if (infos != null) {
+ for (FixedViewInfo info : infos) {
+ if (!info.isSelectable) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean removeHeader(View v) {
+ for (int i = 0; i < mHeaderViewInfos.size(); i++) {
+ FixedViewInfo info = mHeaderViewInfos.get(i);
+ if (info.view == v) {
+ mHeaderViewInfos.remove(i);
+
+ mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);
+
+ mDataSetObservable.notifyChanged();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getCount() {
+ if (mAdapter != null) {
+ return getHeadersCount() * mNumColumns + mAdapter.getCount();
+ } else {
+ return getHeadersCount() * mNumColumns;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ if (mAdapter != null) {
+ return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders) {
+ return (position % mNumColumns == 0)
+ && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.isEnabled(adjPosition);
+ }
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders) {
+ if (position % mNumColumns == 0) {
+ return mHeaderViewInfos.get(position / mNumColumns).data;
+ }
+ return null;
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getItem(adjPosition);
+ }
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (mAdapter != null && position >= numHeadersAndPlaceholders) {
+ int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getItemId(adjPosition);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ if (mAdapter != null) {
+ return mAdapter.hasStableIds();
+ }
+ return false;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns ;
+ if (position < numHeadersAndPlaceholders) {
+ View headerViewContainer = mHeaderViewInfos
+ .get(position / mNumColumns).viewContainer;
+ if (position % mNumColumns == 0) {
+ return headerViewContainer;
+ } else {
+ if (convertView == null) {
+ convertView = new View(parent.getContext());
+ }
+ // We need to do this because GridView uses the height of the last item
+ // in a row to determine the height for the entire row.
+ convertView.setVisibility(View.INVISIBLE);
+ convertView.setMinimumHeight(headerViewContainer.getHeight());
+ return convertView;
+ }
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getView(adjPosition, convertView, parent);
+ }
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {
+ // Placeholders get the last view type number
+ return mAdapter != null ? mAdapter.getViewTypeCount() : 1;
+ }
+ if (mAdapter != null && position >= numHeadersAndPlaceholders) {
+ int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getItemViewType(adjPosition);
+ }
+ }
+
+ return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ if (mAdapter != null) {
+ return mAdapter.getViewTypeCount() + 1;
+ }
+ return 2;
+ }
+
+ @Override
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mDataSetObservable.registerObserver(observer);
+ if (mAdapter != null) {
+ mAdapter.registerDataSetObserver(observer);
+ }
+ }
+
+ @Override
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mDataSetObservable.unregisterObserver(observer);
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(observer);
+ }
+ }
+
+ @Override
+ public Filter getFilter() {
+ if (mIsFilterable) {
+ return ((Filterable) mAdapter).getFilter();
+ }
+ return null;
+ }
+
+ @Override
+ public ListAdapter getWrappedAdapter() {
+ return mAdapter;
+ }
+
+ public void notifyDataSetChanged() {
+ mDataSetObservable.notifyChanged();
+ }
+ }
+}