Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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;
}
@borisdv
Copy link

borisdv commented Aug 8, 2017

Hi, I tried your code ... set the quality to 0.5d or even to 0.1d .. image was awful but the size remains the same. I was testing this on big jpeg pictures with size around 6+mb. Is this behavior ok or am i doing something wrong ? thanks for answer :)

@alexsorokoletov
Copy link
Author

Hi @borisdv!
Should be working just fine.
Can you share a sample app and pictures that you use for tests?

@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.

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