WriteableBitmap SaveJpeg missing for universal apps - c#

I am developing an universal app, in my shared code i am trying to download the image from net and save the image to LocalFolder.
I am using HttpClient to download the images from user given urls and reading the client response to save the image. I am using below code to save, but couldn't able to find Writeable SaveJpeg method.
HttpResponseMessage response = await httpClient.GetAsync(imageUri);
await Task.Run(async () =>
{
if (response.IsSuccessStatusCode)
{
// save image locally
StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Images", CreationCollisionOption.OpenIfExists);
BitmapImage bmp = new BitmapImage();
var buffer = await response.Content.ReadAsBufferAsync();
InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
DataWriter writer = new DataWriter(ras.GetOutputStreamAt(0));
writer.WriteBuffer(buffer);
bmp.SetSource(ras);
}
});
What is the best way to save the imageresponse to localfolder with image quality % (for both WP and Windows).

You should save the stream directly instead of saving the BitmapImage.
Something like this.
var ras = new InMemoryRandomAccessStream();
var writer = new DataWriter(ras);
writer.WriteBuffer(buffer);
await writer.StoreAsync();
var inputStream = ras.GetInputStreamAt(0);
// you can still use this to display it on the UI though
//bmp.SetSource(ras);
// write the picture into this folder
var storageFile = await folder.CreateFileAsync("image1.jpg", CreationCollisionOption.GenerateUniqueName);
using (var storageStream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
await RandomAccessStream.CopyAndCloseAsync(inputStream, storageStream.GetOutputStreamAt(0));
}
Update
You can use BitmapEncoder and when pass in property dpi values in SetPixelData.
using (var storageStream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, storageStream);
var pixelStream = yourWriteableBitmap.PixelBuffer.AsStream();
var pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)yourWriteableBitmap.PixelWidth, (uint)yourWriteableBitmap.PixelHeight, 48, 48, pixels);
await encoder.FlushAsync();
}

Related

How can set Image and PDF size in UWP C#

