locked
BitmapImage.DecodePixelHeight does not actually save memory

    Question

  • If I load a large resolution jpg image 3800 x 3000 and set DecodePixelHeight to say 100 px the memory goes up by a whopping 10MB. If I dont set DecodePixelHeight the amount goes up is EXACTLY the same.

    I thought setting DecodePixel* is supposed to save memory?? Here's my code:

    //stream is from file load
    
    BitmapImage ^bitmap = ref new BitmapImage();
    bitmap->DecodePixelHeight = 150;
    bitmap->SetSourceAsync(stream);

    It is stated in https://msdn.microsoft.com/library/windows/apps/br243235 but it doesn't seem to work? Am I doing something wrong?

    Monday, April 13, 2015 8:28 AM

All replies

  • I don't see how the stream would  change size according to how it's displayed, but I'll ask my graphic expert to check on this thread for better information.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Monday, April 13, 2015 2:23 PM
    Moderator
  • sorry I just realized I quoted the wrong source, it should be from here: https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.image

    "...The DecodePixel* properties enable you to pass information directly to the format-specific codec, and the codec can use this information to decode more efficiently and to a smaller memory footprint. Set DecodePixelWidth to the same pixel width of the area that you want your app to actually display... "

    If I set DecodePixelHeight to 150px and enlarge to full screen by setting Width and Height of the actual Image object it is completely blurred out because it is significantly down-scaled, but it didn't save any memory.

    In addition I also tried passing the stream to BitmapDecoder and have BitmapEncoder.CreateForTranscodingAsync() to down size the image which works in significantly reducing memory footprint. The problem is BitmapEncoder is bugged, there are image that crashes the app when I feed it through BitmapEncoder. But the DecodePixel* option always works, so I would prefer to go that route.

    Thanks for looking into this issue!

    Monday, April 13, 2015 2:56 PM
  • The problem is that the call to SetSourceAsync keeps a reference to stream, so the original image file's contents are live. You're still saving significant memory with the lower DecodePixelHeight: 10MB of encoded jpg is significantly less than the 43MB (3800x3000x4 bytes) in the full size decoded file.

    If you call SetSource instead of SetSourceAsync then the reference isn't kept and the file won't be held in memory.

    You can also use a BitmapDecoder and pass a BitmapTransform to downsize it similar to using DecodePixelHeight and Width. This will also let you choose the interpolation mode if you need a higher quality (at the expense of performance) resize. From what you've described of your use case there is no good reason to reencode the image with a BitmapEncoder.

    --Rob

    Tuesday, April 14, 2015 3:01 AM
    Owner
  • Hi Rob,

    Calling SetSource or SetSourceAsync makes no difference on the memory usage. I was using SetSource before until it the app started to stutter on large images loads. Notice I also mentioned if I dont set DecodePixel* the memory goes up by exactly the same amount. Besides wouldn't the stream be deferenced once SetSourceAsync completes?

    Out of curiosity I also tested loading a 500mb bmp file 15000 x 11000 and a 26MB gigapan jpg both crashed the app regardless of DecodePixel* setting or using SetSourceAsync/SetSource. But passing it throught BitmapEncoder was completely fine.

    I'm curious as to what you mean by passing BitmapTransform to BitmapDecoder? BitmapDecoder doesn't seem to have this property/function. BitmapEncoder on the other hand does exactly that but not the decoder.

    Tuesday, April 14, 2015 4:15 PM
  • Can you reproduce with the XAML images sample ? Scenario 2 demonstrates DecodePixelWidth and Height. If so please provide exact repro steps and a clear description of how the actual behavior differs from the expected behavior.

    You can pass a BitmapTransform to BitmapDecoder's GetPixelDataAsync(BitmapPixelFormat, BitmapAlphaMode, BitmapTransform, ExifOrientationMode, ColorManagementMode)  method if you want to transform the image (including cropping, scaling, rotating, interpolation, etc.)

    Tuesday, April 14, 2015 11:24 PM
    Owner
  • Hi Rob,

    Sorry for the delay here are the steps to reproduce with XAML image sample. It will crash both on BMP and jpg. However in either cases setting DecodePixel* did NOT save memory. I tested on both release and debug configs

    Prep:

    Test 1: Create a 15000 x 11000 white bmp using paint or alike (should be around 500MB)

    Test 2: Download the jpg from here

    ----

    Test 1 Steps

    1. Comment out DecodePixelWidth in the sample app to keep the aspect ratio

    2. Build and load XAML image sample, set DecodePixelHeight to 100 (Default)

    3. Load the BMP file

    4. See memory shoot up 500MB

    5. Now set DecodPixelHeight to 1000 and load the same BMP file again

    6. Out of memory crash

    Test 2 Steps (modify source and build same as test 1)

    1. Load app and set DecodePixelHeight to 100

    2. Load the jpg file

    3. See memory shoot up ~25MB

    4. Set DecodePixelHeight to 1000

    5. Load the same jpg file again

    6. Memory usage does NOT change

    4. Repeat with DecodePixelHeight 2000, again memory usage does not change (insignifiant amount)

    5. You can repeat this incrementally until around 8-9k then it crashes all of a sudden


    You can see how test 2 would pose a big problem if I'm trying to generate thumbnail in a folder of large resolution files. If each file takes ~25mb a folder with more than 20 images will always crash the app. In addition thumbnails are not always embedded in jpg nor generated in time by windows, so using system generated thumbnails are not reliable.
    • Edited by Wetdry Saturday, April 18, 2015 1:11 AM
    Saturday, April 18, 2015 12:56 AM