Details
-
Bug
-
Status: Closed
-
Critical
-
Resolution: Fixed
-
3.1.0
-
Nokia Lumia 925 running WP8
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:
- It contains a solution for: https://issues.apache.org/jira/browse/CB-2737