UWP BitmapEncoder close file? - c#

How can I finalize/close the BitmapEncoder on UWP?
InMemoryRandomAccessStream imras = new InMemoryRandomAccessStream();
await [...] //Fill stream
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imras);
[...] //Do something
StorageFile sf = await ApplicationData.Current.LocalFolder.CreateFileAsync("123.jpg", CreationCollisionOption.ReplaceExisting);
BitmapEncoder bmpEncoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, await sf.OpenAsync(FileAccessMode.ReadWrite));
[...]
await bmpEncoder.FlushAsync();
imras.Dispose();
Now when I try to access the file, I get a System.UnauthorizedAccessException, I have to close the UWP app to be able to access this file... How can I close it?

You need to dispose every IDisposable object. The easiest way is to use using keyword.
using (var stream = await storageFile.OpenAsync()) // Or any other method that will open a stream.
{
var bitmapDecoder = await BitmapDecoder.CreateAsync(stream);
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
var bitmapEncoder = await BitmapEncoder.CreateForTranscodingAsync(randomAccessStream, bitmapDecoder);
// Do stuff.
await bitmapEncoder.FlushAsync();
var buffer = new byte[randomAccessStream.Size];
await randomAccessStream.AsStream().ReadAsync(buffer, 0, buffer.Length);
var someNewFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("SomeFileName", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(someNewFile, buffer);
}
}

Related

Saving list of Bitmap Images

I have a list of Bitmap images. I need to save them to local folder.
This doesn't work on windows 10 Universal application.
var serializer = new DataContractSerializer(typeof(List<BitmapImage>));
using (var stream = await ApplicationData.Current.LocalCacheFolder.OpenStreamForWriteAsync(fileName, CreationCollisionOption.ReplaceExisting)) {
serializer.WriteObject(stream, collection);
}
WriteObject method throws the following error
Exception thrown: 'System.Runtime.Serialization.InvalidDataContractException' in System.Private.DataContractSerialization.dll
BitmapImage is not serializable. Convert that to a byte array and write that to disk instead:
public static byte[] ConvertToBytes(BitmapImage bitmapImage)
{
using (var ms = new MemoryStream())
{
var btmMap = new WriteableBitmap(bitmapImage.PixelWidth, bitmapImage.PixelHeight);
btmMap.SaveJpeg(ms, bitmapImage.PixelWidth, bitmapImage.PixelHeight, 0, 100);
return ms.ToArray();
}
}
var serializer = new DataContractSerializer(typeof(byte[]));
using (var stream = await ApplicationData.Current.LocalCacheFolder.OpenStreamForWriteAsync(fileName, CreationCollisionOption.ReplaceExisting)) {
serializer.WriteObject(stream, ConvertToBytes(collection));
}
You cannot extract the bitmap from a BitmapImage. There is no way to save a BitmapImage to file directly. The only way is to remember the original source and save that out. For more details about save BitmapImage to file please reference this thread.
If you know the original source, for example, you read the BitmapImage from the file picked by a FileOpenPicker, then you can read the image file to a WriteableBitmap then you can extract the PixelBuffer, encode it with a BitmapEncoder, and then save the resulting stream to a StorageFile as Rob said. Sample code as follows:
private async void btncreate_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker openpicker = new FileOpenPicker();
openpicker.FileTypeFilter.Add(".jpg");
openpicker.FileTypeFilter.Add(".png");
StorageFile originalimage = await openpicker.PickSingleFileAsync();
WriteableBitmap writeableimage1;
using (IRandomAccessStream stream = await originalimage.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage1 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage1.SetSource(stream);
}
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile newimage = await folder.CreateFileAsync(originalimage.Name, CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream ras = await newimage.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, ras);
var stream = writeableimage1.PixelBuffer.AsStream();
byte[] buffer = new byte[stream.Length];
await stream.ReadAsync(buffer, 0, buffer.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage1.PixelWidth, (uint)writeableimage1.PixelHeight, 96.0, 96.0, buffer);
await encoder.FlushAsync();
}
}
For list of images, you may need save them one by one.

Create Tiff File from multiple BitmapImage

