You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cordova.apache.org by "Jonathan Naguin (JIRA)" <ji...@apache.org> on 2013/10/28 12:22:32 UTC
[jira] [Comment Edited] (CB-5102) Memory leaks when resizing the
photo on WP8
[ https://issues.apache.org/jira/browse/CB-5102?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13805347#comment-13805347 ]
Jonathan Naguin edited comment on CB-5102 at 10/28/13 11:21 AM:
----------------------------------------------------------------
It seems that saving to disk has memory leaks too, so I have rewritten the code of "SaveImageToLocalStorage" as well. Notice that now it accepts a Stream. Also I removed the code related with the rotation...
{code}
/// <summary>
/// Saves captured image in isolated storage
/// </summary>
/// <param name="imageFileName">image file name</param>
/// <returns>Image path</returns>
private string SaveImageToLocalStorage(Stream stream, string imageFileName)
{
if (stream == null)
{
throw new ArgumentNullException("imageBytes");
}
try
{
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
if (!isoFile.DirectoryExists(isoFolder))
{
isoFile.CreateDirectory(isoFolder);
}
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(filePath))
{
//use photo's actual width & height if user doesn't provide width & height
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
{
BitmapImage objBitmap = new BitmapImage();
objBitmap.SetSource(stream);
objBitmap.CreateOptions = BitmapCreateOptions.None;
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
objBitmap.UriSource = null;
try
{
objWB.SaveJpeg(outputStream, objWB.PixelWidth, objWB.PixelHeight, 0, cameraOptions.Quality);
}
finally
{
//Dispose bitmaps immediately, they are memory expensive
DisposeImage(objBitmap);
DisposeImage(objWB);
GC.Collect();
}
}
else
{
//Resize
BitmapImage objBitmap = new BitmapImage();
objBitmap.SetSource(stream);
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);
try
{
// 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();
}
}
}
return new Uri(filePath, UriKind.Relative).ToString();
}
catch (Exception)
{
//TODO: log or do something else
throw;
}
finally
{
stream.Dispose();
}
}
{code}
was (Author: jonathannaguin):
It seems that saving to disk has memory leaks too, so I have rewritten the code of "SaveImageToLocalStorage" as well. Notice that now it accepts a Stream. Also I removed the code related with the rotation...
{code}
/// <summary>
/// Saves captured image in isolated storage
/// </summary>
/// <param name="imageFileName">image file name</param>
/// <returns>Image path</returns>
private string SaveImageToLocalStorage(Stream stream, string imageFileName)
{
if (stream == null)
{
throw new ArgumentNullException("imageBytes");
}
try
{
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
if (!isoFile.DirectoryExists(isoFolder))
{
isoFile.CreateDirectory(isoFolder);
}
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(filePath))
{
//use photo's actual width & height if user doesn't provide width & height
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
{
BitmapImage objBitmap = new BitmapImage();
objBitmap.SetSource(stream);
objBitmap.CreateOptions = BitmapCreateOptions.None;
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
objBitmap.UriSource = null;
try
{
objWB.SaveJpeg(outputStream, objWB.PixelWidth, objWB.PixelHeight, 0, cameraOptions.Quality);
}
finally
{
//Dispose bitmaps immediately, they are memory expensive
DisposeImage(objBitmap);
DisposeImage(objWB);
GC.Collect();
}
}
else
{
//Resize
BitmapImage objBitmap = new BitmapImage();
objBitmap.SetSource(stream);
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);
try
{
// 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();
}
}
}
return new Uri(filePath, UriKind.Relative).ToString();
}
catch (Exception)
{
//TODO: log or do something else
throw;
}
}
{code}
> Memory leaks when resizing the photo on WP8
> -------------------------------------------
>
> Key: CB-5102
> URL: https://issues.apache.org/jira/browse/CB-5102
> Project: Apache Cordova
> Issue Type: Bug
> Components: Plugin Camera, WP7, WP8
> Affects Versions: 3.1.0
> Environment: Nokia Lumia 925 running WP8
> Reporter: Jonathan Naguin
> Assignee: Jesse MacFadyen
> Priority: Critical
> Labels: camera, memory-leak, wp7, wp8
>
> I was testing the Camera on WP8 and I found memory problems using this code several times (5 to 10):
> {code}
> var _options = {
> quality: 50,
> destinationType: Camera.DestinationType.DATA_URL,
> sourceType: Camera.PictureSourceType.CAMERA,
> allowEdit: false,
> encodingType: Camera.EncodingType.PNG,
> saveToPhotoAlbum: false,
> correctOrientation: true,
> targetWidth: 1024,
> targetHeight: 768
> };
> navigator.camera.getPicture(cameraSuccess, cameraError, _options);
> {code}
> I realized the problem is on "GetImageContent" and "ResizePhoto". It contains several problems disposing Streams and Bitmaps.
> This is my re-implementation, which I tested and it works fine so far:
> {code}
> /// <summary>
> /// Returns image content in a form of base64 string
> /// </summary>
> /// <param name="stream">Image stream</param>
> /// <returns>Base64 representation of the image</returns>
> private string GetImageContent(Stream stream)
> {
> byte[] imageContent = null;
> try
> {
> //use photo's actual width & height if user doesn't provide width & height
> if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
> {
> int streamLength = (int)stream.Length;
> imageContent = new byte[streamLength + 1];
> stream.Read(imageContent, 0, streamLength);
> }
> else
> {
> // resize photo
> imageContent = ResizePhoto(stream);
> }
> }
> finally
> {
> stream.Dispose();
> }
> return Convert.ToBase64String(imageContent);
> }
> /// <summary>
> /// Resize image
> /// </summary>
> /// <param name="stream">Image stream</param>
> /// <returns>resized image</returns>
> private byte[] ResizePhoto(Stream stream)
> {
> //output
> byte[] resizedFile;
> BitmapImage objBitmap = new BitmapImage();
> objBitmap.SetSource(stream);
> 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
> 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.
> int streamLength = (int)objBitmapStreamResized.Length;
> resizedFile = new Byte[streamLength]; //-1
> 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...
> objBitmapStreamResized.Read(resizedFile, 0, streamLength);
> }
> 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)
> {
> }
> }
> }
> {code}
> NOTE:
> - It contains a solution for: https://issues.apache.org/jira/browse/CB-2737
--
This message was sent by Atlassian JIRA
(v6.1#6144)