Ram usage of BitmapImage in UWP - c#

In order to learn how develop on C# and Visual Studio i made an offline UWP application to read Comic/Manga stored in my Windows-based tablet.
One of the main steps was take the relative directory of each image in one episode and create a BitmapImage of eachone to load them to the FLipView.
Currently i'm doing it this way:
foreach (String value in ImageDirectory)
{
StorageFile file = await StorageFile.GetFileFromPathAsync((value));
IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
image = new BitmapImage();
await image.SetSourceAsync(fileStream);
images.Add(image); //images is a <List> of BitmapImage
}
In the most extreme case i have, with 124 images (50,1 Mb on disc) when loaded they use about 860 Mb of Ram which seems excesive.
I know that load all the images as one is not the most efficent approach, where a more on-demad solution would be better, but finally my questions is:
Is there a better (that uses less ram) way to load the images?

You have to have in mind that images loaded in memory are not compressed like on your disk. They are handled as bitmaps (uncompressed)
if you want to see a preview from all images on your disk, a good approach is to resize your images in memory, like in How to Copy and Resize Image in Windows 10 UWP, and load the full image on demand.

Related

Storing a Pic in The project folder, windows Phone 8.0, c#, xaml

I want to take a picture with the CameraCaptureTask, and then save it in a specific folder, in my Project(!). I found some Tutorials, where you can save a pic in the MediaLibrary. But i want to store it right in the Project.
I have the Image as a Bitmap in my code, just have to save it now.
I would really apreciate a tip.
thanks
You can store it in three ways
Using Isolated Storage
Here you just get the stream of the captured image and store it in Isolated Storage as shown here
here
Using Memory Stream
Here you store the captured image stream in the memory
using (MemoryStream stream = new MemoryStream())
{
bitmap2.SaveJpeg(stream, width, height, 0, 100); //Don't change other unless necessary
stream.Close();
}
Using Local Folder Storage
Here you save the captured image stream in Local Folder of your App
StorageFile sampleFile = await dataFolder.CreateFileAsync("image.png");
await FileIO.WriteBytesAsync(sampleFile, capturedImageStream);

Dynamic image assigned to live tile does not show?

I have a Windows Store app written in C# that works with photos. I want to show the last photo the user selected in the app in the medium size live tile (150 x 150). I am using the code below to do it. When I run the app I don't get any errors, but I don't see the selected photo in the live tile either. I know that I am doing at least some things right. I say this because if the user hasn't selected a photo yet, then I show a test image and I do see that image in the tile. But the test image comes from the app package using the ms-appx protocol, not from the app storage area.
I found a few SO posts on the subject but they are all for Windows Phone. I looked at the KnownFolders list for Windows Store app files, but nothing seemed to map to the SharedContent folder required for files meant for live tile use in Windows Phone. What is wrong with my code?
Note, the vvm.ActiveVideomark.GetThumbnail() call simply retrieves a bitmap as a WriteableBitmap object. As you can see in the code, I am resizing the image to the size required by the Medium live tile (150 x 150). ToJpegFileAsync() is an extension method that encodes a WriteableBitmap object to jpeg bytes and then writes those bytes to a file using the given file name. Both of these calls are well-tested and are not the source of the problem as far as I know.
TileUpdateManager.CreateTileUpdaterForApplication().Clear();
TileUpdateManager.CreateTileUpdaterForApplication().EnableNotificationQueue(true);
var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150Image);
var tileImage = tileXml.GetElementsByTagName("image")[0] as XmlElement;
// Got a current photo?
if (vvm.ActiveVideomark == null)
// No, just show the regular logo image.
tileImage.SetAttribute("src", "ms-appx:///Assets/Logo.scale-100.png");
else
{
// Resize it to the correct size.
WriteableBitmap wbm = await vvm.ActiveVideomark.GetThumbnail();
WriteableBitmap wbm2 = wbm.Resize(150, 150, WriteableBitmapExtensions.Interpolation.Bilinear);
// Write it to a file so we can pass it to the Live Tile.
string jpegFilename = "LiveTile1.jpg";
StorageFile jpegFile = await wbm2.ToJpegFileAsync(jpegFilename);
// Yes, show the selected image.
tileImage.SetAttribute("src", jpegFile.Path);
}
The src attribute must contain a URI with ms-appx:///, ms-appdata:///local, or http[s]:// schemes. The StorageFile.Path property, as you're using with jpegFile.Path, is a local filesystem pathmame like c:\users\Robert\AppData... which won't be valid. So create your tile images in local app data, and then use ms-appdata:///local/ to refer to them in tile payloads.