Background:
I'm developing Win 10 Universal App, have list of BitmapImage:
List<BitmapImage> ImagesList = new List<BitmapImage>();
Each list item is created by converting byte[] to BitmapImage by this code:
public async Task<BitmapImage> GetBitmapImage(byte[] array)
{
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
{
writer.WriteBytes(array);
await writer.StoreAsync();
}
BitmapImage image = new BitmapImage();
List<BitmapImage> ImagesList = new List<BitmapImage>();
await image.SetSourceAsync(stream);
return image;
}
}
Question:
How to convert this list to single multi-page Tiff file?
Notes:
I've found many related answers like this but all are based on System.Drawing library which is not supported in Windows 10 Universal Apps, so as you can see in my code, I'm using Windows.Ui.Xaml.Media.Imaging.BitmapImage object instead of System.Drawing.Bitmap to get the image.
How to convert this list to single multi-page Tiff file
In UWP app, we can use BitmapEncoder to encode a Tiff image file to contain several frames. BitmapEncoder.SetPixelData method can be used for setting pixel data on one frame and then BitmapEncoder.GoToNextFrameAsync can asynchronously commits the current frame data and appends a new empty frame to be edited. So the Tiff image can be created by multiply images.
Suppose I want to create a Tiff image file from three images that are located on my local folder, I decode and read pixel data from them and set to the Tiff image. Sample code as follows:
private async void btnConvert_Click(object sender, RoutedEventArgs e)
{
StorageFolder localfolder = ApplicationData.Current.LocalFolder;
StorageFile image1 = await localfolder.GetFileAsync("caffe1.jpg");
StorageFile image2 = await localfolder.GetFileAsync("caffe2.jpg");
StorageFile image3 = await localfolder.GetFileAsync("caffe3.jpg");
StorageFile targettiff = await localfolder.CreateFileAsync("temp.tiff", CreationCollisionOption.ReplaceExisting);
WriteableBitmap writeableimage1;
WriteableBitmap writeableimage2;
WriteableBitmap writeableimage3;
using (IRandomAccessStream stream = await image1.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage1 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage1.SetSource(stream);
}
using (IRandomAccessStream stream = await image2.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage2 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage2.SetSource(stream);
}
using (IRandomAccessStream stream = await image3.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage3 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage3.SetSource(stream);
}
using (IRandomAccessStream ras = await targettiff.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.TiffEncoderId, ras);
var stream = writeableimage1.PixelBuffer.AsStream();
byte[] buffer = new byte[stream.Length];
await stream.ReadAsync(buffer, 0, buffer.Length);
var stream2 = writeableimage2.PixelBuffer.AsStream();
byte[] buffer2 = new byte[stream2.Length];
await stream2.ReadAsync(buffer2, 0, buffer2.Length);
var stream3 = writeableimage3.PixelBuffer.AsStream();
byte[] buffer3 = new byte[stream3.Length];
await stream3.ReadAsync(buffer3, 0, buffer3.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage1.PixelWidth, (uint)writeableimage1.PixelHeight, 96.0, 96.0, buffer);
await encoder.GoToNextFrameAsync();
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage2.PixelWidth, (uint)writeableimage2.PixelHeight, 96.0, 96.0, buffer2);
await encoder.GoToNextFrameAsync();
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage3.PixelWidth, (uint)writeableimage3.PixelHeight, 96.0, 96.0, buffer3);
await encoder.FlushAsync();
}
}
The temp.tiff will be created successfully. I'm not sure how you got the image byte array, but BitmapImage cannot be directly written to or updated, you need to got WriteableBitmap object from your byte array. If you don't know how to get the WriteableBitmap please try to reference the following code or save the BitmapImage to local folder and using the code I provided above.
public async Task<WriteableBitmap> SaveToImageSource(byte[] imageBuffer)
{
using (MemoryStream stream = new MemoryStream(imageBuffer))
{
var ras = stream.AsRandomAccessStream();
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, ras);
var provider = await decoder.GetPixelDataAsync();
byte[] buffer = provider.DetachPixelData();
WriteableBitmap ablebitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
await ablebitmap.PixelBuffer.AsStream().WriteAsync(buffer, 0, buffer.Length);
return ablebitmap;
}
}
More details please reference the official sample.

Save recorded sound to project file in UWP

I recorded sound with the device's microphone but I don't know how to save it. Is it with the help of MediaCapture element, and if yes, then how to do it?
Here is a basic idea how to convert to mp3 and save in a file with Datawriter.
I wrote this code on the fly so its not tested.
MediaEncodingProfile _Profile = Windows.Media.MediaProperties.MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High);
MediaTranscoder _Transcoder = new Windows.Media.Transcoding.MediaTranscoder();
CancellationTokenSource _cts = new CancellationTokenSource();
private void ConvertSteamToMp3()
{
IRandomAccessStream audio = buffer.CloneStream(); //your recoreded InMemoryRandomAccessStream
var folder = KnownFolders.MusicLibrary.CreateFolderAsync("MyCapturedAudio", CreationCollisionOption.OpenIfExists);
outputFile = await folder.CreateFileAsync("record.mp3", CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream fileStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
var preparedTranscodeResult = await _Transcoder.PrepareStreamTranscodeAsync(audio, fileStream, _Profile);
if (preparedTranscodeResult.CanTranscode)
{
var progress = new Progress<double>(TranscodeProgress);
await preparedTranscodeResult.TranscodeAsync().AsTask(_cts.Token, progress);
}
using (IOutputStream outputStream = fileStream.GetOutputStreamAt(0))
{
using (DataWriter dataWriter = new DataWriter(outputStream))
{
//TODO: Replace "Bytes" with the type you want to write.
dataWriter.WriteBytes(bytes);
await dataWriter.StoreAsync();
dataWriter.DetachStream();
}
await outputStream.FlushAsync();
}
}
}
Or just save the stream in a file
public async SaveToFile()
{
IRandomAccessStream audio = buffer.CloneStream(); //your recoreded InMemoryRandomAccessStream
var folder = KnownFolders.MusicLibrary.CreateFolderAsync("MyCapturedAudio", CreationCollisionOption.OpenIfExists);
outputFile = await folder.CreateFileAsync("record.mp3", CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream fileStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
await RandomAccessStream.CopyAndCloseAsync(audio.GetInputStreamAt(0), fileStream.GetOutputStreamAt(0));
await audio.FlushAsync();
audio.Dispose();
}
});
}

