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