I am trying to generate a pdf in my project where I converted Image into Pdf and Image generated from Layout. Generated image size is very large and also There are not received good quality.
Please Help me to solve this.
I am using this code in program.
RenderTargetBitmap renderTargetBitmapimg = new RenderTargetBitmap();
var mainlayoutHeight = pageStackPanel.ActualHeight;
var mainlayoutWidth = pageStackPanel.ActualWidth;
int mlHeight = Convert.ToInt32(mainlayoutHeight);
int mlWidth = Convert.ToInt32(mainlayoutWidth);
await renderTargetBitmapimg.RenderAsync(pageStackPanel, mlWidth, mlHeight);
///SAVE IMAGE IN FOLDER
var pixelBufferimg = await renderTargetBitmapimg.GetPixelsAsync();
var pixels = pixelBufferimg.ToArray();
var displayInformation = DisplayInformation.GetForCurrentView();
var storageFolder = await KnownFolders.GetFolderForUserAsync(null, KnownFolderId.PicturesLibrary);
StorageFolder projectFolder = await storageFolder.CreateFolderAsync("TestImage", CreationCollisionOption.OpenIfExists);
var file = await projectFolder.CreateFileAsync(jpgFilename, CreationCollisionOption.GenerateUniqueName);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)renderTargetBitmapimg.PixelWidth,
(uint)renderTargetBitmapimg.PixelHeight,
200, 200,
pixels);
await encoder.FlushAsync();
StorageFile prescriptionJpgFile = await projectFolder.GetFileAsync(jpgFilename);
await stream.FlushAsync();
stream.Seek(0);
stream.Dispose();
renderTargetBitmapimg = null;
The answer is no, you can't set the size of the generated image file. But you could try to resize it to reduce its physical size by giving a specific width and height. You could use BitmapEncoder class to encode the original image, change the BitmapInterpolationMode of the image. The code looks like this:
//open file as stream
using (IRandomAccessStream fileStream = await imagefile.OpenAsync(FileAccessMode.ReadWrite))
{
var decoder = await BitmapDecoder.CreateAsync(fileStream);
var resizedStream = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(resizedStream, decoder);
double widthRatio = (double)reqWidth / decoder.PixelWidth;
double heightRatio = (double)reqHeight / decoder.PixelHeight;
double scaleRatio = Math.Min(widthRatio, heightRatio);
if (reqWidth == 0)
scaleRatio = heightRatio;
if (reqHeight == 0)
scaleRatio = widthRatio;
uint aspectHeight = (uint)Math.Floor(decoder.PixelHeight * scaleRatio);
uint aspectWidth = (uint)Math.Floor(decoder.PixelWidth * scaleRatio);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Linear;
encoder.BitmapTransform.ScaledHeight = aspectHeight;
encoder.BitmapTransform.ScaledWidth = aspectWidth;
await encoder.FlushAsync();
resizedStream.Seek(0);
var outBuffer = new byte[resizedStream.Size];
await resizedStream.ReadAsync(outBuffer.AsBuffer(), (uint)resizedStream.Size, InputStreamOptions.None);
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
Debug.WriteLine(storageFolder.Path);
StorageFile sampleFile = await storageFolder.CreateFileAsync("testfile.pdf", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(sampleFile, outBuffer);
}
For more information, you could check this document:How to Compress image and change its Size.
Another suggestion is that you could just use the UWP print API to generate the PDF file. You could take a look at the Print Helper of Windows Community Toolkit. You could choose not to print but save it as a PDF file.

How to take Screen shot in windows store apps

I want to take the screenshot in my application and want to save it in local folder of app with unique name.
so please help me.
You can capture you screen using RenderTargetBitmap. Try this code:
//create and capture Window
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(Window.Current.Content);
//create unique file in LocalFolder
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("screenshotCapture.jpg", CreationCollisionOption.GenerateUniqueName);
//create JPEG image
using (var stream = await file.OpenStreamForWriteAsync())
{
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream.AsRandomAccessStream());
encoder.SetPixelData(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight, logicalDpi, logicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
Or
public static async Task<StorageFile> AsUIScreenShotFileAsync(this UIElement elememtName, string ReplaceLocalFileNameWithExtension)
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(ReplaceLocalFileNameWithExtension, CreationCollisionOption.ReplaceExisting);
try
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
// Render to an image at the current system scale and retrieve pixel contents
await renderTargetBitmap.RenderAsync(elememtName);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
// Encode image to an in-memory stream
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)renderTargetBitmap.PixelWidth, (uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi, pixelBuffer.ToArray());
await encoder.FlushAsync();
//CreatingFolder
// var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
RandomAccessStreamReference rasr = RandomAccessStreamReference.CreateFromStream(stream);
var streamWithContent = await rasr.OpenReadAsync();
byte[] buffer = new byte[streamWithContent.Size];
await streamWithContent.ReadAsync(buffer.AsBuffer(), (uint)streamWithContent.Size, InputStreamOptions.None);
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (IOutputStream outputStream = fileStream.GetOutputStreamAt(0))
{
using (DataWriter dataWriter = new DataWriter(outputStream))
{
dataWriter.WriteBytes(buffer);
await dataWriter.StoreAsync(); //
dataWriter.DetachStream();
}
// write data on the empty file:
await outputStream.FlushAsync();
}
await fileStream.FlushAsync();
}
// await file.CopyAsync(folder, "tempFile.jpg", NameCollisionOption.ReplaceExisting);
}
catch (Exception ex)
{
Reporting.DisplayMessageDebugExemption(ex.Message);
}
return file;
}

Storing a BitmapImage in LocalFolder - UWP

I'm trying to store a BitmapImage to the filesystem using C# on UWP. The image is downloaded from Facebook using the graph api and returned as a BitmapImage. That part works, and to retrieve the image (once I can store it, tested with pictures just dropped in the local folder) I'm using the following code:
public static async Task<BitmapImage> GetProfilePicture(string userId){
BitmapImage profilePicture = new BitmapImage();
StorageFolder pictureFolder = await
ApplicationData.Current.LocalFolder.GetFolderAsync("ProfilePictures");
StorageFile pictureFile = await pictureFolder.GetFileAsync(userId + ".jpg");
IRandomAccessStream stream = await pictureFile.OpenAsync(FileAccessMode.Read);
profilePicture.SetSource(stream);
return profilePicture;
This works as well, so what I would want is to simply do the opposite. The preferred result would look like this:
public static async void SaveBitmapToFile(BitmapImage image, userId){
StorageFolder pictureFolder = await
ApplicationData.Current.LocalFolder.CreateFolderAsync(
"ProfilePictures",CreationCollisionOption.OpenIfExists);
//save bitmap to pictureFolder with name userId.jpg
}
I've searched far and wide trying to find a solution, but I can't seem to find any for the UWP platform. How would a go about saving the Bitmap to file? The extension doesn't have to be .jpg if it would be easier to use another extension.
It would be easier if you used a WriteableBitmap. For example, the first method would then be:
public static async Task<WriteableBitmap> GetProfilePictureAsync(string userId)
{
StorageFolder pictureFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync("ProfilePictures");
StorageFile pictureFile = await pictureFolder.GetFileAsync(userId + ".jpg");
using (IRandomAccessStream stream = await pictureFile .OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
WriteableBitmap bmp = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
await bmp.SetSourceAsync(stream);
return bmp;
}
}
Then you could do:
public static async Task SaveBitmapToFileAsync(WriteableBitmap image, userId)
{
StorageFolder pictureFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ProfilePictures",CreationCollisionOption.OpenIfExists);
var file = await pictureFolder.CreateFileAsync(userId + ".jpg", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenStreamForWriteAsync())
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream.AsRandomAccessStream());
var pixelStream = image.PixelBuffer.AsStream();
byte[] pixels = new byte[image.PixelBuffer.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)image.PixelWidth, (uint)image.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
}
}

Rotating images in WinRT

I have a Windows 8 app in which I want to rotate an image file.
In shot, I want to open an image file, rotate it and save the content back to the file.
Is that possible in WinRT? If so, how? Thanks.
Update:
Base on Vasile's answer, I could do some work on this. However I'm not sure what to do next:
public static async Task RotateImage(StorageFile file)
{
if (file == null)
return;
var data = await FileIO.ReadBufferAsync(file);
// create a stream from the file
var ms = new InMemoryRandomAccessStream();
var dw = new DataWriter(ms);
dw.WriteBuffer(data);
await dw.StoreAsync();
ms.Seek(0);
// find out how big the image is, don't need this if you already know
var bm = new BitmapImage();
await bm.SetSourceAsync(ms);
// create a writable bitmap of the right size
var wb = new WriteableBitmap(bm.PixelWidth, bm.PixelHeight);
ms.Seek(0);
// load the writable bitpamp from the stream
await wb.SetSourceAsync(ms);
wb.Rotate(90);
//How should I save the image to the file now?
}
Ofcourse it is possible. You can do it yourself with a pixel manipulation and create a new WriteableBitmapObject or, you could reuse the already implemented functionality from the WriteableBitmapEx (WriteableBitmap Extensions). You can get it via NuGet.
Here you can find a description of the implemented functionality which it offers, and few short samples.
Use this to save WriteableBitmap to StorageFile
private async Task<StorageFile> WriteableBitmapToStorageFile(WriteableBitmap writeableBitmap)
{
var picker = new FileSavePicker();
picker.FileTypeChoices.Add("JPEG Image", new string[] { ".jpg" });
StorageFile file = await picker.PickSaveFileAsync();
if (file != null && writeableBitmap != null)
{
using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(
BitmapEncoder.JpegEncoderId, stream);
Stream pixelStream = writeableBitmap.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)writeableBitmap.PixelWidth, (uint)writeableBitmap.PixelHeight, 96.0, 96.0, pixels);
await encoder.FlushAsync();
}
return file;
}
else
{
return null;
}
}

How to share a screenshot?

I want to share the screenshot of the app on Twitter, Facebook, etc. This is my code: it saves the picture, but doesn't open the share media task. I know the problem is in the path :{
var wb = new WriteableBitmap(LayoutRoot, new TranslateTransform());
using (var mediaLibrary = new MediaLibrary()) {
using (var stream = new MemoryStream()) {
var fileName = string.Format("{0}.jpg", DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss"));
wb.SaveJpeg(stream, wb.PixelWidth, wb.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
mediaLibrary.SavePicture(fileName, stream);
shareMediaTask = new ShareMediaTask();
shareMediaTask.FilePath = fileName;
shareMediaTask.Show();
}
}
How can I get the saved picture's path?
Isn't it possible to just simply take a screenshot and share it without saving it on the phone?
To get the real path for the MediaLibrary file, you'll need to use the GetPath() extension method, something like;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
...
var picture = mediaLibrary.SavePicture(fileName, stream);
shareMediaTask = new ShareMediaTask();
shareMediaTask.FilePath = picture.GetPath();
shareMediaTask.Show();
For sharing the screen shot it is not required to save the image , in windows 8.1 it is very easy.
Here is the code, enjoy!
async void dataTransferMgr_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
DataRequest request = args.Request;
request.Data.Properties.Title = "Title";
request.Data.Properties.Description = "brief description";
request.Data.SetText("detailed information");
RandomAccessStreamReference imageStreamRef = await ScreenshotToStreamReferenceAsync(yourChartControlName);
request.Data.Properties.Thumbnail = imageStreamRef;
request.Data.SetBitmap(imageStreamRef);
}
private async Task ScreenshotToStreamAsync(FrameworkElement element, IRandomAccessStream stream)
{
var renderTargetBitmap = new Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(element);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var dpi = Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi;
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
dpi,
dpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
private async Task<RandomAccessStreamReference> ScreenshotToStreamReferenceAsync(FrameworkElement element)
{
var ms = new InMemoryRandomAccessStream();
await ScreenshotToStreamAsync(element, ms);
ms.Seek(0);
return RandomAccessStreamReference.CreateFromStream(ms);
}

Categories