summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rs/YuvToRgb.rs6
-rw-r--r--rs/rotator.rs24
-rw-r--r--src/com/android/camera/CameraActivity.java2
-rw-r--r--src/com/android/camera/CameraSettings.java7
-rw-r--r--src/com/android/camera/CaptureModule.java111
-rw-r--r--src/com/android/camera/CaptureUI.java30
-rw-r--r--src/com/android/camera/ComboPreferences.java1
-rw-r--r--src/com/android/camera/SettingsManager.java36
-rw-r--r--src/com/android/camera/exif/ExifInterface.java17
-rw-r--r--src/com/android/camera/imageprocessor/FrameProcessor.java8
-rw-r--r--src/com/android/camera/imageprocessor/PostProcessor.java46
-rw-r--r--src/com/android/camera/imageprocessor/filter/OptizoomFilter.java2
-rw-r--r--src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java172
-rw-r--r--src/com/android/camera/mpo/MpoOutputStream.java98
14 files changed, 488 insertions, 72 deletions
diff --git a/rs/YuvToRgb.rs b/rs/YuvToRgb.rs
index 25771c5c7..57076e979 100644
--- a/rs/YuvToRgb.rs
+++ b/rs/YuvToRgb.rs
@@ -41,9 +41,9 @@ uchar4 __attribute__((kernel)) nv21ToRgb(uint32_t x, uint32_t y) {
int vV = (int)(rsGetElementAt_uchar(gIn, index) & 0xFF ) -128;
int uV = (int)(rsGetElementAt_uchar(gIn, index+1) & 0xFF ) -128;
- int r = (int) (1.164f * yV + 1.596f * vV );
- int g = (int) (1.164f * yV - 0.813f * vV - 0.391f * uV);
- int b = (int) (1.164f * yV + 2.018f * uV );
+ int r = (int) (yV + 1.370705f * vV );
+ int g = (int) (yV - 0.698001f * vV - 0.337633f* uV);
+ int b = (int) (yV + 1.732446 * uV );
r = r>255? 255 : r<0 ? 0 : r;
g = g>255? 255 : g<0 ? 0 : g;
diff --git a/rs/rotator.rs b/rs/rotator.rs
index cd9da4396..5a27e00f7 100644
--- a/rs/rotator.rs
+++ b/rs/rotator.rs
@@ -34,18 +34,34 @@ rs_allocation gOut;
rs_allocation gIn;
uint32_t width;
uint32_t height;
+uint32_t pad;
+bool gFlip;
uchar __attribute__((kernel)) rotate90andMerge(uint32_t x, uint32_t y) {
uchar yValue = rsGetElementAt_uchar(gIn, x + y*width);
- rsSetElementAt_uchar(gOut, yValue, x*height + height - 1 - y);
- if(x%2 == 0 && y%2==1) {
+ if(gFlip) {
+ if(x >= width - pad)
+ return (uchar)0;
+ rsSetElementAt_uchar(gOut, yValue, (width-1-x-pad)*height + height - 1 - y);
+ } else {
+ rsSetElementAt_uchar(gOut, yValue, x*height + height - 1 - y);
+ }
+
+ if(x%2 == 0 && y%2 == 0) {
uint32_t ySize = width*height;
uint32_t index = ySize + x + ((y/2) * width);
uchar vValue = rsGetElementAt_uchar(gIn, index);
uchar uValue = rsGetElementAt_uchar(gIn, index + 1);
- rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + height - 1 - y);
- rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + height - 1 - y - 1);
+ if(gFlip) {
+ if(x >= width - pad)
+ return (uchar)0;
+ rsSetElementAt_uchar(gOut, uValue, ySize + (width-2-x-pad)/2*height + height - 1 - y);
+ rsSetElementAt_uchar(gOut, vValue, ySize + (width-2-x-pad)/2*height + height - 1 - y - 1);
+ } else {
+ rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + height - 1 - y);
+ rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + height - 1 - y - 1);
+ }
}
return (uchar)0;
} \ No newline at end of file
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index f10423163..1b6031b53 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -708,7 +708,7 @@ public class CameraActivity extends Activity
public void updateThumbnail(final byte[] jpegData) {
if (mUpdateThumbnailTask != null) mUpdateThumbnailTask.cancel(true);
- mUpdateThumbnailTask = new UpdateThumbnailTask(jpegData, false);
+ mUpdateThumbnailTask = new UpdateThumbnailTask(jpegData, true);
mUpdateThumbnailTask.execute();
}
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index 7a1cf8d74..f700d2187 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -1058,7 +1058,7 @@ public class CameraSettings {
resetIfInvalid(pref);
}
- private void filterSimilarPictureSize(PreferenceGroup group,
+ public static void filterSimilarPictureSize(PreferenceGroup group,
ListPreference pref) {
pref.filterDuplicated();
if (pref.getEntries().length <= 1) {
@@ -1175,11 +1175,6 @@ public class CameraSettings {
return Integer.parseInt(pref.getString(KEY_CAMERA_ID, rearCameraId));
}
- public static int getInitialCameraId(SharedPreferences pref) {
- String value = pref.getString(SettingsManager.KEY_INITIAL_CAMERA, "0");
- return Integer.parseInt(value);
- }
-
public static void writePreferredCameraId(SharedPreferences pref,
int cameraId) {
Editor editor = pref.edit();
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 0f65f21c3..8ac48afcb 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -68,6 +68,7 @@ import com.android.camera.imageprocessor.PostProcessor;
import com.android.camera.imageprocessor.FrameProcessor;
import com.android.camera.PhotoModule.NamedImages;
import com.android.camera.PhotoModule.NamedImages.NamedEntity;
+import com.android.camera.imageprocessor.filter.SharpshooterFilter;
import com.android.camera.ui.CountDownView;
import com.android.camera.ui.ModuleSwitcher;
import com.android.camera.ui.RotateTextToast;
@@ -187,6 +188,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private SettingsManager mSettingsManager;
private long SECONDARY_SERVER_MEM;
private boolean mLongshotActive = false;
+ private CameraCharacteristics mMainCameraCharacteristics;
/**
* A {@link CameraCaptureSession } for camera preview.
@@ -205,6 +207,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private PostProcessor mPostProcessor;
private FrameProcessor mFrameProcessor;
private Size mFrameProcPreviewOutputSize;
+ private CaptureResult mPreviewCaptureResult;
private Face[] mPreviewFaces = null;
private Face[] mStickyFaces = null;
private Rect mBayerCameraRegion;
@@ -223,6 +226,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private boolean mFirstPreviewLoaded;
private int[] mPrecaptureRequestHashCode = new int[MAX_NUM_CAM];
private int[] mLockRequestHashCode = new int[MAX_NUM_CAM];
+ private final Handler mHandler = new MainHandler();
private class MediaSaveNotifyThread extends Thread {
private Uri uri;
@@ -323,6 +327,10 @@ public class CaptureModule implements CameraModule, PhotoController,
return mStickyFaces;
}
+ public CaptureResult getPreviewCaptureResult() {
+ return mPreviewCaptureResult;
+ }
+
public Rect getCameraRegion() {
return mBayerCameraRegion;
}
@@ -333,7 +341,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
- private void process(CaptureResult result) {
+ private void updateState(CaptureResult result) {
int id = (int) result.getRequest().getTag();
if (!mFirstPreviewLoaded) {
@@ -351,6 +359,7 @@ public class CaptureModule implements CameraModule, PhotoController,
if(faces != null && faces.length != 0) {
mStickyFaces = faces;
}
+ mPreviewCaptureResult = result;
switch (mState[id]) {
case STATE_PREVIEW: {
@@ -412,7 +421,6 @@ public class CaptureModule implements CameraModule, PhotoController,
CaptureResult partialResult) {
int id = (int) partialResult.getRequest().getTag();
if (id == getMainCameraId()) updateFocusStateChange(partialResult);
- process(partialResult);
}
@Override
@@ -421,7 +429,7 @@ public class CaptureModule implements CameraModule, PhotoController,
TotalCaptureResult result) {
int id = (int) result.getRequest().getTag();
if (id == getMainCameraId()) updateFocusStateChange(result);
- process(result);
+ updateState(result);
}
};
@@ -492,7 +500,7 @@ public class CaptureModule implements CameraModule, PhotoController,
else return false;
}
- private boolean isBackCamera() {
+ public boolean isBackCamera() {
String value = mSettingsManager.getValue(SettingsManager.KEY_CAMERA_ID);
if (value == null) return true;
if (Integer.parseInt(value) == BAYER_ID) return true;
@@ -556,6 +564,9 @@ public class CaptureModule implements CameraModule, PhotoController,
// are initialized.
if (s != null) {
s.setListener(this);
+ if (isClearSightOn()) {
+ ClearSightImageProcessor.getInstance().setMediaSaveService(s);
+ }
}
mNamedImages = new NamedImages();
@@ -569,6 +580,9 @@ public class CaptureModule implements CameraModule, PhotoController,
MediaSaveService s = mActivity.getMediaSaveService();
if (s != null) {
s.setListener(this);
+ if (isClearSightOn()) {
+ ClearSightImageProcessor.getInstance().setMediaSaveService(s);
+ }
}
mNamedImages = new NamedImages();
}
@@ -855,9 +869,7 @@ public class CaptureModule implements CameraModule, PhotoController,
captureBuilder = mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
}
- // Orientation
- int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, rotation));
+ captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, mOrientation));
captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
captureBuilder.addTarget(getPreviewSurface(id));
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode);
@@ -1027,6 +1039,11 @@ public class CaptureModule implements CameraModule, PhotoController,
mFrameProcPreviewOutputSize = sizeList.get(i);
}
}
+
+ public CameraCharacteristics getMainCameraCharacteristics() {
+ return mMainCameraCharacteristics;
+ }
+
/**
* Sets up member variables related to camera.
*
@@ -1047,6 +1064,7 @@ public class CaptureModule implements CameraModule, PhotoController,
if(i == getMainCameraId()) {
mBayerCameraRegion = characteristics.get(CameraCharacteristics
.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ mMainCameraCharacteristics = characteristics;
}
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
@@ -1069,9 +1087,11 @@ public class CaptureModule implements CameraModule, PhotoController,
mUI.setPreviewSize(mFrameProcPreviewOutputSize.getWidth(), mFrameProcPreviewOutputSize.getHeight());
}
if (isClearSightOn()) {
- ClearSightImageProcessor.getInstance().init(size.getWidth(), size.getHeight(),
- mActivity, mOnMediaSavedListener);
- ClearSightImageProcessor.getInstance().setCallback(this);
+ if(i == getMainCameraId()) {
+ ClearSightImageProcessor.getInstance().init(size.getWidth(), size.getHeight(),
+ mActivity, mOnMediaSavedListener);
+ ClearSightImageProcessor.getInstance().setCallback(this);
+ }
} else {
// No Clearsight
mImageReader[i] = ImageReader.newInstance(size.getWidth(), size.getHeight(), imageFormat, MAX_IMAGE_NUM);
@@ -1364,23 +1384,15 @@ public class CaptureModule implements CameraModule, PhotoController,
return filters;
}
- private int getPostProcFilterId() {
- String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
- if (scene != null) {
- int mode = Integer.parseInt(scene);
- if (mode == SettingsManager.SCENE_MODE_OPTIZOOM_INT)
- return PostProcessor.FILTER_OPTIZOOM;
+ private int getPostProcFilterId(int mode) {
+ if (mode == SettingsManager.SCENE_MODE_OPTIZOOM_INT) {
+ return PostProcessor.FILTER_OPTIZOOM;
+ } else if (mode == SettingsManager.SCENE_MODE_NIGHT_INT && SharpshooterFilter.isSupportedStatic()) {
+ return PostProcessor.FILTER_SHARPSHOOTER;
}
return PostProcessor.FILTER_NONE;
}
- private boolean isPostProcFilter(String value) {
- if(value.equalsIgnoreCase(SettingsManager.SCENE_MODE_OPTIZOOM_INT+"")) {
- return true;
- }
- return false;
- }
-
@Override
public void onResumeAfterSuper() {
Log.d(TAG, "onResume " + getCameraMode());
@@ -1388,12 +1400,19 @@ public class CaptureModule implements CameraModule, PhotoController,
mUI.setSwitcherIndex();
mCameraIdList = new ArrayList<>();
if(mPostProcessor != null) {
- Log.d(TAG, "Chosen postproc filter id : "+getPostProcFilterId());
- mPostProcessor.onOpen(getPostProcFilterId());
+ String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
+ if (scene != null) {
+ int mode = Integer.parseInt(scene);
+ Log.d(TAG, "Chosen postproc filter id : " + getPostProcFilterId(mode));
+ mPostProcessor.onOpen(getPostProcFilterId(mode));
+ } else {
+ mPostProcessor.onOpen(PostProcessor.FILTER_NONE);
+ }
}
if(mFrameProcessor != null) {
mFrameProcessor.onOpen(getFrameProcFilterId());
}
+
if(mPostProcessor.isFilterOn()) {
setUpCameraOutputs(ImageFormat.YUV_420_888);
} else {
@@ -1425,7 +1444,12 @@ public class CaptureModule implements CameraModule, PhotoController,
initializeSecondTime();
}
mUI.reInitUI();
- mActivity.updateStorageSpaceAndHint();
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.updateStorageSpaceAndHint();
+ }
+ });
estimateJpegFileSize();
mUI.enableShutter(true);
}
@@ -1575,7 +1599,7 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
- private int getMainCameraId() {
+ public int getMainCameraId() {
if (isBackCamera()) {
switch (getCameraMode()) {
case DUAL_MODE:
@@ -1664,6 +1688,7 @@ public class CaptureModule implements CameraModule, PhotoController,
@Override
public void onPreviewUIDestroyed() {
+ mSurfaceReady = false;
}
@Override
@@ -1700,6 +1725,10 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
+ public int getDisplayOrientation() {
+ return mOrientation;
+ }
+
@Override
public void onShowSwitcherPopup() {
@@ -1741,6 +1770,12 @@ public class CaptureModule implements CameraModule, PhotoController,
@Override
public void onShutterButtonClick() {
+ if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
+ Log.i(TAG, "Not enough space or storage not ready. remaining="
+ + mActivity.getStorageSpaceBytes());
+ return;
+ }
+
String timer = mSettingsManager.getValue(SettingsManager.KEY_TIMER);
int seconds = Integer.parseInt(timer);
@@ -1959,6 +1994,10 @@ public class CaptureModule implements CameraModule, PhotoController,
String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE);
if (value == null) return;
int mode = Integer.parseInt(value);
+ if(getPostProcFilterId(mode) != PostProcessor.FILTER_NONE) {
+ request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+ return;
+ }
if (mode != CaptureRequest.CONTROL_SCENE_MODE_DISABLED && mode !=
SettingsManager.SCENE_MODE_DUAL_INT) {
request.set(CaptureRequest.CONTROL_SCENE_MODE, mode);
@@ -2011,7 +2050,7 @@ public class CaptureModule implements CameraModule, PhotoController,
.FLASH_MODE_OFF);
break;
case CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH:
- if (redeye.equals("disable")) {
+ if (redeye != null && redeye.equals("disable")) {
request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest
.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
}
@@ -2095,7 +2134,8 @@ public class CaptureModule implements CameraModule, PhotoController,
}
private void updateFocusStateChange(CaptureResult result) {
- final int resultAFState = result.get(CaptureResult.CONTROL_AF_STATE);
+ final Integer resultAFState = result.get(CaptureResult.CONTROL_AF_STATE);
+ if (resultAFState == null) return;
// Report state change when AF state has changed.
if (resultAFState != mLastResultAFState && mFocusStateListener != null) {
@@ -2203,7 +2243,8 @@ public class CaptureModule implements CameraModule, PhotoController,
private boolean checkNeedToRestart(String value) {
mPostProcessor.setFilter(PostProcessor.FILTER_NONE);
- if (isPostProcFilter(value))
+ int mode = Integer.parseInt(value);
+ if (getPostProcFilterId(mode) != PostProcessor.FILTER_NONE)
return true;
if (value.equals(SettingsManager.SCENE_MODE_DUAL_STRING) && mCurrentMode != DUAL_MODE)
return true;
@@ -2312,4 +2353,14 @@ public class CaptureModule implements CameraModule, PhotoController,
unlockFocus(BAYER_ID);
unlockFocus(MONO_ID);
}
+
+ /**
+ * This Handler is used to post message back onto the main thread of the
+ * application
+ */
+ private class MainHandler extends Handler {
+ public MainHandler() {
+ super(Looper.getMainLooper());
+ }
+ }
}
diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java
index 5b8ecad6e..71d39e067 100644
--- a/src/com/android/camera/CaptureUI.java
+++ b/src/com/android/camera/CaptureUI.java
@@ -96,6 +96,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
SettingsManager.KEY_MAKEUP
};
String[] mDeveloperKeys = new String[]{
+ SettingsManager.KEY_REDEYE_REDUCTION,
SettingsManager.KEY_MONO_ONLY,
SettingsManager.KEY_CLEARSIGHT,
SettingsManager.KEY_MONO_PREVIEW
@@ -135,15 +136,14 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
public void surfaceCreated(SurfaceHolder holder) {
Log.v(TAG, "surfaceCreated");
mSurfaceHolder = holder;
- mModule.onPreviewUIReady();
- mActivity.updateThumbnail(mThumbnail);
+ previewUIReady();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v(TAG, "surfaceDestroyed");
mSurfaceHolder = null;
- mModule.onPreviewUIDestroyed();
+ previewUIDestroyed();
}
};
@@ -171,19 +171,39 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
// SurfaceHolder callbacks
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.v(TAG, "surfaceChanged2");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
+ Log.v(TAG, "surfaceCreated2");
mSurfaceHolder2 = holder;
+ previewUIReady();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.v(TAG, "surfaceDestroyed2");
mSurfaceHolder2 = null;
+ previewUIDestroyed();
}
};
+ private void previewUIReady() {
+ if((mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid()) &&
+ (mSurfaceView2.getVisibility() != View.VISIBLE ||
+ (mSurfaceView2.getVisibility() == View.VISIBLE &&
+ mSurfaceHolder2 != null &&
+ mSurfaceHolder2.getSurface().isValid()))) {
+ mModule.onPreviewUIReady();
+ mActivity.updateThumbnail(mThumbnail);
+ }
+ }
+
+ private void previewUIDestroyed() {
+ mModule.onPreviewUIDestroyed();
+ }
+
public CaptureUI(CameraActivity activity, CaptureModule module, View parent) {
mActivity = activity;
mModule = module;
@@ -348,7 +368,9 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
int index = mSettingsManager.getValueIndex(SettingsManager.KEY_CAMERA_ID);
CharSequence[] entries = mSettingsManager.getEntries(SettingsManager.KEY_CAMERA_ID);
- index = (index + 1) % entries.length;
+ do {
+ index = (index + 1) % entries.length;
+ } while (entries[index] == null);
mSettingsManager.setValueIndex(SettingsManager.KEY_CAMERA_ID, index);
int[] largeIcons = mSettingsManager.getResource(SettingsManager.KEY_CAMERA_ID,
SettingsManager.RESOURCE_TYPE_LARGEICON);
diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java
index 7d4d92087..24a5612c7 100644
--- a/src/com/android/camera/ComboPreferences.java
+++ b/src/com/android/camera/ComboPreferences.java
@@ -154,7 +154,6 @@ public class ComboPreferences implements
|| key.equals(CameraSettings.KEY_PHOTOSPHERE_PICTURESIZE)
|| key.equals(CameraSettings.KEY_CAMERA_SAVEPATH)
|| key.equals(SettingsManager.KEY_CAMERA2)
- || key.equals(SettingsManager.KEY_INITIAL_CAMERA)
|| key.equals(SettingsManager.KEY_CAMERA_ID)
|| key.equals(SettingsManager.KEY_MONO_ONLY)
|| key.equals(SettingsManager.KEY_MONO_PREVIEW)
diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java
index af26b27fe..cc7523265 100644
--- a/src/com/android/camera/SettingsManager.java
+++ b/src/com/android/camera/SettingsManager.java
@@ -30,11 +30,13 @@
package com.android.camera;
import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Log;
import android.util.Range;
@@ -58,6 +60,8 @@ import java.util.Set;
public class SettingsManager implements ListMenu.SettingsListener {
public static final int RESOURCE_TYPE_THUMBNAIL = 0;
public static final int RESOURCE_TYPE_LARGEICON = 1;
+ public static final int SCENE_MODE_NIGHT_INT = 5;
+
// Custom-Scenemodes start from 100
public static final int SCENE_MODE_DUAL_INT = 100;
public static final int SCENE_MODE_OPTIZOOM_INT = 101;
@@ -83,7 +87,6 @@ public class SettingsManager implements ListMenu.SettingsListener {
public static final String KEY_EXPOSURE = "pref_camera2_exposure_key";
public static final String KEY_TIMER = "pref_camera2_timer_key";
public static final String KEY_LONGSHOT = "pref_camera2_longshot_key";
- public static final String KEY_INITIAL_CAMERA = "pref_camera2_initial_camera_key";
private static final String TAG = "SnapCam_SettingsManager";
private static SettingsManager sInstance;
@@ -171,7 +174,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
public void init() {
Log.d(TAG, "SettingsManager init");
- int cameraId = CameraSettings.getInitialCameraId(mPreferences);
+ int cameraId = getInitialCameraId(mPreferences);
setLocalIdAndInitialize(cameraId);
}
@@ -473,6 +476,15 @@ public class SettingsManager implements ListMenu.SettingsListener {
return null;
}
+ public int getInitialCameraId(SharedPreferences pref) {
+ String value = pref.getString(SettingsManager.KEY_CAMERA_ID, "0");
+ int frontBackId = Integer.parseInt(value);
+ if (frontBackId == CaptureModule.FRONT_ID) return frontBackId;
+ String monoOnly = pref.getString(SettingsManager.KEY_MONO_ONLY, "off");
+ if (monoOnly.equals("off")) return frontBackId;
+ else return CaptureModule.MONO_ID;
+ }
+
private void filterPreferences(int cameraId) {
// filter unsupported preferences
ListPreference whiteBalance = mPreferenceGroup.findPreference(KEY_WHITE_BALANCE);
@@ -486,6 +498,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
ListPreference clearsight = mPreferenceGroup.findPreference(KEY_CLEARSIGHT);
ListPreference monoPreview = mPreferenceGroup.findPreference(KEY_MONO_PREVIEW);
ListPreference monoOnly = mPreferenceGroup.findPreference(KEY_MONO_ONLY);
+ ListPreference redeyeReduction = mPreferenceGroup.findPreference(KEY_REDEYE_REDUCTION);
if (whiteBalance != null) {
CameraSettings.filterUnsupportedOptions(mPreferenceGroup,
@@ -511,6 +524,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
if (pictureSize != null) {
CameraSettings.filterUnsupportedOptions(mPreferenceGroup,
pictureSize, getSupportedPictureSize(cameraId));
+ CameraSettings.filterSimilarPictureSize(mPreferenceGroup, pictureSize);
}
if (exposure != null) buildExposureCompensation(cameraId);
@@ -524,7 +538,11 @@ public class SettingsManager implements ListMenu.SettingsListener {
if (clearsight != null) removePreference(mPreferenceGroup, KEY_CLEARSIGHT);
if (monoPreview != null) removePreference(mPreferenceGroup, KEY_MONO_PREVIEW);
if (monoOnly != null) removePreference(mPreferenceGroup, KEY_MONO_ONLY);
+ }
+ if (redeyeReduction != null) {
+ CameraSettings.filterUnsupportedOptions(mPreferenceGroup,
+ redeyeReduction, getSupportedRedeyeReduction(cameraId));
}
}
@@ -687,6 +705,20 @@ public class SettingsManager implements ListMenu.SettingsListener {
return res;
}
+ private List<String> getSupportedRedeyeReduction(int cameraId) {
+ int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics
+ .CONTROL_AE_AVAILABLE_MODES);
+ List<String> modes = new ArrayList<>();
+ for (int i = 0; i < flashModes.length; i++) {
+ if (flashModes[i] == CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
+ modes.add("disable");
+ modes.add("enable");
+ break;
+ }
+ }
+ return modes;
+ }
+
private List<String> getSupportedWhiteBalanceModes(int cameraId) {
int[] whiteBalanceModes = mCharacteristics.get(cameraId).get(CameraCharacteristics
.CONTROL_AWB_AVAILABLE_MODES);
diff --git a/src/com/android/camera/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java
index 2fec1bf4f..773518821 100644
--- a/src/com/android/camera/exif/ExifInterface.java
+++ b/src/com/android/camera/exif/ExifInterface.java
@@ -1978,6 +1978,23 @@ public class ExifInterface {
return true;
}
+ public boolean addOrientationTag(int orientation) {
+ int value = Orientation.TOP_LEFT;
+ if(orientation == 90) {
+ value = Orientation.RIGHT_TOP;
+ } else if(orientation == 180) {
+ value = Orientation.BOTTOM_LEFT;
+ } else if(orientation == 270) {
+ value = Orientation.RIGHT_BOTTOM;
+ }
+ ExifTag t = buildTag(TAG_ORIENTATION, value);
+ if (t == null) {
+ return false;
+ }
+ setTag(t);
+ return true;
+ }
+
/**
* Creates and sets all to the GPS tags for a give latitude and longitude.
*
diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java
index 951479de9..6a2091158 100644
--- a/src/com/android/camera/imageprocessor/FrameProcessor.java
+++ b/src/com/android/camera/imageprocessor/FrameProcessor.java
@@ -128,7 +128,7 @@ public class FrameProcessor {
}
}
- private void createAllocation(int width, int height) {
+ private void createAllocation(int width, int height, int stridePad) {
Type.Builder yuvTypeBuilder = new Type.Builder(mRs, Element.YUV(mRs));
yuvTypeBuilder.setX(width);
yuvTypeBuilder.setY(height);
@@ -141,6 +141,8 @@ public class FrameProcessor {
mRsRotator.set_gOut(mProcessAllocation);
mRsRotator.set_width(width);
mRsRotator.set_height(height);
+ mRsRotator.set_pad(stridePad);
+ mRsRotator.set_gFlip(!mModule.isBackCamera());
mRsYuvToRGB.set_gIn(mProcessAllocation);
mRsYuvToRGB.set_width(height);
mRsYuvToRGB.set_height(width);
@@ -266,6 +268,7 @@ public class FrameProcessor {
int ySize;
int stride;
int height;
+ int width;
public ProcessingTask() {
}
@@ -288,6 +291,7 @@ public class FrameProcessor {
ByteBuffer bVU = image.getPlanes()[2].getBuffer();
if(yvuBytes == null) {
stride = image.getPlanes()[0].getRowStride();
+ width = mSize.getWidth();
height = mSize.getHeight();
ySize = stride * mSize.getHeight();
yvuBytes = new byte[ySize*3/2];
@@ -314,7 +318,7 @@ public class FrameProcessor {
return;
}
if(mInputAllocation == null) {
- createAllocation(stride, height);
+ createAllocation(stride, height, stride-width);
}
mInputAllocation.copyFrom(yvuBytes);
mRsRotator.forEach_rotate90andMerge(mInputAllocation);
diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java
index a126e8817..f39845346 100644
--- a/src/com/android/camera/imageprocessor/PostProcessor.java
+++ b/src/com/android/camera/imageprocessor/PostProcessor.java
@@ -43,17 +43,24 @@ import android.widget.Toast;
import com.android.camera.CameraActivity;
import com.android.camera.CaptureModule;
+import com.android.camera.Exif;
import com.android.camera.MediaSaveService;
import com.android.camera.PhotoModule;
import com.android.camera.SettingsManager;
+import com.android.camera.exif.ExifInterface;
import com.android.camera.imageprocessor.filter.OptizoomFilter;
+import com.android.camera.imageprocessor.filter.SharpshooterFilter;
import com.android.camera.ui.RotateTextToast;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
+import java.util.TimeZone;
+
import com.android.camera.imageprocessor.filter.ImageFilter;
+import com.android.camera.util.CameraUtil;
public class PostProcessor implements ImageReader.OnImageAvailableListener{
@@ -62,7 +69,8 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
private static final String TAG = "PostProcessor";
public static final int FILTER_NONE = 0;
public static final int FILTER_OPTIZOOM = 1;
- public static final int FILTER_MAX = 2;
+ public static final int FILTER_SHARPSHOOTER = 2;
+ public static final int FILTER_MAX = 3;
private int mCurrentNumImage = 0;
private ImageFilter mFilter;
@@ -80,7 +88,7 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
private WatchdogThread mWatchdog;
//This is for the debug feature.
- private static boolean DEBUG_FILTER = true; //TODO: This has to be false before releasing.
+ private static boolean DEBUG_FILTER = false;
private ImageFilter.ResultImage mDebugResultImage;
@Override
@@ -258,6 +266,9 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
case FILTER_OPTIZOOM:
mFilter = new OptizoomFilter(mController);
break;
+ case FILTER_SHARPSHOOTER:
+ mFilter = new SharpshooterFilter(mController);
+ break;
}
}
@@ -363,6 +374,20 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
});
}
+ private byte[] addExifTags(byte[] jpeg, int orientationInDegree) {
+ ExifInterface exif = new ExifInterface();
+ exif.addOrientationTag(orientationInDegree);
+ exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, System.currentTimeMillis(),
+ TimeZone.getDefault());
+ ByteArrayOutputStream jpegOut = new ByteArrayOutputStream();
+ try {
+ exif.writeExif(jpeg, jpegOut);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not write EXIF", e);
+ }
+ return jpegOut.toByteArray();
+ }
+
private void clear() {
mCurrentNumImage = 0;
}
@@ -409,29 +434,32 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{
) {
Log.e(TAG, "Processed outRoi is not within picture range");
} else {
+ int orientation = CameraUtil.getJpegRotation(mController.getMainCameraId(), mController.getDisplayOrientation());
if(mFilter != null && DEBUG_FILTER) {
- bytes = nv21ToJpeg(mDebugResultImage);
+ bytes = nv21ToJpeg(mDebugResultImage, orientation);
mActivity.getMediaSaveService().addImage(
bytes, title + "_beforeApplyingFilter", date, null, mDebugResultImage.outRoi.width(), mDebugResultImage.outRoi.height(),
- 0, null, mediaSavedListener, contentResolver, "jpeg");
+ orientation, null, mediaSavedListener, contentResolver, "jpeg");
}
- bytes = nv21ToJpeg(resultImage);
- mController.updateThumbnailJpegData(bytes);
+ bytes = nv21ToJpeg(resultImage, orientation);
mActivity.getMediaSaveService().addImage(
bytes, title, date, null, resultImage.outRoi.width(), resultImage.outRoi.height(),
- 0, null, mediaSavedListener, contentResolver, "jpeg");
+ orientation, null, mediaSavedListener, contentResolver, "jpeg");
+ mController.updateThumbnailJpegData(bytes);
}
}
}
});
}
- private byte[] nv21ToJpeg(ImageFilter.ResultImage resultImage) {
+ private byte[] nv21ToJpeg(ImageFilter.ResultImage resultImage, int orientation) {
BitmapOutputStream bos = new BitmapOutputStream(1024);
YuvImage im = new YuvImage(resultImage.outBuffer.array(), ImageFormat.NV21,
resultImage.width, resultImage.height, new int[]{resultImage.stride, resultImage.stride});
im.compressToJpeg(resultImage.outRoi, 50, bos);
- return bos.getArray();
+ byte[] bytes = bos.getArray();
+ bytes = addExifTags(bytes, orientation);
+ return bytes;
}
private class BitmapOutputStream extends ByteArrayOutputStream {
diff --git a/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java
index 4773418de..9b5af29dc 100644
--- a/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java
+++ b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java
@@ -45,7 +45,7 @@ public class OptizoomFilter implements ImageFilter{
private int mStrideY;
private int mStrideVU;
private static String TAG = "OptizoomFilter";
- private static final boolean DEBUG = true; //TODO: Have to be false before releasing.
+ private static final boolean DEBUG = false;
private int temp;
private static boolean mIsSupported = true;
private ByteBuffer mOutBuf;
diff --git a/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java b/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java
new file mode 100644
index 000000000..74469afc3
--- /dev/null
+++ b/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java
@@ -0,0 +1,172 @@
+/*
+Copyright (c) 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.camera.imageprocessor.filter;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.util.Log;
+import android.util.Range;
+import android.util.Rational;
+
+import com.android.camera.CaptureModule;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SharpshooterFilter implements ImageFilter{
+ public static final int NUM_REQUIRED_IMAGE = 5;
+ private int mWidth;
+ private int mHeight;
+ private int mStrideY;
+ private int mStrideVU;
+ private static String TAG = "SharpshooterFilter";
+ private static final boolean DEBUG = false;
+ private int temp;
+ private static boolean mIsSupported = true;
+ private ByteBuffer mOutBuf;
+ private CaptureModule mModule;
+ private int mSenseValue = 0;
+ private long mExpoTime;
+
+ private static void Log(String msg) {
+ if(DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+
+ public SharpshooterFilter(CaptureModule module) {
+ mModule = module;
+ }
+
+ private void getSenseUpperValue() {
+ if(mSenseValue == 0) {
+ Range<Integer> sensRange = mModule.getMainCameraCharacteristics().get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+ mSenseValue = sensRange.getUpper();
+ }
+ }
+ @Override
+ public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) {
+ getSenseUpperValue();
+ mExpoTime = (mModule.getPreviewCaptureResult().get(CaptureResult.SENSOR_EXPOSURE_TIME)/2);
+ int isoValue = (mModule.getPreviewCaptureResult().get(CaptureResult.SENSOR_SENSITIVITY)).intValue()*2;
+ if(isoValue < mSenseValue) {
+ mSenseValue = isoValue;
+ }
+
+ List<CaptureRequest> list = new ArrayList<CaptureRequest>();
+ for(int i=0; i < NUM_REQUIRED_IMAGE; i++) {
+ builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
+ builder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, new Long(mExpoTime));
+ builder.set(CaptureRequest.SENSOR_SENSITIVITY, mSenseValue);
+ list.add(builder.build());
+ }
+ return list;
+ }
+
+ @Override
+ public String getStringName() {
+ return "SharpshooterFilter";
+ }
+
+ @Override
+ public int getNumRequiredImage() {
+ return NUM_REQUIRED_IMAGE;
+ }
+
+ @Override
+ public void init(int width, int height, int strideY, int strideVU) {
+ Log("init");
+ mWidth = width/2*2;
+ mHeight = height/2*2;
+ mStrideY = strideY/2*2;
+ mStrideVU = strideVU/2*2;
+ mOutBuf = ByteBuffer.allocate(mStrideY*mHeight*3/2);
+ Log("width: "+mWidth+" height: "+mHeight+" strideY: "+mStrideY+" strideVU: "+mStrideVU);
+ nativeInit(mWidth, mHeight, mStrideY, mStrideVU,
+ 0, 0, mWidth, mHeight, NUM_REQUIRED_IMAGE);
+ }
+
+ @Override
+ public void deinit() {
+ Log("deinit");
+ mOutBuf = null;
+ nativeDeinit();
+ }
+
+ @Override
+ public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object param) {
+ Log("addImage");
+ int yActualSize = bY.remaining();
+ int vuActualSize = bVU.remaining();
+ int status = nativeAddImage(bY, bVU, yActualSize, vuActualSize, imageNum);
+ if(status != 0) {
+ Log.e(TAG, "Fail to add image");
+ }
+ }
+
+ @Override
+ public ResultImage processImage() {
+ Log("processImage ");
+ int[] roi = new int[4];
+ int status = nativeProcessImage(mOutBuf.array(), (int) (mExpoTime / 1000000), mSenseValue, roi);
+ Log("processImage done");
+ if(status < 0) { //In failure case, library will return the first image as it is.
+ Log.w(TAG, "Fail to process the image.");
+ }
+ return new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth, mHeight, mStrideY);
+ }
+
+ @Override
+ public boolean isSupported() {
+ return mIsSupported;
+ }
+
+ public static boolean isSupportedStatic() {
+ return mIsSupported;
+ }
+
+ private native int nativeInit(int width, int height, int yStride, int vuStride,
+ int roiX, int roiY, int roiW, int roiH, int numImages);
+ private native int nativeDeinit();
+ private native int nativeAddImage(ByteBuffer yB, ByteBuffer vuB, int ySize, int vuSize, int imageNum);
+ private native int nativeProcessImage(byte[] buffer, int expoTime, int isoValue, int[] roi);
+
+ static {
+ try {
+ System.loadLibrary("jni_sharpshooter");
+ mIsSupported = true;
+ }catch(UnsatisfiedLinkError e) {
+ Log.d(TAG, e.toString());
+ mIsSupported = false;
+ }
+ }
+}
diff --git a/src/com/android/camera/mpo/MpoOutputStream.java b/src/com/android/camera/mpo/MpoOutputStream.java
index 6e8e72fd9..5d01d269f 100644
--- a/src/com/android/camera/mpo/MpoOutputStream.java
+++ b/src/com/android/camera/mpo/MpoOutputStream.java
@@ -40,6 +40,7 @@ class MpoOutputStream extends FilterOutputStream {
private static final int STATE_SOI = 0;
private static final int STATE_FRAME_HEADER = 1;
+ private static final int STATE_SKIP_CROP = 2;
private static final int STATE_JPEG_DATA = 3;
private static final short TIFF_HEADER = 0x002A;
@@ -47,6 +48,9 @@ class MpoOutputStream extends FilterOutputStream {
private static final short TIFF_LITTLE_ENDIAN = 0x4949;
private static final int MAX_EXIF_SIZE = 65535;
+ private static final String DC_CROP_INFO = "Qualcomm Dual Camera Attributes";
+ private static final int DC_CROP_INFO_BYTE_SIZE = DC_CROP_INFO.length();
+
private MpoData mMpoData;
private MpoImageData mCurrentImageData;
private int mState = STATE_SOI;
@@ -54,8 +58,10 @@ class MpoOutputStream extends FilterOutputStream {
private int mByteToCopy;
private byte[] mSingleByteArray = new byte[1];
private ByteBuffer mBuffer = ByteBuffer.allocate(4);
+ private ByteBuffer mCropInfo = ByteBuffer.allocate(DC_CROP_INFO_BYTE_SIZE);
private int mMpoOffsetStart = -1;
private int mSize = 0;
+ private boolean mSkipCropData = false;
protected MpoOutputStream(OutputStream ou) {
super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
@@ -77,19 +83,41 @@ class MpoOutputStream extends FilterOutputStream {
mBuffer.rewind();
}
- private int requestByteToBuffer(int requestByteCount, byte[] buffer, int offset, int length) {
- int byteNeeded = requestByteCount - mBuffer.position();
+ private int requestByteToBuffer(ByteBuffer buffer, int requestByteCount, byte[] data, int offset, int length) {
+ int byteNeeded = requestByteCount - buffer.position();
int byteToRead = length > byteNeeded ? byteNeeded : length;
- mBuffer.put(buffer, offset, byteToRead);
+ buffer.put(data, offset, byteToRead);
return byteToRead;
}
+ private boolean isDualCamCropInfo() {
+ // first check length
+ if(mCropInfo.position() != DC_CROP_INFO_BYTE_SIZE) {
+ return false;
+ }
+
+ mCropInfo.rewind();
+ for(int i = 0; i < DC_CROP_INFO.length(); i++) {
+ char c = (char)mCropInfo.get(i);
+ //Log.d(TAG, "mCropInfo char @ " + (i) + ": " + c);
+ if(DC_CROP_INFO.charAt(i) != c)
+ return false;
+ }
+
+ return true;
+ }
+
void writeMpoFile() throws IOException {
// check and write primary image
mCurrentImageData = mMpoData.getPrimaryMpoImage();
+ // don't skip if primary == bayer
+ if(mMpoData.getAuxiliaryImageCount() > 1) {
+ mSkipCropData = true;
+ }
write(mCurrentImageData.getJpegData());
flush();
+ mSkipCropData = false;
// check and write auxiliary images
for (MpoImageData image : mMpoData.getAuxiliaryMpoImages()) {
resetStates();
@@ -125,7 +153,7 @@ class MpoOutputStream extends FilterOutputStream {
}
switch (mState) {
case STATE_SOI:
- int byteRead = requestByteToBuffer(2, buffer, offset, length);
+ int byteRead = requestByteToBuffer(mBuffer, 2, buffer, offset, length);
offset += byteRead;
length -= byteRead;
if (mBuffer.position() < 2) {
@@ -141,12 +169,9 @@ class MpoOutputStream extends FilterOutputStream {
mBuffer.rewind();
break;
case STATE_FRAME_HEADER:
- // Copy APP1 if it exists
+ // Copy APP0 and APP1 if it exists
// Insert MPO data
- // Copy remainder of image
- byteRead = requestByteToBuffer(4, buffer, offset, length);
- offset += byteRead;
- length -= byteRead;
+ byteRead = requestByteToBuffer(mBuffer, 4, buffer, offset, length);
// Check if this image data doesn't contain SOF.
if (mBuffer.position() == 2) {
short tag = mBuffer.getShort();
@@ -165,8 +190,63 @@ class MpoOutputStream extends FilterOutputStream {
out.write(mBuffer.array(), 0, 4);
mSize += 4;
mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
+ offset += byteRead;
+ length -= byteRead;
} else {
writeMpoData();
+ if(mSkipCropData)
+ mState = STATE_SKIP_CROP;
+ else
+ mState = STATE_JPEG_DATA;
+ }
+ mBuffer.rewind();
+ break;
+ case STATE_SKIP_CROP:
+ byteRead = requestByteToBuffer(mBuffer, 4, buffer, offset, length);
+ // Check if this image data doesn't contain SOF.
+ if (mBuffer.position() == 2) {
+ short tag = mBuffer.getShort();
+ if (tag == JpegHeader.EOI) {
+ out.write(mBuffer.array(), 0, 2);
+ mSize += 2;
+ mBuffer.rewind();
+ }
+ }
+ if (mBuffer.position() < 4) {
+ return;
+ }
+
+ offset += byteRead;
+ length -= byteRead;
+ mBuffer.rewind();
+
+ marker = mBuffer.getShort();
+ if (!JpegHeader.isSofMarker(marker)) {
+ // if not SOF, read first 31 bytes
+ // try to match dual cam crop magic string
+ byteRead = requestByteToBuffer(mCropInfo, DC_CROP_INFO_BYTE_SIZE, buffer, offset, length);
+ if(isDualCamCropInfo()) {
+ // if crop info, clear with 0
+ out.write(mBuffer.array(), 0, 4);
+ mSize += 4;
+
+ int sizeToClear = mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
+ while(sizeToClear > 0) {
+ out.write(0);
+ mSize++;
+ sizeToClear--;
+ }
+ mState = STATE_JPEG_DATA;
+ } else {
+ // else copy this block
+ // and move on to next header
+ out.write(mBuffer.array(), 0, 4);
+ mSize += 4;
+ mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
+ }
+ mCropInfo.rewind();
+ } else {
+ // SOF is reached, no crop info detected, skip
out.write(mBuffer.array(), 0, 4);
mSize += 4;
mState = STATE_JPEG_DATA;