mirror of
https://gitee.com/shuto/cordova-plugin-camera.git
synced 2025-04-22 04:36:23 +08:00
CB-4078: Fix for orientation/scaling on Android 4.4+ devices
The only way to get rotation for photos in library (Gallery, File System, Google Drive,etc) is to first create a temporary file from the provider. Only then can we determine the orientation and scale the bitmap correctly. By doing it in a central place, it eliminates reading the inputstream repetitively in the plugin.
This commit is contained in:
parent
d4a55f20ec
commit
f2b4eeded0
@ -26,13 +26,11 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PermissionHelper;
|
||||
import org.apache.cordova.PluginResult;
|
||||
@ -40,27 +38,30 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.ExifInterface;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PermissionInfo;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
@ -117,6 +118,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
private Uri scanMe; // Uri of image to be added to content store
|
||||
private Uri croppedUri;
|
||||
private ExifHelper exifData; // Exif data from source
|
||||
|
||||
|
||||
/**
|
||||
@ -168,7 +170,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
this.encodingType = JPEG;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
if (this.srcType == CAMERA) {
|
||||
this.callTakePicture(destType, encodingType);
|
||||
}
|
||||
@ -230,8 +232,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
* or to display URI in an img tag
|
||||
* img.src=result;
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param returnType Set the type of image to return.
|
||||
* @param encodingType JPEG or PNG
|
||||
*/
|
||||
public void callTakePicture(int returnType, int encodingType) {
|
||||
boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
@ -339,7 +341,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param srcType The album to get image from.
|
||||
* @param returnType Set the type of image to return.
|
||||
* @param encodingType
|
||||
@ -373,17 +374,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
}
|
||||
} else if (this.mediaType == VIDEO) {
|
||||
intent.setType("video/*");
|
||||
title = GET_VIDEO;
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("video/*");
|
||||
title = GET_VIDEO;
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
} else if (this.mediaType == ALLMEDIA) {
|
||||
// I wanted to make the type 'image/*, video/*' but this does not work on all versions
|
||||
// of android so I had to go with the wildcard search.
|
||||
intent.setType("*/*");
|
||||
title = GET_All;
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
// I wanted to make the type 'image/*, video/*' but this does not work on all versions
|
||||
// of android so I had to go with the wildcard search.
|
||||
intent.setType("*/*");
|
||||
title = GET_All;
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
}
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||
@ -391,52 +392,52 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Brings up the UI to perform crop on passed image URI
|
||||
*
|
||||
* @param picUri
|
||||
*/
|
||||
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
|
||||
try {
|
||||
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||
// indicate image type and Uri
|
||||
cropIntent.setDataAndType(picUri, "image/*");
|
||||
// set crop properties
|
||||
cropIntent.putExtra("crop", "true");
|
||||
/**
|
||||
* Brings up the UI to perform crop on passed image URI
|
||||
*
|
||||
* @param picUri
|
||||
*/
|
||||
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
|
||||
try {
|
||||
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||
// indicate image type and Uri
|
||||
cropIntent.setDataAndType(picUri, "image/*");
|
||||
// set crop properties
|
||||
cropIntent.putExtra("crop", "true");
|
||||
|
||||
// indicate output X and Y
|
||||
if (targetWidth > 0) {
|
||||
cropIntent.putExtra("outputX", targetWidth);
|
||||
}
|
||||
if (targetHeight > 0) {
|
||||
cropIntent.putExtra("outputY", targetHeight);
|
||||
}
|
||||
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
||||
cropIntent.putExtra("aspectX", 1);
|
||||
cropIntent.putExtra("aspectY", 1);
|
||||
}
|
||||
// create new file handle to get full resolution crop
|
||||
croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
||||
cropIntent.putExtra("output", croppedUri);
|
||||
// indicate output X and Y
|
||||
if (targetWidth > 0) {
|
||||
cropIntent.putExtra("outputX", targetWidth);
|
||||
}
|
||||
if (targetHeight > 0) {
|
||||
cropIntent.putExtra("outputY", targetHeight);
|
||||
}
|
||||
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
||||
cropIntent.putExtra("aspectX", 1);
|
||||
cropIntent.putExtra("aspectY", 1);
|
||||
}
|
||||
// create new file handle to get full resolution crop
|
||||
croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
||||
cropIntent.putExtra("output", croppedUri);
|
||||
|
||||
// start the activity - we handle returning in onActivityResult
|
||||
// start the activity - we handle returning in onActivityResult
|
||||
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this,
|
||||
cropIntent, CROP_CAMERA + destType);
|
||||
}
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.e(LOG_TAG, "Crop operation not supported on this device");
|
||||
try {
|
||||
processResultFromCamera(destType, cameraIntent);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, "Unable to write to file");
|
||||
}
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this,
|
||||
cropIntent, CROP_CAMERA + destType);
|
||||
}
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.e(LOG_TAG, "Crop operation not supported on this device");
|
||||
try {
|
||||
processResultFromCamera(destType, cameraIntent);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e(LOG_TAG, "Unable to write to file");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all needed transformation to the image received from the camera.
|
||||
@ -450,8 +451,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
String sourcePath = (this.allowEdit && this.croppedUri != null) ?
|
||||
FileHelper.stripFileProtocol(this.croppedUri.toString()) :
|
||||
FileHelper.stripFileProtocol(this.imageUri.toString());
|
||||
FileHelper.stripFileProtocol(this.croppedUri.toString()) :
|
||||
FileHelper.stripFileProtocol(this.imageUri.toString());
|
||||
|
||||
if (this.encodingType == JPEG) {
|
||||
try {
|
||||
@ -472,7 +473,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
// in the gallery and the modified image is saved in the temporary
|
||||
// directory
|
||||
if (this.saveToPhotoAlbum) {
|
||||
galleryUri = Uri.fromFile(new File(getPicutresPath()));
|
||||
galleryUri = Uri.fromFile(new File(getPicturesPath()));
|
||||
|
||||
if(this.allowEdit && this.croppedUri != null) {
|
||||
writeUncompressedImage(this.croppedUri, galleryUri);
|
||||
@ -485,7 +486,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
bitmap = getScaledBitmap(sourcePath);
|
||||
bitmap = getScaledAndRotatedBitmap(sourcePath);
|
||||
|
||||
if (bitmap == null) {
|
||||
// Try to get the bitmap from intent.
|
||||
@ -499,9 +500,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
this.processPicture(bitmap, this.encodingType);
|
||||
|
||||
@ -533,7 +531,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
}
|
||||
} else {
|
||||
Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
||||
bitmap = getScaledBitmap(sourcePath);
|
||||
bitmap = getScaledAndRotatedBitmap(sourcePath);
|
||||
|
||||
// Double-check the bitmap.
|
||||
if (bitmap == null) {
|
||||
@ -542,9 +540,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
@ -575,33 +570,28 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
||||
bitmap = null;
|
||||
}
|
||||
|
||||
private String getPicutresPath()
|
||||
{
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
|
||||
return galleryPath;
|
||||
}
|
||||
private String getPicturesPath()
|
||||
{
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
|
||||
return galleryPath;
|
||||
}
|
||||
|
||||
private void refreshGallery(Uri contentUri)
|
||||
{
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(contentUri);
|
||||
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
private void refreshGallery(Uri contentUri)
|
||||
{
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(contentUri);
|
||||
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
|
||||
|
||||
private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||
|
||||
// Get filename from uri
|
||||
String fileName = realPath != null ?
|
||||
realPath.substring(realPath.lastIndexOf('/') + 1) :
|
||||
"modified." + (this.encodingType == JPEG ? "jpg" : "png");
|
||||
private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
|
||||
|
||||
OutputStream os = new FileOutputStream(modifiedPath);
|
||||
@ -612,18 +602,14 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
bitmap.compress(compressFormat, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
if (realPath != null && this.encodingType == JPEG) {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
|
||||
if (exifData != null && this.encodingType == JPEG) {
|
||||
try {
|
||||
exif.createInFile(realPath);
|
||||
exif.readExifData();
|
||||
if (this.correctOrientation && this.orientationCorrected) {
|
||||
exif.resetOrientation();
|
||||
exifData.resetOrientation();
|
||||
}
|
||||
exif.createOutFile(modifiedPath);
|
||||
exif.writeExifData();
|
||||
exifData.createOutFile(modifiedPath);
|
||||
exifData.writeExifData();
|
||||
exifData = null;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -633,7 +619,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Applies all needed transformation to the image received from the gallery.
|
||||
*
|
||||
* @param destType In which form should we return the image
|
||||
@ -677,7 +663,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
}
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = getScaledBitmap(uriString);
|
||||
bitmap = getScaledAndRotatedBitmap(uriString);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -687,20 +673,6 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.correctOrientation) {
|
||||
rotate = getImageOrientation(uri);
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
try {
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
this.orientationCorrected = true;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
this.orientationCorrected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap, this.encodingType);
|
||||
@ -712,7 +684,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
|
||||
(this.correctOrientation && this.orientationCorrected) ) {
|
||||
try {
|
||||
String modifiedPath = this.ouputModifiedBitmap(bitmap, uri);
|
||||
String modifiedPath = this.outputModifiedBitmap(bitmap, uri);
|
||||
// The modified image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
|
||||
@ -822,69 +794,19 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
private int getImageOrientation(Uri uri) {
|
||||
int rotate = 0;
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
try {
|
||||
Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
|
||||
cols, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// You can get an IllegalArgumentException if ContentProvider doesn't support querying for orientation.
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
|
||||
* portrait mode
|
||||
* Write an inputstream to local disk
|
||||
*
|
||||
* @param rotate
|
||||
* @param bitmap
|
||||
* @return rotated bitmap
|
||||
*/
|
||||
private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
|
||||
Matrix matrix = new Matrix();
|
||||
if (rotate == 180) {
|
||||
matrix.setRotate(rotate);
|
||||
} else {
|
||||
matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
exif.resetOrientation();
|
||||
}
|
||||
catch (OutOfMemoryError oom)
|
||||
{
|
||||
// You can run out of memory if the image is very large:
|
||||
// http://simonmacdonald.blogspot.ca/2012/07/change-to-camera-code-in-phonegap-190.html
|
||||
// If this happens, simply do not rotate the image and return it unmodified.
|
||||
// If you do not catch the OutOfMemoryError, the Android app crashes.
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the special case where the default width, height and quality are unchanged
|
||||
* we just write the file out to disk saving the expensive Bitmap.compress function.
|
||||
*
|
||||
* @param uri
|
||||
* @param fis - The InputStream to write
|
||||
* @param dest - Destination on disk to write to
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundException,
|
||||
private void writeUncompressedImage(InputStream fis, Uri dest) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileInputStream fis = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));
|
||||
os = this.cordova.getActivity().getContentResolver().openOutputStream(dest);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
@ -909,6 +831,21 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* In the special case where the default width, height and quality are unchanged
|
||||
* we just write the file out to disk saving the expensive Bitmap.compress function.
|
||||
*
|
||||
* @param src
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundException,
|
||||
IOException {
|
||||
|
||||
FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));
|
||||
writeUncompressedImage(fis, dest);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create entry in media store for image
|
||||
@ -934,15 +871,15 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a scaled bitmap based on the target width and height
|
||||
* Return a scaled and rotated bitmap based on the target width and height
|
||||
*
|
||||
* @param imagePath
|
||||
* @param imageUrl
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
|
||||
// If no new width or height were specified, and orientation is not needed return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0 && !(this.correctOrientation)) {
|
||||
InputStream fileStream = null;
|
||||
Bitmap image = null;
|
||||
try {
|
||||
@ -960,53 +897,147 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
return image;
|
||||
}
|
||||
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
InputStream fileStream = null;
|
||||
|
||||
/* Copy the inputstream to a temporary file on the device.
|
||||
We then use this temporary file to determine the width/height/orientation.
|
||||
This is the only way to determine the orientation of the photo coming from 3rd party providers (Google Drive, Dropbox,etc)
|
||||
This also ensures we create a scaled bitmap with the correct orientation
|
||||
|
||||
We delete the temporary file once we are done
|
||||
*/
|
||||
File localFile = null;
|
||||
Uri galleryUri = null;
|
||||
int rotate = 0;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
InputStream fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
if (fileStream != null) {
|
||||
// Generate a temporary file
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||
localFile = new File(getTempDirectoryPath() + fileName);
|
||||
galleryUri = Uri.fromFile(localFile);
|
||||
writeUncompressedImage(fileStream, galleryUri);
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
String mimeType = FileHelper.getMimeType(imageUrl.toString(), cordova);
|
||||
if ("image/jpeg".equalsIgnoreCase(mimeType)) {
|
||||
// ExifInterface doesn't like the file:// prefix
|
||||
String filePath = galleryUri.toString().replace("file://", "");
|
||||
// read exifData of source
|
||||
exifData = new ExifHelper();
|
||||
exifData.createInFile(filePath);
|
||||
// Use ExifInterface to pull rotation information
|
||||
if (this.correctOrientation) {
|
||||
ExifInterface exif = new ExifInterface(filePath);
|
||||
rotate = exifToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED));
|
||||
}
|
||||
}
|
||||
} catch (Exception oe) {
|
||||
LOG.w(LOG_TAG,"Unable to read Exif data: "+ oe.toString());
|
||||
rotate = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CB-2292: WTF? Why is the width null?
|
||||
if(options.outWidth == 0 || options.outHeight == 0)
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.e(LOG_TAG,"Exception while getting input stream: "+ e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = null;
|
||||
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||
unscaledBitmap = BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
InputStream fileStream = null;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(galleryUri.toString(), cordova);
|
||||
BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG, "Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//CB-2292: WTF? Why is the width null?
|
||||
if (options.outWidth == 0 || options.outHeight == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// User didn't specify output dimensions, but they need orientation
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
this.targetWidth = options.outWidth;
|
||||
this.targetHeight = options.outHeight;
|
||||
}
|
||||
|
||||
// Setup target width/height based on orientation
|
||||
int rotatedWidth, rotatedHeight;
|
||||
boolean rotated= false;
|
||||
if (rotate == 90 || rotate == 270) {
|
||||
rotatedWidth = options.outHeight;
|
||||
rotatedHeight = options.outWidth;
|
||||
rotated = true;
|
||||
} else {
|
||||
rotatedWidth = options.outWidth;
|
||||
rotatedHeight = options.outHeight;
|
||||
}
|
||||
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(rotatedWidth, rotatedHeight);
|
||||
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(rotatedWidth, rotatedHeight, widthHeight[0], widthHeight[1]);
|
||||
Bitmap unscaledBitmap = null;
|
||||
try {
|
||||
fileStream = FileHelper.getInputStreamFromUriString(galleryUri.toString(), cordova);
|
||||
unscaledBitmap = BitmapFactory.decodeStream(fileStream, null, options);
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
LOG.d(LOG_TAG, "Exception while closing file input stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int scaledWidth = (!rotated) ? widthHeight[0] : widthHeight[1];
|
||||
int scaledHeight = (!rotated) ? widthHeight[1] : widthHeight[0];
|
||||
|
||||
Bitmap scaledBitmap = Bitmap.createScaledBitmap(unscaledBitmap, scaledWidth, scaledHeight, true);
|
||||
if (scaledBitmap != unscaledBitmap) {
|
||||
unscaledBitmap.recycle();
|
||||
unscaledBitmap = null;
|
||||
}
|
||||
if (this.correctOrientation && (rotate != 0)) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
try {
|
||||
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
|
||||
this.orientationCorrected = true;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
this.orientationCorrected = false;
|
||||
}
|
||||
}
|
||||
return scaledBitmap;
|
||||
}
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
finally {
|
||||
// delete the temporary copy
|
||||
if (localFile != null) {
|
||||
localFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1027,11 +1058,11 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
}
|
||||
// Only the width was specified
|
||||
else if (newWidth > 0 && newHeight <= 0) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
newHeight = (int)((double)(newWidth / (double)origWidth) * origHeight);
|
||||
}
|
||||
// only the height was specified
|
||||
else if (newWidth <= 0 && newHeight > 0) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
newWidth = (int)((double)(newHeight / (double)origHeight) * origWidth);
|
||||
}
|
||||
// If the user specified both a positive width and height
|
||||
// (potentially different aspect ratio) then the width or height is
|
||||
@ -1075,7 +1106,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
} else {
|
||||
return srcHeight / dstHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor that can be used to determine how many images we have.
|
||||
@ -1288,4 +1319,4 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user