/* * Copyright (c) 2015, 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; import java.io.File; import java.io.FileInputStream; import java.io.OutputStream; import android.animation.Animator; import android.animation.Keyframe; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Build; import android.util.AttributeSet; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.FrameLayout; import org.codeaurora.snapcam.R; public class RefocusActivity extends Activity { private static final String TAG = "RefocusActivity"; private static final String[] NAMES = { "00", "01", "02", "03", "04", "AllFocusImage" }; private Uri mUri; private ImageView mImageView; private int mWidth; private int mHeight; private Indicator mIndicator; private View mAllInFocusView; private DepthMap mDepthMap; private int mCurrentImage = -1; private int mRequestedImage = -1; private LoadImageTask mLoadImageTask; @Override public void onCreate(Bundle state) { super.onCreate(state); new Thread(new Runnable() { public void run() { mDepthMap = new DepthMap(getFilesDir() + "/DepthMapImage.y"); } }).start(); mUri = getIntent().getData(); setResult(RESULT_CANCELED, new Intent()); setContentView(R.layout.refocus_editor); mIndicator = (Indicator) findViewById(R.id.refocus_indicator); mImageView = (ImageView) findViewById(R.id.refocus_image); mImageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: float x = event.getX(); float y = event.getY(); int w = v.getWidth(); int h = v.getHeight(); mIndicator.startAnimation(x + mImageView.getLeft(), y + mImageView.getTop()); if (mDepthMap != null) { int depth = mDepthMap.getDepth(x / (float) w, y / (float) h); setCurrentImage(depth); mAllInFocusView.setBackground(getDrawable( R.drawable.refocus_button_disable)); } break; } return true; } }); mAllInFocusView = findViewById(R.id.refocus_all); mAllInFocusView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { allInFocus(); } }); findViewById(R.id.refocus_cancel).setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { finish(); } }); findViewById(R.id.refocus_done).setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (mRequestedImage != NAMES.length - 1) { new SaveImageTask().execute(getFilesDir() + "/" + NAMES[mRequestedImage] + ".jpg"); } else { finish(); } } }); Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); mWidth = size.x; mHeight = size.y; allInFocus(); } private void setCurrentImage(int depth) { if (depth >= 0 && depth < NAMES.length && depth != mRequestedImage) { mRequestedImage = depth; if (mLoadImageTask != null) { mLoadImageTask.cancel(true); } if (depth != mCurrentImage) { mCurrentImage = depth; mLoadImageTask = new LoadImageTask(); mLoadImageTask.execute(getFilesDir() + "/" + NAMES[depth] + ".jpg"); } } } private void allInFocus() { setCurrentImage(NAMES.length - 1); mAllInFocusView.setBackground(getDrawable(R.drawable.refocus_button_enable)); } private class SaveImageTask extends AsyncTask { protected Void doInBackground(String... path) { try { OutputStream out = getContentResolver().openOutputStream(mUri); FileInputStream in = new FileInputStream(path[0]); byte[] buf = new byte[4096]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } in.close(); out.close(); } catch (Exception e) { } Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mUri); sendBroadcast(intent); return null; } protected void onPostExecute(Void v) { finish(); } } private class LoadImageTask extends AsyncTask { protected Bitmap doInBackground(String... path) { final BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(path[0], o); int h = o.outHeight; int w = o.outWidth; int sample = 1; if (h > mHeight || w > mWidth) { while (h / sample / 2 > mHeight && w / sample / 2 > mWidth) { sample *= 2; } } o.inJustDecodeBounds = false; o.inSampleSize = sample; return BitmapFactory.decodeFile(path[0], o); } protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } } private class DepthMap { private byte[] mData; private int mWidth; private int mHeight; private boolean mFail = true; private static final int W_SIZE = 61; public DepthMap(final String path) { File file = new File(path); try { FileInputStream stream = new FileInputStream(file); mData = new byte[(int) file.length()]; stream.read(mData); stream.close(); } catch (Exception e) { } int length = (int) mData.length; if (length > 25) { mFail = (mData[length - 25] != 0); mWidth = readInteger(length - 24); mHeight = readInteger(length - 20); } if (mWidth * mHeight + 25 > length) { mFail = true; } } public int getDepth(float x, float y) { if (mFail || x > 1.0f || y > 1.0f) { return NAMES.length - 1; } int newX = (int) (x * mWidth); int newY = (int) (y * mHeight); int[] hist = new int[256]; for (int i = 0; i < 256; i++) { hist[i] = 0; } int colStart = Math.max(newX - W_SIZE / 2, 0); int colEnd = Math.min(colStart + W_SIZE, mWidth); int rowStart = Math.max(newY - W_SIZE / 2, 0); int rowEnd = Math.min(rowStart + W_SIZE, mHeight); for (int col = colStart; col < colEnd; col++) { for (int row = rowStart; row < rowEnd; row++) { int level = mData[row * mWidth + col]; hist[level]++; } } int depth = NAMES.length - 1; int maxCount = 0; for (int i = 0; i < 256; i++) { int count = hist[i]; if (count != 0 && (maxCount == 0 || count > maxCount)) { maxCount = count; depth = i; } } return depth; } private int readInteger(int offset) { int result = mData[offset] & 0xff; for (int i = 1; i < 4; ++i) { result <<= 8; result += mData[offset + i] & 0xff; } return result; } } public static class Indicator extends FrameLayout { private float mX; private float mY; private ValueAnimator mAnimator; private Paint mPaint; private float mCrossLength; private float mStrokeWidth; private int mColor1; private int mColor2; public Indicator(Context context, AttributeSet attr) { super(context, attr); final Resources res = context.getResources(); float r1 = res.getDimensionPixelSize(R.dimen.refocus_circle_diameter_1) / 2; float r2 = res.getDimensionPixelSize(R.dimen.refocus_circle_diameter_2) / 2; float r3 = res.getDimensionPixelSize(R.dimen.refocus_circle_diameter_3) / 2; mCrossLength = res.getDimensionPixelSize(R.dimen.refocus_cross_length) / 2; mStrokeWidth = res.getDimensionPixelSize(R.dimen.refocus_stroke_width) / 2; mColor1 = res.getColor(R.color.refocus_indicator_1); mColor2 = res.getColor(R.color.refocus_indicator_2); Keyframe k0 = Keyframe.ofFloat(0f, r1); Keyframe k1 = Keyframe.ofFloat(5f / 12f, r2); Keyframe k2 = Keyframe.ofFloat(0.5f, r2); Keyframe k3 = Keyframe.ofFloat(0.75f, r3); Keyframe k4 = Keyframe.ofFloat(1f, r2); PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe( "radius", k0, k1, k2, k3, k4); mAnimator = ValueAnimator.ofPropertyValuesHolder(holder); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStrokeCap(Paint.Cap.BUTT); mPaint.setStrokeWidth(res.getDimensionPixelSize(R.dimen.refocus_stroke_width)); } @Override protected void onDraw(Canvas canvas) { if (mAnimator != null && mAnimator.isStarted()) { mPaint.setColor(mAnimator.getAnimatedFraction() < 0.5f ? mColor1 : mColor2); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(mX, mY, (Float) mAnimator.getAnimatedValue(), mPaint); canvas.drawLine(mX - mCrossLength, mY, mX + mCrossLength, mY, mPaint); canvas.drawLine(mX, mY - mCrossLength, mX, mY + mCrossLength, mPaint); } } public void startAnimation(float x, float y) { mX = x; mY = y; if (mAnimator != null) { mAnimator.cancel(); } mAnimator.setDuration(720); mAnimator.removeAllUpdateListeners(); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } }); mAnimator.removeAllListeners(); mAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { setWillNotDraw(false); } @Override public void onAnimationEnd(Animator animation) { setWillNotDraw(true); } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { setWillNotDraw(true); } }); mAnimator.start(); } } }