How to improve performance method GetThumbnailAsync in window phone 8.1 - c#

I write a function to show images on a folder (assume i have about 60 images in this folder) in window phone 8.1. And the problem is function GetThumbnailAsync() take so long time when i create stream to get bitmapImage.
Here's my code
//getFileInPicture is function get all file in picture folder
List<StorageFile> lstPicture = await getFileInPicture();
foreach (var file in lstPicture)
{
var thumbnail = await file.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView,50);
var bitmapImage = new BitmapImage();
bitmapImage.DecodePixelWidth = (int)(ScreenWidth / 4);
await bitmapImage.SetSourceAsync(thumbnail);
g_listBitmapImage.Add(bitmapImage);
//g_listBitmapImage is a list of bitmapImage
}
I was test and find the problem is function GetThumbnailAsync take so long time.
If i have about 60 picture it take about 15second to finish this function( i test in lumia 730).
Have anybody get this problem and how to make this code run faster ?.
Thanks so much for your supports

You are currently awaiting file.GetThumbnailAsync for each file which means that although the function is executed asynchronously for each file, it is executed in order, not in parallel.
Try converting each async operation returned from file.GetThumbnailAsyncto a Task and then storing those in a list and then await all tasks using Task.WhenAll.
List<StorageFile> lstPicture = await getFileInPicture();
List<Task<StorageItemThumbnail>> thumbnailOperations = List<Task<StorageItemThumbnail>>();
foreach (var file in lstPicture)
{
thumbnailOperations.Add(file.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.PicturesView,50).AsTask());
}
// wait for all operations in parallel
await Task.WhenAll(thumbnailOperations);
foreach (var task in thumbnailOperations)
{
var thumbnail = task.Result;
var bitmapImage = new BitmapImage();
bitmapImage.DecodePixelWidth = (int)(ScreenWidth / 4);
await bitmapImage.SetSourceAsync(thumbnail);
g_listBitmapImage.Add(bitmapImage);
//g_listBitmapImage is a list of bitmapImage
}

Related

Send InkCanvas strokes directly to IRandomnaccessStream

I'm trying to get a byte array from the InkCanvas control, but the method i've come up with so far seems a bit long winded.
Currently I use the following:
StorageFolder folder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("Temp");
StorageFile file = await folder.CreateFileAsync(GenerateString(5)+".zzx", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
await SignatureCanvas.InkPresenter.StrokeContainer.SaveAsync(stream);
var array = await IRandomAccessStreamToByteArray(stream);
}
The custom stream reader is as follows.
private async Task<byte[]> IRandomAccessStreamToByteArray(IRandomAccessStream stream)
{
var reader = new DataReader(stream.GetInputStreamAt(0));
var bytes = new byte[stream.Size];
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(bytes);
return bytes;
}
This works, and gives me the byte array that i need, but also leaves me with unwanted images. Was having some access issues due to files still being written when another call wanted to replace the file so decided to go down the multiple images route. Is there a way to skip the image file entirely? It's not too much of an issue to clear out a temp folder, but if it can be avoided that would be preferable.
I had read somewhere already that InkCanvas doesn't support direct to array dumps, so any suggestions would be appreciated!
If you want to save your InkCanvas strokes to bytes Array without create the file, you should be able to use the Win2D.uwp.
To install Win2D.uwp, run the "Install-Package Win2D.uwp" command in the Package Manager Console.
There is a CanvasDrawingSession.DrawInk method to draw a collection of ink strokes. That we should be able to use CanvasBitmap.GetPixelBytes method to get an array of raw byte data for the entire bitmap.
For example:
private byte[] ConvertInkCanvasToByteArray()
{
var canvasStrokes = SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes();
if (canvasStrokes.Count > 0)
{
var width = (int)SignatureCanvas.ActualWidth;
var height = (int)SignatureCanvas.ActualHeight;
var device = CanvasDevice.GetSharedDevice();
device.DeviceLost += DeviceOnDeviceLost;
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
using (var ds = renderTarget.CreateDrawingSession())
{
ds.Clear(Windows.UI.Colors.White);
ds.DrawInk(SignatureCanvas.InkPresenter.StrokeContainer.GetStrokes());
}
return renderTarget.GetPixelBytes();
}
else
{
return null;
}
}
private void DeviceOnDeviceLost(CanvasDevice sender, object args)
{
Debug.WriteLine("DeviceOnDeviceLost");
}
Also if we want to convert the bytes array to image, we should be able to use following code:
WriteableBitmap bitmap = new WriteableBitmap((int)SignatureCanvas.ActualWidth, (int)SignatureCanvas.ActualHeight);
await bitmap.PixelBuffer.AsStream().WriteAsync(mybytes, 0, mybytes.Length);