Photo Capture stream to SoftwareBitmap

My Code:
var stream = new InMemoryRandomAccessStream();
ImageEncodingProperties properties = ImageEncodingProperties.CreateJpeg();
await _mediaCapture.CapturePhotoToStreamAsync(properties, stream);
IBuffer buffer = new byte[stream.Size].AsBuffer();
await stream.ReadAsync(buffer, (uint)stream.Size, InputStreamOptions.None);
SoftwareBitmap sfbmp = SoftwareBitmap.CreateCopyFromBuffer(buffer, BitmapPixelFormat.Bgra8, 4176, 3120); // Exception is thrown here
Exception:
Insufficient memory for response
Honestly, I don't know anything about bitmap buffers so can anyone help me?
I solved it by using BitmapDecoder
var stream = new InMemoryRandomAccessStream();
ImageEncodingProperties properties = ImageEncodingProperties.CreateJpeg();
await _mediaCapture.CapturePhotoToStreamAsync(properties, stream);
var decoder = await BitmapDecoder.CreateAsync(stream);
SoftwareBitmap sfbmp = await decoder.GetSoftwareBitmapAsync();

Download an image to local storage in Metro style apps

In WinRT / C#, How do I download an image to a local folder to support caching of an online catalogue for offline use? is there a way to directly download the images and link the control to get them from the cache as a fallback?
var downloadedimage = await HttpWebRequest.Create(url).GetResponseAsync();
StorageFile imgfile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
"localfile.png", CreationCollisionOption.FailIfExists);
What do I do next to store downloadedimage as localfile.jpg?
Looks like the code below from the HttpClient sample for Windows 8 solves the issue
HttpRequestMessage request = new HttpRequestMessage(
HttpMethod.Get, resourceAddress);
HttpResponseMessage response = await rootPage.httpClient.SendAsync(request,
HttpCompletionOption.ResponseHeadersRead);
httpClient is a HttpClient, and its BaseAddress needs to be set a the server folder of your resource. we can then do this to convert that to an image source (if that's what we're downloading)
InMemoryRandomAccessStream randomAccessStream =
new InMemoryRandomAccessStream();
DataWriter writer = new DataWriter(randomAccessStream.GetOutputStreamAt(0));
writer.WriteBytes(await response.Content.ReadAsByteArrayAsync());
await writer.StoreAsync();
BitmapImage image = new BitmapImage();
imagecontrol.SetSource(randomAccessStream);
or this to write it to file
var imageFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(
filename, CreationCollisionOption.ReplaceExisting);
var fs = await imageFile.OpenAsync(FileAccessMode.ReadWrite);
DataWriter writer = new DataWriter(fs.GetOutputStreamAt(0));
writer.WriteBytes(await response.Content.ReadAsByteArrayAsync());
await writer.StoreAsync();
writer.DetachStream();
await fs.FlushAsync();
Try this:
var response = await HttpWebRequest.Create(url).GetResponseAsync();
List<Byte> allBytes = new List<byte>();
using (Stream imageStream = response.GetResponseStream())
{
byte[] buffer = new byte[4000];
int bytesRead = 0;
while ((bytesRead = await imageStream.ReadAsync(buffer, 0, 4000)) > 0)
{
allBytes.AddRange(buffer.Take(bytesRead));
}
}
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(
"localfile.png", CreationCollisionOption.FailIfExists);
await FileIO.WriteBytesAsync(file, allBytes.ToArray());
If you want to download a file represented by a URL, the easiest way is to use the BackgroundDownloader class. See this page for more information on how to use the background downloader.
GetResponseAsync will give you a WebResponse file not the image:
Stream imageStream = downloadedimage.GetResponseStream();
Then you can write to the image file:
byte[] imageData = new byte[imageStream.Length];
using(StreamReader reader = new Streamreader(imageStream))
{
reader.ReadBytes(imageData, 0, imageData.Length);
}
await FileIO.WriteBytesAsync(imgfile, imageData);
By default Bitmap Images are cached by WinRT with no intervention required by the developer.

Categories

Resources