S
S
SUDALV2014-12-03 14:49:09
Windows phone
SUDALV, 2014-12-03 14:49:09

How to avoid memory overflow due to WriteableBitmap operations?

There is a page in the Windows Phone 8.1 app that initially contains almost nothing:

<Page.BottomAppBar>
        <CommandBar>
            <AppBarButton Icon="Add" x:Uid="AddPhotoAppButton" Click="AddPhotoClick" IsEnabled="{Binding IsButtonsEnabled}"/>
            <AppBarButton Icon="Send" x:Uid="SendAppButton" Click="SendClick" IsEnabled="{Binding IsButtonsEnabled}"/>
        </CommandBar>
    </Page.BottomAppBar>
    <ScrollViewer>
        <Grid>
                 <Grid Grid.Row="2">
                <TextBox />
            </Grid>
            <GridView Grid.Row="3" ItemsSource="{Binding Attachments}">
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Holding="ListViewItem_Holding">
                            <Image Margin="10" Height="90" Width="90" Source="{Binding Image}"/>
                        </StackPanel>
                    </DataTemplate>
                </GridView.ItemTemplate>
            </GridView>
        </Grid>
    </ScrollViewer>

(To simplify reading, I cut out insignificant tags and parameters)
When you click on the button in the app bar, the user can select any picture from the phone's gallery, after which it is immediately uploaded to a special server (the client receives the id of the picture from the server), and the body of the picture is written to ObservableCollection<FilePost>();
where
public class FilePost
    {
        public Guid ID { get; set; }
        public WriteableBitmap Image { get; set; }
    }

This collection is associated with a GridView on the page and a preview of all the images displayed on the page (Image tag).
If the picture is, say, a 5 mp photo, then on a phone with 512 MB of RAM, after 3-4 downloads of such pictures, the application crashes with an OutOfMemoryException.
The logic of working with the file is as follows:
1. Using the FileOpenPicker we get a pointer to the StorageFile file.
2. Use the file.OpenAsync() method to open the stream.
3. Create a 1x1 WriteableBitmap (it does not have an empty constructor or other ways to create it).
4. We read the picture from the stream using the extension method from the WriteableBitmapEx library WriteableBitmap.FromStream() - at this point, memory consumption increases by about 40-50 MB (!)
5. After that, we need to reduce the image to 1920 on the larger side, for which we use the WriteableBitmap.Resize () extension method from the same WriteableBitmapEx library. At this point, a lot more memory is being consumed up to OutOfMemoryException.
Further logic can be seen in the code below. How can this process be optimized? It is necessary that at least 10 photos can be selected and displayed without problems (for example, in the iOS version of the same application, you can freely select at least 50 photos even on the iPhone 4s, which has 512 MB of RAM).
WriteableBitmap wbmp;
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
  wbmp = new WriteableBitmap(1, 1);
        wbmp = await wbmp.FromStream(stream);
}

var ratio = wbmp.PixelWidth > wbmp.PixelHeight ? 1920.0f / wbmp.PixelWidth : 1920.0f / wbmp.PixelHeight;
if (ratio < 1)
{
  var newWidth = (int)(wbmp.PixelWidth * ratio);
        var newHeight = (int)(wbmp.PixelHeight * ratio);
        wbmp = wbmp.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);

}

using (IRandomAccessStream iras = new InMemoryRandomAccessStream())
{
  await wbmp.ToStreamAsJpeg(iras);
        var bytes = new byte[iras.Size];
        var buffer = await iras.ReadAsync(bytes.AsBuffer(), (uint)iras.Size, InputStreamOptions.None);
        var response = await Uploader.UploadData(GetString("ServerApi") + "File?tokenID=" + GetToken(), buffer.ToArray());
        if (response.Item != Guid.Empty)
        	Attachments.Add(new FilePost { ID = Guid.Empty, WImage = wbmp });
}

Answer the question

In order to leave comments, you need to log in

2 answer(s)
V
Vitaly Pukhov, 2014-12-04
@Neuroware

for these things there are thumbnails - reduced images of the original, because. your smart does not have a resolution of 100500 MP, you do not need to upload the entire picture to the "thumbnails".

V
Vyacheslav Zolotov, 2014-12-05
@SZolotov

In fact, using bitmaps in a mobile application is evil, and storing collections of bitmaps in memory is generally not an affordable luxury. It is redundant and very heavy format. For image processing, look towards the Nokia Imaging SDK, it is just optimized for working with large images.
I would also advise avoiding such constructions: wbmp = wbmp.Resize ...
During the operation of the wbmp.Resize method, another bitmap is created, and then the wbmp variable refers to the second one, but the first one remains in memory. In theory, the garbage collector should clean up the memory, but it may not have time to do this, UNTIL the next time a large piece of memory is allocated. Therefore, the memory must be cleaned by hand.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question