Loading Images in some directories via BitmapImage results in E_NETWORK_ERROR

I am new to Windows 8 development and Microsoft's technologies. I've done a lot of iOS development, but I've never touched Visual Studio, C#, etc before, so I am learning a lot (frameworks, IDEs, language) all at once. Forgive me if this is something simple, but I can't find an answer anywhere.
Note: I'm using Visual Studio Express 2013, C#, and Windows 8.1
For learning purposes I'm just trying to create an app that loads images, displays them on the screen, and allows the user to rotate, move, resize, and arrange them. I've got this working, except for one major bug that I can't figure out: When I load images from some directories everything works fine, when I load images from other directories I get E_NETWORK_ERROR and BitmapImage.ImageFailed. I can see no reason for this. I can actually take an image and put it in a directory and it will load, copy the same image to another directory, and it wont load.
Here's the code:
private async void addImageButton_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.Pickers.FileOpenPicker filePicker = new Windows.Storage.Pickers.FileOpenPicker();
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".bmp");
filePicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
Windows.Storage.StorageFile imageFile = await filePicker.PickSingleFileAsync();
if (imageFile != null)
{
System.Diagnostics.Debug.WriteLine(imageFile.Path);
Windows.UI.Xaml.Media.Imaging.BitmapImage bitmap = new Windows.UI.Xaml.Media.Imaging.BitmapImage(new Uri(imageFile.Path));
System.Diagnostics.Debug.WriteLine(bitmap.UriSource);
bitmap.CreateOptions = Windows.UI.Xaml.Media.Imaging.BitmapCreateOptions.None;
Image img = new Image();
img.Source = bitmap;
this.theCanvas.Children.Add(img);
}
}
This seems to work great with image files from some directories, and doesn't work at all with others. For example: in my Pictures library I have a "Camera Roll" directory that the Windows 8 camera app saves to, and I also have my Steam screenshots directory. If I try to open images in my Steam screenshots directory it works fine; however, if I try to load images in the Camera Roll directory it fails with E_NETWORK_ERROR. If I copy images from the Camera Roll directory to the Steam Screenshots directory I can open them.
Can anyone point out what's wrong? I'm starting to think this isn't a code problem so much as a security / capabilities / declarations type thing. Is there something I need to declare or request to get unfettered access to the filesystem? Do I need to form my URI differently or something?
EDIT: Here is some output from the debug statements showing the paths and URIs:
Loaded fine:
C:\Program Files (x86)\Steam\userdata\24321739\760\remote\72850\screenshots\2013-12-04_00001.jpg
file:///C:/Program Files (x86)/Steam/userdata/24321739/760/remote/72850/screenshots/2013-12-04_00001.jpg
Resulted in E_NETWORK_ERROR
C:\Users\Adam\Pictures\Camera Roll\WIN_20131023_194718.JPG
file:///C:/Users/Adam/Pictures/Camera Roll/WIN_20131023_194718.JPG
Weird issue, but I think that you should modify your code a bit as this answer here suggests. Instead of using the file path, try to create an image via loading a stream.
Just to be sure, does your app have the permission for accessing files in the user libraries?
I fixed this, but I'm not 100% sure I understand how/why the solution I arrived at resolved my problem.
I fixed the problem by switching to using a Windows.Storage.Streams.IRandomAccessStream to read the file into the BitmapImage Source:
private async void addImageButton_Click(object sender, RoutedEventArgs e)
{
Windows.Storage.Pickers.FileOpenPicker filePicker = new Windows.Storage.Pickers.FileOpenPicker();
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".bmp");
filePicker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
Windows.Storage.StorageFile imageFile = await filePicker.PickSingleFileAsync();
if (imageFile != null)
{
Windows.UI.Xaml.Media.Imaging.BitmapImage bitmap = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
Windows.Storage.Streams.IRandomAccessStream stream = await imageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
Image newImage = new Image();
bitmap.SetSource(stream);
newImage.Source = bitmap;
newImage.Height = 250;
newImage.Stretch = Stretch.UniformToFill;
newImage.ManipulationMode = ManipulationModes.All;
newImage.ManipulationDelta += TestImage_ManipulationDelta;
this.theCanvas.Children.Add(newImage);
}
}
This resolved the issue and I can now load image files from any arbitrary location. However, I'm not 100% why this resolved the problem. I think it has something to do with async. I think that the way I was doing it before would somehow result in the BitmapImage or Image not containing the actual image data at display time. What I think this solution did was make the display code wait until we'd actually taken time to load the image. Even though this is solved, if a

