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 = findViewById(R.id.imageview); 69 mDrawView = 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