How do i take a photo with the correct rotation, aspect ratio in Windows Phone 8.1? (using MediaCapture)

Can any of you provide an actual working sample of how to take and save a photo using the MediaCapture element. I've tried looking for an actual solution in MSDN but none of those explanations or code actually describe the process in a simple way.
I need to take a picture and save it to my library (i need to show the correct preview for this), however right now it is rotated 90 degrees and i can't adjust it. I've tried setting the rotation of the video preview and it works for the preview however when i do this the aspect ratio its all wrong and the saved image its not correct.
The examples from channel 9 kind of suck too. I just need a simple implementation...
Im using a Runtime app NOT a silverlight app for Windows Phone 8.1.
I have had the same issue, SetRecordRotation doesn't work for me. I found workaround - take photo and rotate an image, it works great. I use method like that:
private async void CapturePhoto()
{
string photoPath = string.Empty;
ImageEncodingProperties format = ImageEncodingProperties.CreateJpeg();
using (var imageStream = new InMemoryRandomAccessStream())
{
await MediaCapture.CapturePhotoToStreamAsync(format, imageStream);
BitmapDecoder dec = await BitmapDecoder.CreateAsync(imageStream);
BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(imageStream, dec);
enc.BitmapTransform.Rotation = BitmapRotation.Clockwise90Degrees;
await enc.FlushAsync();
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile capturefile = await folder.CreateFileAsync("photo.jpg", CreationCollisionOption.GenerateUniqueName);
photoPath = capturefile.Name;
using (var fileStream = await capturefile.OpenAsync(FileAccessMode.ReadWrite))
{
try
{
await RandomAccessStream.CopyAsync(imageStream, fileStream);
}
catch {}
}
}
}
I modified sample of code from article How to capture a photo in your Windows Phone 8.1 Runtime app by Marco Siccardi
http://dotnet.dzone.com/articles/how-capture-photo-your-windows-0
There are two samples posted on the Microsoft github page that are relevant, although they target Windows 10. Still, the APIs should work on 8/8.1.
GetPreviewFrame: This sample will not lock the page rotation, and apply a corrective rotation to the preview stream. It does not use SetPreviewRotation, as that method is more resource-heavy than using the metadata approach. This sample doesn't capture photos (just preview frames)
UniversalCameraSample: This one does capture photos, and supports portrait and landscape orientations. Here is the relevant part:
var stream = new InMemoryRandomAccessStream();
try
{
await _mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);
var photoOrientation = ConvertOrientationToPhotoOrientation(GetCameraOrientation());
await ReencodeAndSavePhotoAsync(stream, photoOrientation);
}
catch (Exception ex)
{
Debug.WriteLine("Exception when taking a photo: {0}", ex.ToString());
}
With:
private static async Task ReencodeAndSavePhotoAsync(IRandomAccessStream stream, PhotoOrientation photoOrientation)
{
using (var inputStream = stream)
{
var decoder = await BitmapDecoder.CreateAsync(inputStream);
var file = await KnownFolders.PicturesLibrary.CreateFileAsync("SimplePhoto.jpeg", CreationCollisionOption.GenerateUniqueName);
using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
var properties = new BitmapPropertySet { { "System.Photo.Orientation", new BitmapTypedValue(photoOrientation, PropertyType.UInt16) } };
await encoder.BitmapProperties.SetPropertiesAsync(properties);
await encoder.FlushAsync();
}
}
}
Have a closer look at the sample to see how to get the orientation of the camera in the first place (a call to it is being made in the first snippet I posted).
Or, if you prefer a video, you can watch the camera session from the recent //build/ conference, which includes a little bit of a walkthrough through some camera samples.
you can change the aspect ratio for your video preview & captured photo by setting in the MediaCapture.VideoDeviceController.
Also, you can set your video preview upright by using the following code.
MediaCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees);
I have answered a similar questions in the another post in the link below. Hope it helps.
https://stackoverflow.com/a/29875992/4672579

Download and Save image in Pictures Library through Windows 8 Metro XAML App