Reading an image file from local storage on mono for android

In mono for android I have an app that saves images to local storage for caching purposes. When the app launches it tries to load images from the cache before trying to load them from the web.
I'm currently having a hard time finding a good way to read and load them from local storage.
I'm currently using something equivilant to this:
List<byte> byteList = new List<byte>();
using (System.IO.BinaryReader binaryReader = new System.IO.BinaryReader(context.OpenFileInput("filename.jpg")))
{
while (binaryReader.BaseStream.IsDataAvailable())
{
byteList.Add(binaryReader.ReadByte());
}
}
return byteList.toArray();
OpenFileInput() returns a stream that does not give me a length so I have to read one byte at a time. It also can't seek. This seems to be causing images to load much slower than they aughto. Loading images from Resrouce.Drawable is almost instantanious by comparison, but with my method there a very noticable pause, maybe 300ms, for loading a 8kb file. This seems like a really obvious task to be able to do, but I've tried many solutions and searched a lot for advise but to no avail.
I've also noticed this code seems to crash with an EndOfStream exception when not run on the UI thread.
Any help would be hugely appreciated
What do you intend on doing with the List<byte>? You want to "load images from the cache," but you don't specify what you want to load them into.
If you want to load them into a Android.Graphics.Bitmap, you could use BitmapFactory.DecodeStream(Stream):
Bitmap bitmap = BitmapFactory.DecodeStream(context.OpenFileInput("filename.jpg"));
This would remove the List<byte> intermediary.
If you really need all the bytes (for whatever reason), you can rely on the fact that System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) is the same as Context.FilesDir, which is what context.OpenFileInput() will use, permitting:
byte[] bytes = System.IO.File.ReadAllBytes(
Path.Combine (
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),
"filename.jpg"));
However, if this is truly a cache, you should be using Context.CacheDir instead of Context.FilesDir, which is Path.GetTempPath returns:
byte[] cachedBytes = System.IO.File.ReadAllBytes(
Path.Combine(System.IO.Path.GetTempPath(), "filename.jpg"));

Creating thumbnail of all the images inside a folder

I was trying to generate thumbnails using Bitmap.GetThumbnailImage() function for 20+ images in a folder. I could see huge memory spike by the application when it does the following procedure (about 600,000K in Task Manager mem usage).
foreach (var image in ListOfImages)
{
var thumbnailFolder = #"\thumb";
var thumbnailFile = thumbnailFolder + "\\" + image.Name;
if (!Directory.Exists(thumbnailFolder))
{
Directory.CreateDirectory(thumbnailFolder);
}
if (!File.Exists(thumbnailFile))
{
using (FileStream fs = new FileStream(image.FullName, FileMode.Open, FileAccess.Read))
{
Image origImage = Image.FromStream(fs);
var thumbnail = origImage.GetThumbnailImage(90, 120, null, IntPtr.Zero);
thumbnail.Save(thumbnailFile);
thumbnail.Dispose();
origImage.Dispose();
}
}
}
Is there any way to reduce this much memory usage for thumbnail generation?
Give it a try using WPF.
In my experience WPF's image operations quite well optimized (actually it is the WIC library that's being used), and designed with threading in mind, and it does not depend the GDI bitmap handles like GDI+ does. I read once that GDI+ is not supported in server code because it is not entirely leakfree. For your scenario, WPF does not need a 3D video card.
WPF's BitmapDecoder even has built-in thumbnail functionality, which will take advantage of thumbnails in the image itself if available. See http://msdn.microsoft.com/en-us/library/ms750864(VS.85).aspx for basic image tasks in WPF. To access WPF, you need to reference the WindowsBase assembly (.net 3.0 or better).
Do not use Image.FromStream, use Image.FromFile instead for memory reasons. Frankly, I think you'd be better to adapt this example for quality reasons:
http://www.webcosmoforums.com/asp/321-create-high-quality-thumbnail-resize-image-dynamically-asp-net-c-code.html

Categories

Resources