1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package rs.example.android.com.healingbrush;
18
19import android.app.Activity;
20import android.content.Intent;
21import android.database.Cursor;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.Matrix;
25import android.graphics.drawable.Drawable;
26import android.net.Uri;
27import android.os.AsyncTask;
28import android.os.Bundle;
29import android.os.Environment;
30import android.provider.OpenableColumns;
31import android.support.v8.renderscript.RenderScript;
32import android.util.Log;
33import android.view.MotionEvent;
34import android.view.View;
35import android.widget.ImageView;
36import android.widget.Toast;
37
38import com.example.android.rs.sample.ScriptC_find_region;
39import com.example.android.rs.sample.ScriptC_healing;
40
41import java.io.File;
42import java.io.FileNotFoundException;
43import java.io.InputStream;
44
45public class MainActivity extends Activity {
46    private static final String TAG = "MainActivity";
47    ImageView mImgView;
48    DrawView mDrawView;
49    Matrix mMatrix = new Matrix();
50    Matrix mInverseMatrix = new Matrix();
51    Bitmap mDisplayedImage;
52    Bitmap mImage2;
53    RenderScript mRs;
54    ScriptC_healing mHealingScript;
55    ScriptC_find_region mFindRegion;
56    private float mZoom = 0.8f;
57    float mYOffset = 0;
58    float mXOffset = 0;
59    RunScript mRunScript = null;
60    private String mImagePath;
61    private String mImageName;
62    private Region mLastRegion;
63
64    @Override
65    protected void onCreate(Bundle savedInstanceState) {
66        super.onCreate(savedInstanceState);
67        setContentView(R.layout.activity_main);
68        mImgView = (ImageView) findViewById(R.id.imageview);
69        mDrawView = (DrawView) findViewById(R.id.overlay);
70        mDrawView.setImageView(mImgView);
71        mRs = RenderScript.create(this.getBaseContext());
72        mHealingScript = new ScriptC_healing(mRs);
73        mFindRegion = new ScriptC_find_region(mRs);
74
75        mImgView.setOnTouchListener(new View.OnTouchListener() {
76            float[] imgPoint = new float[2];
77            float[] imgMoveList = new float[100];
78            boolean mPanZoomDown = false;
79
80            float mCenterDownX;
81            float mCenterDownY;
82            float mDistDown;
83            float mDownXOffset;
84            float mDownYOffset;
85            float mDownZoom;
86            boolean inMultiTouch = false;
87
88            @Override
89            public boolean onTouch(View v, MotionEvent event) {
90                int action = event.getAction();
91                float x = event.getX();
92                float y = event.getY();
93                imgPoint[0] = x;
94                imgPoint[1] = y;
95                int sw = mImgView.getWidth();
96                int sh = mImgView.getHeight();
97                int iw = mImgView.getDrawable().getIntrinsicWidth();
98                int ih = mImgView.getDrawable().getIntrinsicHeight();
99                switch (action) {
100                    case MotionEvent.ACTION_DOWN:
101                        Log.v(TAG, "ACTION_DOWN " + event.getPointerCount());
102
103                        break;
104                    case MotionEvent.ACTION_UP:
105                        Log.v(TAG, "ACTION_UP " + event.getPointerCount());
106
107                        break;
108                    case MotionEvent.ACTION_MOVE:
109                        Log.v(TAG, "ACTION_MOVE " + event.getPointerCount());
110                        break;
111                }
112                if (event.getPointerCount() > 1) {
113                    inMultiTouch = true;
114                }
115                if (event.getPointerCount() == 2) {
116                    float x1 = event.getX(0);
117                    float y1 = event.getY(0);
118                    float x2 = event.getX(1);
119                    float y2 = event.getY(1);
120                    if (mPanZoomDown) {
121                        float dx = (x1 + x2) / 2 - mCenterDownX;
122                        float dy = (y1 + y2) / 2 - mCenterDownY;
123                        float zoom = (float) Math.hypot(x1 - x2, y1 - y2);
124                        mZoom = zoom * mDownZoom / mDistDown;
125
126                        float scale = mZoom * Math.min(sw / (float) iw, sh / (float) ih);
127                        mXOffset = mDownXOffset + 2 * (dx / (sw - scale * iw));
128                        mYOffset = mDownYOffset + 2 * (dy / (sh - scale * ih));
129                        if (Math.abs(mXOffset) > 1) {
130                            mXOffset = Math.signum(mXOffset);
131                        }
132                        if (Math.abs(mYOffset) > 1) {
133                            mYOffset = Math.signum(mYOffset);
134                        }
135                    } else {
136                        mDrawView.undo();
137                        mPanZoomDown = true;
138                        mCenterDownX = (x1 + x2) / 2;
139                        mCenterDownY = (y1 + y2) / 2;
140                        mDistDown = (float) Math.hypot(x1 - x2, y1 - y2);
141                        mDownXOffset = mXOffset;
142                        mDownYOffset = mYOffset;
143                        mDownZoom = mZoom;
144                    }
145                } else {
146                    if (mPanZoomDown) {
147                        mPanZoomDown = false;
148                    }
149                }
150                if (!mPanZoomDown) {
151                    switch (action) {
152                        case MotionEvent.ACTION_DOWN:
153                            mInverseMatrix.mapPoints(imgPoint);
154                            mDrawView.clearDrawables();
155                            mDrawView.downPoint(imgPoint);
156                            mDrawView.invalidate();
157
158                            break;
159                        case MotionEvent.ACTION_UP:
160                            if (inMultiTouch && event.getPointerCount() == 1) {
161                                inMultiTouch = false;
162                            } else {
163                                mInverseMatrix.mapPoints(imgPoint);
164                                mDrawView.upPoint(imgPoint);
165                                mDrawView.invalidate();
166                            }
167
168                            break;
169                        case MotionEvent.ACTION_MOVE:
170
171                            int size = event.getHistorySize();
172                            size = Math.min(size, imgMoveList.length / 2);
173                            for (int i = 0; i < size; i++) {
174                                imgMoveList[i * 2] = event.getHistoricalX(size - i - 1);
175                                imgMoveList[i * 2 + 1] = event.getHistoricalY(size - i - 1);
176                            }
177                            mInverseMatrix.mapPoints(imgMoveList, 0, imgMoveList, 0, size);
178                            if (!inMultiTouch) {
179                                mDrawView.movePoint(imgMoveList, size);
180                                mDrawView.invalidate();
181                            }
182                            break;
183                    }
184                }
185                updateMatrix();
186
187                return true;
188            }
189        });
190
191
192        new AsyncLoad().execute();
193    }
194
195    void updateMatrix() {
196        int sw = mImgView.getWidth();
197        int sh = mImgView.getHeight();
198        int iw = mImgView.getDrawable().getIntrinsicWidth();
199        int ih = mImgView.getDrawable().getIntrinsicHeight();
200
201
202        mMatrix.reset();
203        float scale = mZoom * Math.min(sw / (float) iw, sh / (float) ih);
204        mMatrix.postTranslate((1 + mXOffset) * (sw - iw * scale) / 2,
205                (1 + mYOffset) * (sh - ih * scale) / 2);
206        mMatrix.preScale(scale, scale);
207        boolean ret = mMatrix.invert(mInverseMatrix);
208        if (!ret) {
209            Log.e(TAG, "Fail to invert");
210        }
211        mImgView.setImageMatrix(mMatrix);
212        mImgView.invalidate();
213        mDrawView.invalidate();
214     }
215
216    void getScreenCoord(float[] point) {
217        Matrix matrix = mImgView.getImageMatrix();
218    }
219    class AsyncLoad extends AsyncTask<Void, Void, Void> {
220
221        protected Void doInBackground(Void... regions) {
222            Intent intent = getIntent();
223
224            if (intent != null) {
225                File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
226                mImagePath = folder.getPath();
227
228                String s = intent.getType();
229                if (s != null && s.indexOf("image/") != -1) {
230                    Uri data = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
231                    mImageName = "edit"+data.getLastPathSegment(); // TODO the wrong way to do this
232
233                    if (data != null) {
234                        InputStream input = null;
235                        try {
236                            input = getContentResolver().openInputStream(data);
237                            mDisplayedImage = BitmapFactory.decodeStream(input);
238
239                            return null;
240                        } catch (FileNotFoundException e) {
241                            e.printStackTrace();
242                        }
243
244                    }
245                }
246            }
247
248            getLocalImage();
249            return null;
250        }
251
252        @Override
253        protected void onPostExecute(Void s) {
254            mImgView.setImageBitmap(mDisplayedImage);
255            Log.v(TAG, "BITMAP SIZE = " + mDisplayedImage.getWidth() + "," +
256                    mDisplayedImage.getHeight());
257
258            updateMatrix();
259        }
260    }
261    void getLocalImage() {
262
263        File folder;
264        folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
265        mImagePath = folder.getPath();
266        File[] files = folder.listFiles();
267        if (files != null) {
268            Log.v(TAG, "files" + files.length);
269            for (int i = 0; i < files.length; i++) {
270                Log.v(TAG, "[" + i + "]=" + files[i].getAbsolutePath());
271                if (files[i].getName().toLowerCase().endsWith(".jpg")) {
272                    mDisplayedImage = BitmapFactory.decodeFile(files[i].getAbsolutePath());
273                    mImagePath = files[i].getParentFile().getAbsolutePath();
274                    mImageName = files[i].getName();
275                    return;
276                }
277            }
278        }
279
280        mDisplayedImage = BitmapFactory.decodeResource(this.getResources(), R.drawable.bugs);
281        mImageName = "bugs";
282    }
283
284    public void heal(View v) {
285        mLastRegion = mDrawView.getRegion(mDisplayedImage);
286        if (mRunScript == null) {
287            mRunScript = new RunScript();
288            mRunScript.execute(mLastRegion);
289        }
290    }
291
292    public void undo(View v) {
293        if (mImage2 != null) {
294            mLastRegion.undo(mImage2);
295            mDrawView.invalidate();
296        }
297    }
298
299    public void save(View v) {
300        String name = mImageName;
301        if (name.indexOf(".") > 0) {
302            name = name.substring(0, name.lastIndexOf(".")) + "_e";
303        }
304        MediaStoreSaver.save(mImage2,
305                mImagePath,
306                name,
307                this,
308                MediaStoreSaver.TYPE_JPG);
309        Toast.makeText(this, "Saved " + name, Toast.LENGTH_SHORT).show();
310    }
311
312    class RunScript extends AsyncTask<Region, String, String> {
313        Drawable d;
314
315        protected String doInBackground(Region... regions) {
316            d = regions[0].findMatch(mFindRegion, mRs, mDisplayedImage);
317
318            if (mImage2 == null) {
319                mImage2 = mDisplayedImage.copy(Bitmap.Config.ARGB_8888, true);
320
321            }
322            regions[0].heal(mHealingScript, mRs, mImage2, mImage2);
323
324            return "";
325        }
326
327        @Override
328        protected void onPostExecute(String s) {
329            super.onPostExecute(s);
330            mDrawView.addDrawable(d);
331            mDrawView.invalidate();
332            mImgView.setImageBitmap(mImage2);
333            mRunScript = null;
334        }
335    }
336
337}
338