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/16 14:13:46 UTC

[jira] [Created] (CB-5102) Memory leaks when resizing the photo on WP8

Jonathan Naguin created CB-5102:
-----------------------------------

             Summary: 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: WP7, WP8
    Affects Versions: 3.1.0
         Environment: Nokia Lumia 925 running WP8
            Reporter: Jonathan Naguin
            Assignee: Jesse MacFadyen
            Priority: Critical


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)