mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2025-01-19 03:42:52 +08:00
Fix camera issues, cropping, memory leaks CB-4027, CB-5102, CB-2737, CB-2387
This commit is contained in:
parent
91d6e10b29
commit
e7a3d70fe9
198
src/wp/Camera.cs
198
src/wp/Camera.cs
@ -113,8 +113,6 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
||||||
public bool CorrectOrientation { get; set; }
|
public bool CorrectOrientation { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ignored
|
/// Ignored
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -176,16 +174,6 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to open photo library
|
|
||||||
/// </summary>
|
|
||||||
PhotoChooserTask photoChooserTask;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to open camera application
|
|
||||||
/// </summary>
|
|
||||||
CameraCaptureTask cameraTask;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Camera options
|
/// Camera options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -198,20 +186,17 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
||||||
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
||||||
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
||||||
this.cameraOptions = new CameraOptions();
|
cameraOptions = new CameraOptions();
|
||||||
this.cameraOptions.Quality = int.Parse(args[0]);
|
cameraOptions.Quality = int.Parse(args[0]);
|
||||||
this.cameraOptions.DestinationType = int.Parse(args[1]);
|
cameraOptions.DestinationType = int.Parse(args[1]);
|
||||||
this.cameraOptions.PictureSourceType = int.Parse(args[2]);
|
cameraOptions.PictureSourceType = int.Parse(args[2]);
|
||||||
this.cameraOptions.TargetWidth = int.Parse(args[3]);
|
cameraOptions.TargetWidth = int.Parse(args[3]);
|
||||||
this.cameraOptions.TargetHeight = int.Parse(args[4]);
|
cameraOptions.TargetHeight = int.Parse(args[4]);
|
||||||
this.cameraOptions.EncodingType = int.Parse(args[5]);
|
cameraOptions.EncodingType = int.Parse(args[5]);
|
||||||
this.cameraOptions.MediaType = int.Parse(args[6]);
|
cameraOptions.MediaType = int.Parse(args[6]);
|
||||||
this.cameraOptions.AllowEdit = bool.Parse(args[7]);
|
cameraOptions.AllowEdit = bool.Parse(args[7]);
|
||||||
this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
||||||
this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
||||||
|
|
||||||
//this.cameraOptions = String.IsNullOrEmpty(options) ?
|
|
||||||
// new CameraOptions() : JSON.JsonHelper.Deserialize<CameraOptions>(options);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -224,15 +209,13 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
if (cameraOptions.PictureSourceType == CAMERA)
|
if (cameraOptions.PictureSourceType == CAMERA)
|
||||||
{
|
{
|
||||||
cameraTask = new CameraCaptureTask();
|
CameraCaptureTask cameraTask = new CameraCaptureTask();
|
||||||
cameraTask.Completed += onCameraTaskCompleted;
|
cameraTask.Completed += onCameraTaskCompleted;
|
||||||
cameraTask.Show();
|
cameraTask.Show();
|
||||||
}
|
}
|
||||||
else
|
else if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
||||||
{
|
{
|
||||||
if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
PhotoChooserTask photoChooserTask = new PhotoChooserTask();
|
||||||
{
|
|
||||||
photoChooserTask = new PhotoChooserTask();
|
|
||||||
photoChooserTask.Completed += onPickerTaskCompleted;
|
photoChooserTask.Completed += onPickerTaskCompleted;
|
||||||
photoChooserTask.Show();
|
photoChooserTask.Show();
|
||||||
}
|
}
|
||||||
@ -240,12 +223,18 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
{
|
{
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCameraTaskCompleted(object sender, PhotoResult e)
|
public void onCameraTaskCompleted(object sender, PhotoResult e)
|
||||||
{
|
{
|
||||||
|
var task = sender as ChooserBase<PhotoResult>;
|
||||||
|
if (task != null)
|
||||||
|
{
|
||||||
|
task.Completed -= onCameraTaskCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.Error != null)
|
if (e.Error != null)
|
||||||
{
|
{
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
||||||
@ -289,10 +278,7 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
// we should return stream position back after saving stream to media library
|
// we should return stream position back after saving stream to media library
|
||||||
rotImageStream.Seek(0, SeekOrigin.Begin);
|
rotImageStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
imagePathOrContent = this.SaveImageToLocalStorage(rotImageStream, Path.GetFileName(e.OriginalFileName));
|
||||||
WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
|
|
||||||
|
|
||||||
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -329,6 +315,12 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
public void onPickerTaskCompleted(object sender, PhotoResult e)
|
public void onPickerTaskCompleted(object sender, PhotoResult e)
|
||||||
{
|
{
|
||||||
|
var task = sender as ChooserBase<PhotoResult>;
|
||||||
|
if (task != null)
|
||||||
|
{
|
||||||
|
task.Completed -= onCameraTaskCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.Error != null)
|
if (e.Error != null)
|
||||||
{
|
{
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
||||||
@ -344,8 +336,7 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
if (cameraOptions.DestinationType == FILE_URI)
|
if (cameraOptions.DestinationType == FILE_URI)
|
||||||
{
|
{
|
||||||
WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
|
imagePathOrContent = this.SaveImageToLocalStorage(e.ChosenPhoto, Path.GetFileName(e.OriginalFileName));
|
||||||
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
|
||||||
}
|
}
|
||||||
else if (cameraOptions.DestinationType == DATA_URL)
|
else if (cameraOptions.DestinationType == DATA_URL)
|
||||||
{
|
{
|
||||||
@ -385,24 +376,30 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
/// <returns>Base64 representation of the image</returns>
|
/// <returns>Base64 representation of the image</returns>
|
||||||
private string GetImageContent(Stream stream)
|
private string GetImageContent(Stream stream)
|
||||||
{
|
{
|
||||||
int streamLength = (int)stream.Length;
|
byte[] imageContent = null;
|
||||||
byte[] fileData = new byte[streamLength + 1];
|
|
||||||
stream.Read(fileData, 0, streamLength);
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
//use photo's actual width & height if user doesn't provide width & height
|
//use photo's actual width & height if user doesn't provide width & height
|
||||||
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
||||||
{
|
{
|
||||||
stream.Close();
|
int streamLength = (int)stream.Length;
|
||||||
return Convert.ToBase64String(fileData);
|
imageContent = new byte[streamLength + 1];
|
||||||
|
stream.Read(imageContent, 0, streamLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// resize photo
|
// resize photo
|
||||||
byte[] resizedFile = ResizePhoto(stream, fileData);
|
imageContent = ResizePhoto(stream);
|
||||||
stream.Close();
|
|
||||||
return Convert.ToBase64String(resizedFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToBase64String(imageContent);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resize image
|
/// Resize image
|
||||||
@ -410,51 +407,87 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
/// <param name="stream">Image stream</param>
|
/// <param name="stream">Image stream</param>
|
||||||
/// <param name="fileData">File data</param>
|
/// <param name="fileData">File data</param>
|
||||||
/// <returns>resized image</returns>
|
/// <returns>resized image</returns>
|
||||||
private byte[] ResizePhoto(Stream stream, byte[] fileData)
|
private byte[] ResizePhoto(Stream stream)
|
||||||
{
|
{
|
||||||
int streamLength = (int)stream.Length;
|
//output
|
||||||
int intResult = 0;
|
|
||||||
|
|
||||||
byte[] resizedFile;
|
byte[] resizedFile;
|
||||||
|
|
||||||
stream.Read(fileData, 0, streamLength);
|
|
||||||
|
|
||||||
BitmapImage objBitmap = new BitmapImage();
|
BitmapImage objBitmap = new BitmapImage();
|
||||||
MemoryStream objBitmapStream = new MemoryStream(fileData);
|
|
||||||
MemoryStream objBitmapStreamResized = new MemoryStream();
|
|
||||||
WriteableBitmap objWB;
|
|
||||||
objBitmap.SetSource(stream);
|
objBitmap.SetSource(stream);
|
||||||
objWB = new WriteableBitmap(objBitmap);
|
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
||||||
|
|
||||||
|
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
||||||
|
objBitmap.UriSource = null;
|
||||||
|
|
||||||
|
//Keep proportionally
|
||||||
|
double ratio = Math.Min((double)cameraOptions.TargetWidth / objWB.PixelWidth, (double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
||||||
|
int width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
||||||
|
int height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
||||||
|
|
||||||
|
//Hold the result stream
|
||||||
|
using (MemoryStream objBitmapStreamResized = new MemoryStream())
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// resize the photo with user defined TargetWidth & TargetHeight
|
// resize the photo with user defined TargetWidth & TargetHeight
|
||||||
Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
Extensions.SaveJpeg(objWB, objBitmapStreamResized, width, height, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//Dispose bitmaps immediately, they are memory expensive
|
||||||
|
DisposeImage(objBitmap);
|
||||||
|
DisposeImage(objWB);
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
//Convert the resized stream to a byte array.
|
//Convert the resized stream to a byte array.
|
||||||
streamLength = (int)objBitmapStreamResized.Length;
|
int streamLength = (int)objBitmapStreamResized.Length;
|
||||||
resizedFile = new Byte[streamLength]; //-1
|
resizedFile = new Byte[streamLength]; //-1
|
||||||
objBitmapStreamResized.Position = 0;
|
objBitmapStreamResized.Position = 0;
|
||||||
|
|
||||||
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
||||||
intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
||||||
|
}
|
||||||
|
|
||||||
return resizedFile;
|
return resizedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Util: Dispose a bitmap resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">BitmapSource subclass to dispose</param>
|
||||||
|
private void DisposeImage(BitmapSource image)
|
||||||
|
{
|
||||||
|
if (image != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var ms = new MemoryStream(new byte[] { 0x0 }))
|
||||||
|
{
|
||||||
|
image.SetSource(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves captured image in isolated storage
|
/// Saves captured image in isolated storage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="imageFileName">image file name</param>
|
/// <param name="imageFileName">image file name</param>
|
||||||
/// <returns>Image path</returns>
|
/// <returns>Image path</returns>
|
||||||
private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
|
private string SaveImageToLocalStorage(Stream stream, string imageFileName)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (image == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("imageBytes");
|
throw new ArgumentNullException("imageBytes");
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
||||||
|
|
||||||
if (!isoFile.DirectoryExists(isoFolder))
|
if (!isoFile.DirectoryExists(isoFolder))
|
||||||
@ -464,16 +497,41 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
||||||
|
|
||||||
using (var stream = isoFile.CreateFile(filePath))
|
using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(filePath))
|
||||||
{
|
{
|
||||||
// resize image if Height and Width defined via options
|
BitmapImage objBitmap = new BitmapImage();
|
||||||
if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
|
objBitmap.SetSource(stream);
|
||||||
|
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
||||||
|
|
||||||
|
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
||||||
|
objBitmap.UriSource = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
|
||||||
|
//use photo's actual width & height if user doesn't provide width & height
|
||||||
|
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
||||||
|
{
|
||||||
|
objWB.SaveJpeg(outputStream, objWB.PixelWidth, objWB.PixelHeight, 0, cameraOptions.Quality);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
|
//Resize
|
||||||
|
//Keep proportionally
|
||||||
|
double ratio = Math.Min((double)cameraOptions.TargetWidth / objWB.PixelWidth, (double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
||||||
|
int width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
||||||
|
int height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
||||||
|
|
||||||
|
// resize the photo with user defined TargetWidth & TargetHeight
|
||||||
|
objWB.SaveJpeg(outputStream, width, height, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//Dispose bitmaps immediately, they are memory expensive
|
||||||
|
DisposeImage(objBitmap);
|
||||||
|
DisposeImage(objWB);
|
||||||
|
GC.Collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +542,10 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
//TODO: log or do something else
|
//TODO: log or do something else
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stream.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user