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)