1 /* 2 * Copyright (C) 2013 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 17 package com.android.gallery3d.filtershow.crop; 18 19 import android.app.ActionBar; 20 import android.app.Activity; 21 import android.app.WallpaperManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.res.Configuration; 25 import android.graphics.Bitmap; 26 import android.graphics.Bitmap.CompressFormat; 27 import android.graphics.BitmapFactory; 28 import android.graphics.BitmapRegionDecoder; 29 import android.graphics.Canvas; 30 import android.graphics.Matrix; 31 import android.graphics.Paint; 32 import android.graphics.Rect; 33 import android.graphics.RectF; 34 import android.net.Uri; 35 import android.os.AsyncTask; 36 import android.os.Bundle; 37 import android.provider.MediaStore; 38 import android.util.DisplayMetrics; 39 import android.util.Log; 40 import android.view.View; 41 import android.view.View.OnClickListener; 42 import android.view.WindowManager; 43 import android.widget.Toast; 44 45 import com.android.gallery3d.R; 46 import com.android.gallery3d.common.Utils; 47 import com.android.gallery3d.filtershow.cache.ImageLoader; 48 import com.android.gallery3d.filtershow.tools.SaveImage; 49 50 import java.io.ByteArrayInputStream; 51 import java.io.ByteArrayOutputStream; 52 import java.io.FileNotFoundException; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.io.OutputStream; 56 57 /** 58 * Activity for cropping an image. 59 */ 60 public class CropActivity extends Activity { 61 private static final String LOGTAG = "CropActivity"; 62 public static final String CROP_ACTION = "com.android.camera.action.CROP"; 63 private CropExtras mCropExtras = null; 64 private LoadBitmapTask mLoadBitmapTask = null; 65 66 private int mOutputX = 0; 67 private int mOutputY = 0; 68 private Bitmap mOriginalBitmap = null; 69 private RectF mOriginalBounds = null; 70 private int mOriginalRotation = 0; 71 private Uri mSourceUri = null; 72 private CropView mCropView = null; 73 private View mSaveButton = null; 74 private boolean finalIOGuard = false; 75 76 private static final int SELECT_PICTURE = 1; // request code for picker 77 78 private static final int DEFAULT_COMPRESS_QUALITY = 90; 79 /** 80 * The maximum bitmap size we allow to be returned through the intent. 81 * Intents have a maximum of 1MB in total size. However, the Bitmap seems to 82 * have some overhead to hit so that we go way below the limit here to make 83 * sure the intent stays below 1MB.We should consider just returning a byte 84 * array instead of a Bitmap instance to avoid overhead. 85 */ 86 public static final int MAX_BMAP_IN_INTENT = 750000; 87 88 // Flags 89 private static final int DO_SET_WALLPAPER = 1; 90 private static final int DO_RETURN_DATA = 1 << 1; 91 private static final int DO_EXTRA_OUTPUT = 1 << 2; 92 93 private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT; 94 95 @Override onCreate(Bundle savedInstanceState)96 public void onCreate(Bundle savedInstanceState) { 97 super.onCreate(savedInstanceState); 98 Intent intent = getIntent(); 99 setResult(RESULT_CANCELED, new Intent()); 100 mCropExtras = getExtrasFromIntent(intent); 101 if (mCropExtras != null && mCropExtras.getShowWhenLocked()) { 102 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 103 } 104 105 setContentView(R.layout.crop_activity); 106 mCropView = (CropView) findViewById(R.id.cropView); 107 108 ActionBar actionBar = getActionBar(); 109 if (actionBar != null) { 110 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 111 actionBar.setCustomView(R.layout.filtershow_actionbar); 112 113 View mSaveButton = actionBar.getCustomView(); 114 mSaveButton.setOnClickListener(new OnClickListener() { 115 @Override 116 public void onClick(View view) { 117 startFinishOutput(); 118 } 119 }); 120 } 121 if (intent.getData() != null) { 122 mSourceUri = intent.getData(); 123 startLoadBitmap(mSourceUri); 124 } else { 125 pickImage(); 126 } 127 } 128 enableSave(boolean enable)129 private void enableSave(boolean enable) { 130 if (mSaveButton != null) { 131 mSaveButton.setEnabled(enable); 132 } 133 } 134 135 @Override onDestroy()136 protected void onDestroy() { 137 if (mLoadBitmapTask != null) { 138 mLoadBitmapTask.cancel(false); 139 } 140 super.onDestroy(); 141 } 142 143 @Override onConfigurationChanged(Configuration newConfig)144 public void onConfigurationChanged (Configuration newConfig) { 145 super.onConfigurationChanged(newConfig); 146 mCropView.configChanged(); 147 } 148 149 /** 150 * Opens a selector in Gallery to chose an image for use when none was given 151 * in the CROP intent. 152 */ pickImage()153 private void pickImage() { 154 Intent intent = new Intent(); 155 intent.setType("image/*"); 156 intent.setAction(Intent.ACTION_GET_CONTENT); 157 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)), 158 SELECT_PICTURE); 159 } 160 161 /** 162 * Callback for pickImage(). 163 */ 164 @Override onActivityResult(int requestCode, int resultCode, Intent data)165 public void onActivityResult(int requestCode, int resultCode, Intent data) { 166 if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) { 167 mSourceUri = data.getData(); 168 startLoadBitmap(mSourceUri); 169 } 170 } 171 172 /** 173 * Gets screen size metric. 174 */ getScreenImageSize()175 private int getScreenImageSize() { 176 DisplayMetrics outMetrics = new DisplayMetrics(); 177 getWindowManager().getDefaultDisplay().getMetrics(outMetrics); 178 return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels); 179 } 180 181 /** 182 * Method that loads a bitmap in an async task. 183 */ startLoadBitmap(Uri uri)184 private void startLoadBitmap(Uri uri) { 185 if (uri != null) { 186 enableSave(false); 187 final View loading = findViewById(R.id.loading); 188 loading.setVisibility(View.VISIBLE); 189 mLoadBitmapTask = new LoadBitmapTask(); 190 mLoadBitmapTask.execute(uri); 191 } else { 192 cannotLoadImage(); 193 done(); 194 } 195 } 196 197 /** 198 * Method called on UI thread with loaded bitmap. 199 */ doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation)200 private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) { 201 final View loading = findViewById(R.id.loading); 202 loading.setVisibility(View.GONE); 203 mOriginalBitmap = bitmap; 204 mOriginalBounds = bounds; 205 mOriginalRotation = orientation; 206 if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) { 207 RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); 208 mCropView.initialize(bitmap, imgBounds, imgBounds, orientation); 209 if (mCropExtras != null) { 210 int aspectX = mCropExtras.getAspectX(); 211 int aspectY = mCropExtras.getAspectY(); 212 mOutputX = mCropExtras.getOutputX(); 213 mOutputY = mCropExtras.getOutputY(); 214 if (mOutputX > 0 && mOutputY > 0) { 215 mCropView.applyAspect(mOutputX, mOutputY); 216 217 } 218 float spotX = mCropExtras.getSpotlightX(); 219 float spotY = mCropExtras.getSpotlightY(); 220 if (spotX > 0 && spotY > 0) { 221 mCropView.setWallpaperSpotlight(spotX, spotY); 222 } 223 if (aspectX > 0 && aspectY > 0) { 224 mCropView.applyAspect(aspectX, aspectY); 225 } 226 } 227 enableSave(true); 228 } else { 229 Log.w(LOGTAG, "could not load image for cropping"); 230 cannotLoadImage(); 231 setResult(RESULT_CANCELED, new Intent()); 232 done(); 233 } 234 } 235 236 /** 237 * Display toast for image loading failure. 238 */ cannotLoadImage()239 private void cannotLoadImage() { 240 CharSequence text = getString(R.string.cannot_load_image); 241 Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT); 242 toast.show(); 243 } 244 245 /** 246 * AsyncTask for loading a bitmap into memory. 247 * 248 * @see #startLoadBitmap(Uri) 249 * @see #doneLoadBitmap(Bitmap) 250 */ 251 private class LoadBitmapTask extends AsyncTask<Uri, Void, Bitmap> { 252 int mBitmapSize; 253 Context mContext; 254 Rect mOriginalBounds; 255 int mOrientation; 256 LoadBitmapTask()257 public LoadBitmapTask() { 258 mBitmapSize = getScreenImageSize(); 259 mContext = getApplicationContext(); 260 mOriginalBounds = new Rect(); 261 mOrientation = 0; 262 } 263 264 @Override doInBackground(Uri... params)265 protected Bitmap doInBackground(Uri... params) { 266 Uri uri = params[0]; 267 Bitmap bmap = ImageLoader.loadConstrainedBitmap(uri, mContext, mBitmapSize, 268 mOriginalBounds, false); 269 mOrientation = ImageLoader.getMetadataRotation(mContext, uri); 270 return bmap; 271 } 272 273 @Override onPostExecute(Bitmap result)274 protected void onPostExecute(Bitmap result) { 275 doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation); 276 } 277 } 278 startFinishOutput()279 protected void startFinishOutput() { 280 if (finalIOGuard) { 281 return; 282 } else { 283 finalIOGuard = true; 284 } 285 enableSave(false); 286 Uri destinationUri = null; 287 int flags = 0; 288 if (mOriginalBitmap != null && mCropExtras != null) { 289 if (mCropExtras.getExtraOutput() != null) { 290 destinationUri = mCropExtras.getExtraOutput(); 291 if (destinationUri != null) { 292 flags |= DO_EXTRA_OUTPUT; 293 } 294 } 295 if (mCropExtras.getSetAsWallpaper()) { 296 flags |= DO_SET_WALLPAPER; 297 } 298 if (mCropExtras.getReturnData()) { 299 flags |= DO_RETURN_DATA; 300 } 301 } 302 if (flags == 0) { 303 destinationUri = SaveImage.makeAndInsertUri(this, mSourceUri); 304 if (destinationUri != null) { 305 flags |= DO_EXTRA_OUTPUT; 306 } 307 } 308 if ((flags & FLAG_CHECK) != 0 && mOriginalBitmap != null) { 309 RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight()); 310 RectF crop = getBitmapCrop(photo); 311 startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop, 312 photo, mOriginalBounds, 313 (mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation); 314 return; 315 } 316 setResult(RESULT_CANCELED, new Intent()); 317 done(); 318 return; 319 } 320 startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri, RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format, int rotation)321 private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri, 322 RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format, 323 int rotation) { 324 if (cropBounds == null || photoBounds == null || currentBitmap == null 325 || currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0 326 || cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0 327 || photoBounds.height() == 0) { 328 return; // fail fast 329 } 330 if ((flags & FLAG_CHECK) == 0) { 331 return; // no output options 332 } 333 if ((flags & DO_SET_WALLPAPER) != 0) { 334 Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show(); 335 } 336 337 final View loading = findViewById(R.id.loading); 338 loading.setVisibility(View.VISIBLE); 339 BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds, 340 photoBounds, currentBitmapBounds, rotation, mOutputX, mOutputY); 341 ioTask.execute(currentBitmap); 342 } 343 doneBitmapIO(boolean success, Intent intent)344 private void doneBitmapIO(boolean success, Intent intent) { 345 final View loading = findViewById(R.id.loading); 346 loading.setVisibility(View.GONE); 347 if (success) { 348 setResult(RESULT_OK, intent); 349 } else { 350 setResult(RESULT_CANCELED, intent); 351 } 352 done(); 353 } 354 355 private class BitmapIOTask extends AsyncTask<Bitmap, Void, Boolean> { 356 357 private final WallpaperManager mWPManager; 358 InputStream mInStream = null; 359 OutputStream mOutStream = null; 360 String mOutputFormat = null; 361 Uri mOutUri = null; 362 Uri mInUri = null; 363 int mFlags = 0; 364 RectF mCrop = null; 365 RectF mPhoto = null; 366 RectF mOrig = null; 367 Intent mResultIntent = null; 368 int mRotation = 0; 369 370 // Helper to setup input stream regenerateInputStream()371 private void regenerateInputStream() { 372 if (mInUri == null) { 373 Log.w(LOGTAG, "cannot read original file, no input URI given"); 374 } else { 375 Utils.closeSilently(mInStream); 376 try { 377 mInStream = getContentResolver().openInputStream(mInUri); 378 } catch (FileNotFoundException e) { 379 Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); 380 } 381 } 382 } 383 BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags, RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation, int outputX, int outputY)384 public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags, 385 RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation, 386 int outputX, int outputY) { 387 mOutputFormat = outputFormat; 388 mOutStream = null; 389 mOutUri = destUri; 390 mInUri = sourceUri; 391 mFlags = flags; 392 mCrop = cropBounds; 393 mPhoto = photoBounds; 394 mOrig = originalBitmapBounds; 395 mWPManager = WallpaperManager.getInstance(getApplicationContext()); 396 mResultIntent = new Intent(); 397 mRotation = (rotation < 0) ? -rotation : rotation; 398 mRotation %= 360; 399 mRotation = 90 * (int) (mRotation / 90); // now mRotation is a multiple of 90 400 mOutputX = outputX; 401 mOutputY = outputY; 402 403 if ((flags & DO_EXTRA_OUTPUT) != 0) { 404 if (mOutUri == null) { 405 Log.w(LOGTAG, "cannot write file, no output URI given"); 406 } else { 407 try { 408 mOutStream = getContentResolver().openOutputStream(mOutUri); 409 } catch (FileNotFoundException e) { 410 Log.w(LOGTAG, "cannot write file: " + mOutUri.toString(), e); 411 } 412 } 413 } 414 415 if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) { 416 regenerateInputStream(); 417 } 418 } 419 420 @Override doInBackground(Bitmap... params)421 protected Boolean doInBackground(Bitmap... params) { 422 boolean failure = false; 423 Bitmap img = params[0]; 424 425 // Set extra for crop bounds 426 if (mCrop != null && mPhoto != null && mOrig != null) { 427 RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); 428 Matrix m = new Matrix(); 429 m.setRotate(mRotation); 430 m.mapRect(trueCrop); 431 if (trueCrop != null) { 432 Rect rounded = new Rect(); 433 trueCrop.roundOut(rounded); 434 mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded); 435 } 436 } 437 438 // Find the small cropped bitmap that is returned in the intent 439 if ((mFlags & DO_RETURN_DATA) != 0) { 440 assert (img != null); 441 Bitmap ret = getCroppedImage(img, mCrop, mPhoto); 442 if (ret != null) { 443 ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT); 444 } 445 if (ret == null) { 446 Log.w(LOGTAG, "could not downsample bitmap to return in data"); 447 failure = true; 448 } else { 449 if (mRotation > 0) { 450 Matrix m = new Matrix(); 451 m.setRotate(mRotation); 452 Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(), 453 ret.getHeight(), m, true); 454 if (tmp != null) { 455 ret = tmp; 456 } 457 } 458 mResultIntent.putExtra(CropExtras.KEY_DATA, ret); 459 } 460 } 461 462 // Do the large cropped bitmap and/or set the wallpaper 463 if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) { 464 // Find crop bounds (scaled to original image size) 465 RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); 466 if (trueCrop == null) { 467 Log.w(LOGTAG, "cannot find crop for full size image"); 468 failure = true; 469 return false; 470 } 471 Rect roundedTrueCrop = new Rect(); 472 trueCrop.roundOut(roundedTrueCrop); 473 474 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 475 Log.w(LOGTAG, "crop has bad values for full size image"); 476 failure = true; 477 return false; 478 } 479 480 // Attempt to open a region decoder 481 BitmapRegionDecoder decoder = null; 482 try { 483 decoder = BitmapRegionDecoder.newInstance(mInStream, true); 484 } catch (IOException e) { 485 Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); 486 } 487 488 Bitmap crop = null; 489 if (decoder != null) { 490 // Do region decoding to get crop bitmap 491 BitmapFactory.Options options = new BitmapFactory.Options(); 492 options.inMutable = true; 493 crop = decoder.decodeRegion(roundedTrueCrop, options); 494 decoder.recycle(); 495 } 496 497 if (crop == null) { 498 // BitmapRegionDecoder has failed, try to crop in-memory 499 regenerateInputStream(); 500 Bitmap fullSize = null; 501 if (mInStream != null) { 502 fullSize = BitmapFactory.decodeStream(mInStream); 503 } 504 if (fullSize != null) { 505 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 506 roundedTrueCrop.top, roundedTrueCrop.width(), 507 roundedTrueCrop.height()); 508 } 509 } 510 511 if (crop == null) { 512 Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); 513 failure = true; 514 return false; 515 } 516 if (mOutputX > 0 && mOutputY > 0) { 517 Matrix m = new Matrix(); 518 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); 519 if (mRotation > 0) { 520 m.setRotate(mRotation); 521 m.mapRect(cropRect); 522 } 523 RectF returnRect = new RectF(0, 0, mOutputX, mOutputY); 524 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 525 m.preRotate(mRotation); 526 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 527 (int) returnRect.height(), Bitmap.Config.ARGB_8888); 528 if (tmp != null) { 529 Canvas c = new Canvas(tmp); 530 c.drawBitmap(crop, m, new Paint()); 531 crop = tmp; 532 } 533 } else if (mRotation > 0) { 534 Matrix m = new Matrix(); 535 m.setRotate(mRotation); 536 Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(), 537 crop.getHeight(), m, true); 538 if (tmp != null) { 539 crop = tmp; 540 } 541 } 542 // Get output compression format 543 CompressFormat cf = 544 convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); 545 546 // If we only need to output to a URI, compress straight to file 547 if (mFlags == DO_EXTRA_OUTPUT) { 548 if (mOutStream == null 549 || !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) { 550 Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString()); 551 failure = true; 552 } else { 553 mResultIntent.setData(mOutUri); 554 } 555 } else { 556 // Compress to byte array 557 ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); 558 if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { 559 560 // If we need to output to a Uri, write compressed 561 // bitmap out 562 if ((mFlags & DO_EXTRA_OUTPUT) != 0) { 563 if (mOutStream == null) { 564 Log.w(LOGTAG, 565 "failed to compress bitmap to file: " + mOutUri.toString()); 566 failure = true; 567 } else { 568 try { 569 mOutStream.write(tmpOut.toByteArray()); 570 mResultIntent.setData(mOutUri); 571 } catch (IOException e) { 572 Log.w(LOGTAG, 573 "failed to compress bitmap to file: " 574 + mOutUri.toString(), e); 575 failure = true; 576 } 577 } 578 } 579 580 // If we need to set to the wallpaper, set it 581 if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) { 582 if (mWPManager == null) { 583 Log.w(LOGTAG, "no wallpaper manager"); 584 failure = true; 585 } else { 586 try { 587 mWPManager.setStream(new ByteArrayInputStream(tmpOut 588 .toByteArray())); 589 } catch (IOException e) { 590 Log.w(LOGTAG, "cannot write stream to wallpaper", e); 591 failure = true; 592 } 593 } 594 } 595 } else { 596 Log.w(LOGTAG, "cannot compress bitmap"); 597 failure = true; 598 } 599 } 600 } 601 return !failure; // True if any of the operations failed 602 } 603 604 @Override onPostExecute(Boolean result)605 protected void onPostExecute(Boolean result) { 606 Utils.closeSilently(mOutStream); 607 Utils.closeSilently(mInStream); 608 doneBitmapIO(result.booleanValue(), mResultIntent); 609 } 610 611 } 612 done()613 private void done() { 614 finish(); 615 } 616 getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds)617 protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) { 618 RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight()); 619 RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds); 620 if (crop == null) { 621 return null; 622 } 623 Rect intCrop = new Rect(); 624 crop.roundOut(intCrop); 625 return Bitmap.createBitmap(image, intCrop.left, intCrop.top, intCrop.width(), 626 intCrop.height()); 627 } 628 getDownsampledBitmap(Bitmap image, int max_size)629 protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) { 630 if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) { 631 throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()"); 632 } 633 int shifts = 0; 634 int size = CropMath.getBitmapSize(image); 635 while (size > max_size) { 636 shifts++; 637 size /= 4; 638 } 639 Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts, 640 image.getHeight() >> shifts, true); 641 if (ret == null) { 642 return null; 643 } 644 // Handle edge case for rounding. 645 if (CropMath.getBitmapSize(ret) > max_size) { 646 return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true); 647 } 648 return ret; 649 } 650 651 /** 652 * Gets the crop extras from the intent, or null if none exist. 653 */ getExtrasFromIntent(Intent intent)654 protected static CropExtras getExtrasFromIntent(Intent intent) { 655 Bundle extras = intent.getExtras(); 656 if (extras != null) { 657 return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), 658 extras.getInt(CropExtras.KEY_OUTPUT_Y, 0), 659 extras.getBoolean(CropExtras.KEY_SCALE, true) && 660 extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false), 661 extras.getInt(CropExtras.KEY_ASPECT_X, 0), 662 extras.getInt(CropExtras.KEY_ASPECT_Y, 0), 663 extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false), 664 extras.getBoolean(CropExtras.KEY_RETURN_DATA, false), 665 (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT), 666 extras.getString(CropExtras.KEY_OUTPUT_FORMAT), 667 extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false), 668 extras.getFloat(CropExtras.KEY_SPOTLIGHT_X), 669 extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y)); 670 } 671 return null; 672 } 673 convertExtensionToCompressFormat(String extension)674 protected static CompressFormat convertExtensionToCompressFormat(String extension) { 675 return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; 676 } 677 getFileExtension(String requestFormat)678 protected static String getFileExtension(String requestFormat) { 679 String outputFormat = (requestFormat == null) 680 ? "jpg" 681 : requestFormat; 682 outputFormat = outputFormat.toLowerCase(); 683 return (outputFormat.equals("png") || outputFormat.equals("gif")) 684 ? "png" // We don't support gif compression. 685 : "jpg"; 686 } 687 getBitmapCrop(RectF imageBounds)688 private RectF getBitmapCrop(RectF imageBounds) { 689 RectF crop = mCropView.getCrop(); 690 RectF photo = mCropView.getPhoto(); 691 if (crop == null || photo == null) { 692 Log.w(LOGTAG, "could not get crop"); 693 return null; 694 } 695 RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds); 696 return scaledCrop; 697 } 698 } 699