From 7f35afc3939acbffeff32c2e419d1e46648fba4c Mon Sep 17 00:00:00 2001 From: Jack Yoo Date: Tue, 10 May 2016 14:11:19 -0700 Subject: SnapdragonCamera: Sharpshooter post filter Adding Sharpshooter post process filter Change-Id: I8f3c1962db437e7dfbe2973f806a3f9a26480eb7 CRs-Fixed: 1023183 --- rs/YuvToRgb.rs | 6 +- rs/rotator.rs | 24 ++- src/com/android/camera/CaptureModule.java | 59 ++++--- src/com/android/camera/SettingsManager.java | 2 + src/com/android/camera/exif/ExifInterface.java | 17 ++ .../camera/imageprocessor/FrameProcessor.java | 8 +- .../camera/imageprocessor/PostProcessor.java | 46 ++++-- .../imageprocessor/filter/OptizoomFilter.java | 2 +- .../imageprocessor/filter/SharpshooterFilter.java | 172 +++++++++++++++++++++ 9 files changed, 299 insertions(+), 37 deletions(-) create mode 100644 src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java 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/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 0f65f21c3..9d09343ea 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; @@ -323,6 +326,10 @@ public class CaptureModule implements CameraModule, PhotoController, return mStickyFaces; } + public CaptureResult getPreviewCaptureResult() { + return mPreviewCaptureResult; + } + public Rect getCameraRegion() { return mBayerCameraRegion; } @@ -351,6 +358,7 @@ public class CaptureModule implements CameraModule, PhotoController, if(faces != null && faces.length != 0) { mStickyFaces = faces; } + mPreviewCaptureResult = result; switch (mState[id]) { case STATE_PREVIEW: { @@ -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; @@ -1027,6 +1035,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 +1060,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); @@ -1364,23 +1378,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 +1394,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 { @@ -1575,7 +1588,7 @@ public class CaptureModule implements CameraModule, PhotoController, } } - private int getMainCameraId() { + public int getMainCameraId() { if (isBackCamera()) { switch (getCameraMode()) { case DUAL_MODE: @@ -1664,6 +1677,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPreviewUIDestroyed() { + mSurfaceReady = false; } @Override @@ -1700,6 +1714,10 @@ public class CaptureModule implements CameraModule, PhotoController, } } + public int getDisplayOrientation() { + return mOrientation; + } + @Override public void onShowSwitcherPopup() { @@ -1959,6 +1977,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); @@ -2203,7 +2225,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; diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index 215ab8436..1433f1cd0 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -58,6 +58,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; 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 sensRange = mModule.getMainCameraCharacteristics().get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); + mSenseValue = sensRange.getUpper(); + } + } + @Override + public List 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 list = new ArrayList(); + 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; + } + } +} -- cgit v1.2.3