I am trying to develop a simple Windows 8 Metro app which simply downloads an image file from a given URL (say http://sample.com/foo.jpg) and then save it to Pictures Library.
I have an image control in the UI to display the downloaded image.
I'm also facing difficulty in setting the image source for the image control to the newly downloaded image (actually I'm not even able to download it).
Also, is it possible to store the image file in a particular folder in the Pictures library (if it doesn't exist, then the app should create it)?
I'm really stuck here.
Please help me.
Here's some rough code that I believe accomplishes what you want. It assumes you have two image controls (Image1 and Image2) and that you have the Pictures Library capability checked in the manifest. Take a look at the XAML images sample as well
Uri uri = new Uri("http://www.picsimages.net/photo/lebron-james/lebron-james_1312647633.jpg");
var fileName = Guid.NewGuid().ToString() + ".jpg";
// download pic
var bitmapImage = new BitmapImage();
var httpClient = new HttpClient();
var httpResponse = await httpClient.GetAsync(uri);
byte[] b = await httpResponse.Content.ReadAsByteArrayAsync();
// create a new in memory stream and datawriter
using (var stream = new InMemoryRandomAccessStream())
{
using (DataWriter dw = new DataWriter(stream))
{
// write the raw bytes and store
dw.WriteBytes(b);
await dw.StoreAsync();
// set the image source
stream.Seek(0);
bitmapImage.SetSource(stream);
// set image in first control
Image1.Source = bitmapImage;
// write to pictures library
var storageFile = await KnownFolders.PicturesLibrary.CreateFileAsync(
fileName,
CreationCollisionOption.ReplaceExisting);
using (var storageStream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
await RandomAccessStream.CopyAndCloseAsync(stream.GetInputStreamAt(0), storageStream.GetOutputStreamAt(0));
}
}
}
// read from pictures library
var pictureFile = await KnownFolders.PicturesLibrary.GetFileAsync(fileName);
using ( var pictureStream = await pictureFile.OpenAsync(FileAccessMode.Read) )
{
bitmapImage.SetSource(pictureStream);
}
Image2.Source = bitmapImage;
}

The best way load files from Isolated Storage

In my Windows Store App I save/use files (almost images) in Isolated Storage. When I need to present image i use following:
var file = await folder.GetFileAsync(fileName);
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
obj.Image = new BitmapImage();
await obj.Image.SetSourceAsync(stream);
}
But when I use 3+ images in same page I have lags. I'm looking for faster solution to access Isolated Storage files.
You can try to start with this article Optimize media resources (Windows Store apps using C#/VB/C++ and XAML), and couple more things:
Make sure that if you show them in the ListView / GridView - you have enabled virtualization (you use right ItemsPanel which supports virtualization).
If you just need to load images from local storage - set the binding from Image.Source to to the right URI (ms-appx:/// or ms-appdata:///local/), and Image control will do everything for you.
I don't know how you're opening multiple images, but since all the methods are asynchronous you shouldn't iterate through your files sequentially, but open all of them in parallel.
So instead of doing this (where you're waiting for the previous image to load before starting to load the next one):
foreach (var fileName in fileNames)
{
var file = await folder.GetFileAsync(fileName);
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
obj.Image = new BitmapImage();
await obj.Image.SetSourceAsync(stream);
}
}
You should approach it like this:
// not sure about the type of obj
public async Task<Image> LoadImage(string fileName, dynamic obj)
{
var file = await folder.GetFileAsync(fileName);
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
obj.Image = new BitmapImage();
await obj.Image.SetSourceAsync(stream);
}
}
var tasks = fileNames.Select(f => LoadImage(f, obj)).ToArray();
await Task.WhenAll(tasks);
This will initialize an array of awaitable tasks loading the images and then await all of them at the same time so that they will execute in parallel.

WinRT create animated GIF

I'm working on creating some animated GIFs in a WinRT app. I've got gif conversion working with multiple frames in the resulting image, but I cannot figure out how to set things like frame delay and repeat behavior.
The GoToNextFrameAsync takes in options which feels like the right place to specify things like that, but I have no idea what to pass in and can't find any documentation on the subject.
public async static void AnimateImages(IEnumerable<StorageFile> files)
{
var outFile = await KnownFolders.PicturesLibrary.CreateFileAsync("test.gif", CreationCollisionOption.ReplaceExisting);
var outStream = await outFile.OpenAsync(FileAccessMode.ReadWrite);
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outStream);
for (int i = 0; i < files.Count(); i++)
{
using (var str = await files.ElementAt(i).OpenAsync(FileAccessMode.Read))
{
var decoder = await BitmapDecoder.CreateAsync(str);
var pixels = await decoder.GetPixelDataAsync();
encoder.SetPixelData(decoder.BitmapPixelFormat, BitmapAlphaMode.Ignore,
decoder.OrientedPixelWidth, decoder.OrientedPixelHeight,
decoder.DpiX, decoder.DpiY,
pixels.DetachPixelData());
if(i < files.Count() -1 )
await encoder.GoToNextFrameAsync();
}
}
await encoder.FlushAsync();
outStream.Dispose();
}
I've been having trouble with this too. It seems that whilst reading properties is OK setting any kind of property on a frame results in an exception. As far as I can tell from here the BitmapEncoders are related to Windows Imaging Components, and as the page for Native WIC Codecs on MSDN states, there are no options for GIFs. It does seem odd though.

Categories

Resources