Uploaded image for project: 'Apache Cordova'
  1. Apache Cordova
  2. CB-5102

Memory leaks when resizing the photo on WP8

    XMLWordPrintableJSON

Details

    Description

      I was testing the Camera on WP8 and I found memory problems using this code several times (5 to 10):

      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);
      

      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:

      /// <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)
              {
              }
          }
      }
      

      NOTE:

      Attachments

        Activity

          People

            purplecabbage Jesse MacFadyen
            jonathannaguin Jonathan Naguin
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: