Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save alexsorokoletov/71431e403c0fa55f1b4c942845a3c850 to your computer and use it in GitHub Desktop.
Save alexsorokoletov/71431e403c0fa55f1b4c942845a3c850 to your computer and use it in GitHub Desktop.
How to convert image to JPEG and specify quality (q) parameter in UWP C# XAML
/// <summary>
/// Converts source image file to jpeg of defined quality (0.85)
/// </summary>
/// <param name="sourceFile">Source StorageFile</param>
/// <param name="outputFile">Target StorageFile</param>
/// <returns></returns>
private async Task<StorageFile> ConvertImageToJpegAsync(StorageFile sourceFile, StorageFile outputFile)
{
//you can use WinRTXamlToolkit StorageItemExtensions.GetSizeAsync to get file size (if you already plugged this nuget in)
var sourceFileProperties = await sourceFile.GetBasicPropertiesAsync();
var fileSize = sourceFileProperties.Size;
var imageStream = await sourceFile.OpenReadAsync();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
using (imageStream)
{
var decoder = await BitmapDecoder.CreateAsync(imageStream);
var pixelData = await decoder.GetPixelDataAsync();
var detachedPixelData = pixelData.DetachPixelData();
pixelData = null;
//0.85d
double jpegImageQuality = Constants.ImageAttachStartingImageQuality;
//since we're using MvvmCross, we're outputing diagnostic info to MvxTrace, you can use System.Diagnostics.Debug.WriteLine instead
Mvx.TaggedTrace(MvxTraceLevel.Diagnostic, "ImageService", $"Source image size: {fileSize}, trying Q={jpegImageQuality}");
var imageWriteableStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite);
ulong jpegImageSize = 0;
using (imageWriteableStream)
{
var propertySet = new BitmapPropertySet();
var qualityValue = new BitmapTypedValue(jpegImageQuality, Windows.Foundation.PropertyType.Single);
propertySet.Add("ImageQuality", qualityValue);
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, imageWriteableStream, propertySet);
//key thing here is to use decoder.OrientedPixelWidth and decoder.OrientedPixelHeight otherwise you will get garbled image on devices on some photos with orientation in metadata
encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, decoder.OrientedPixelWidth, decoder.OrientedPixelHeight, decoder.DpiX, decoder.DpiY, detachedPixelData);
await encoder.FlushAsync();
await imageWriteableStream.FlushAsync();
jpegImageSize = imageWriteableStream.Size;
}
Mvx.TaggedTrace(MvxTraceLevel.Diagnostic, "ImageService", $"Final image size now: {jpegImageSize}");
}
stopwatch.Stop();
Mvx.TaggedTrace(MvxTraceLevel.Diagnostic, "ImageService", $"Time spent optimizing image: {stopwatch.Elapsed}");
return outputFile;
}
@JohnnyWestlake
Copy link

Problem here is imageWriteableStream needs to set it's size to Zero before creating the encoder using it, as BitmapEncoder doesn't clear any existing data in the stream - so if the file already has content you have some issues.

@alexsorokoletov
Copy link
Author

@JohnnyWestlake - interesting. If I understand what you're saying is that if outputFile already exists and has content - it will not work correctly?

@GailBowen
Copy link

Thank you - you're a lifesaver! I couldn't find a working example anywhere else.

@rjaeckel
Copy link

rjaeckel commented Oct 6, 2024

Cheers for this solution. Made small changes to adopt into c#/uwp.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment