如何在C#WinRT / winmd中调整图像大小?

我有一个简单的问题,但到目前为止我还没有找到答案:如何在C#WinRT / WinMD项目中调整jpeg图像并将其保存为新的jpeg?

我正在开发Windows 8 Metro应用程序,用于从某个站点下载日常图像并将其显示在Live Tile上。 问题是图像必须小于1024×1024且小于200kB,否则它将不会显示在磁贴上: http : //msdn.microsoft.com/en-us/library/windows/apps/hh465403.aspx

如果我得到更大的图像,如何调整它以适应Live Tile? 我正在考虑简单的resize,如宽度/ 2和高度/ 2,同时保持纵横比。

这里的具体要求是代码必须作为Windows运行时组件运行,因此WriteableBitmapEx库在这里不起作用 – 它仅适用于常规WinRT项目。 WriteableBitmapEx甚至还有一个分支作为winmd项目,但它还远没有准备好。

如何缩放和裁剪的示例:

async private void BitmapTransformTest() { // hard coded image location string filePath = "C:\\Users\\Public\\Pictures\\Sample Pictures\\fantasy-dragons-wallpaper.jpg"; StorageFile file = await StorageFile.GetFileFromPathAsync(filePath); if (file == null) return; // create a stream from the file and decode the image var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream); // create a new stream and encoder for the new image InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream(); BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder); // convert the entire bitmap to a 100px by 100px bitmap enc.BitmapTransform.ScaledHeight = 100; enc.BitmapTransform.ScaledWidth = 100; BitmapBounds bounds = new BitmapBounds(); bounds.Height = 50; bounds.Width = 50; bounds.X = 50; bounds.Y = 50; enc.BitmapTransform.Bounds = bounds; // write out to the stream try { await enc.FlushAsync(); } catch (Exception ex) { string s = ex.ToString(); } // render the stream to the screen BitmapImage bImg = new BitmapImage(); bImg.SetSource(ras); img.Source = bImg; // image element in xaml } 

更简单的代码来重新调整图像大小,而不是裁剪。 以下代码将图像重新调整为80×80

 using (var sourceStream = await sourceFile.OpenAsync(FileAccessMode.Read)) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream); BitmapTransform transform = new BitmapTransform() { ScaledHeight = 80, ScaledWidth = 80 }; PixelDataProvider pixelData = await decoder.GetPixelDataAsync( BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.DoNotColorManage); using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite)) { BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream); encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, 80, 80, 96, 96, pixelData.DetachPixelData()); await encoder.FlushAsync(); } } 

资源

所以这是我在大量谷歌搜索和试用/错误编码后带来的解决方案:

这里的目标是找出如何在WinRT中操作图像,特别是在后台任务中 。 后台任务比常规WinRT项目更受限制,因为它们必须是Windows运行时组件类型。 NuGet目标WinRT上99%的可用库仅针对默认的WinRT项目,因此它们无法在Windows运行时组件项目中使用。

起初我尝试使用着名的WriteableBitmapEx库 – 将必要的代码移植到我的winmd项目中。 甚至WBE项目的分支都以winmd为目标 ,但尚未完成。 我在将[ReadOnlyArray],[WriteOnlyArray]属性添加到类型数组的方法参数之后以及在将项目命名空间更改为不以“Windows”开头的内容(winmd项目限制)之后编译。

即使我能够在我的后台任务项目中使用这个库它也无法正常工作,因为正如我发现的那样,必须在UI线程中实例化WriteableBitmap,就我在后台任务中所知,这是不可能的。

与此同时,我也发现了这篇关于WinRT中图像处理的MSDN文章 。 那里的大多数样本只在JavaScript部分中,所以我必须先将它转换为C#。 我还在StackOverflow上找到了关于WinRT中图像处理的这篇有用的文章 。

 internal static async Task LoadTileImageInternalAsync(string imagePath) { string tileName = imagePath.GetHashedTileName(); StorageFile origFile = await ApplicationData.Current.LocalFolder.GetFileAsync(imagePath); // open file for the new tile image file StorageFile tileFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(tileName, CreationCollisionOption.ReplaceExisting); using (IRandomAccessStream tileStream = await tileFile.OpenAsync(FileAccessMode.ReadWrite)) { // get width and height from the original image IRandomAccessStreamWithContentType stream = await origFile.OpenReadAsync(); ImageProperties properties = await origFile.Properties.GetImagePropertiesAsync(); uint width = properties.Width; uint height = properties.Height; // get proper decoder for the input file - jpg/png/gif BitmapDecoder decoder = await GetProperDecoder(stream, imagePath); if (decoder == null) return; // should not happen // get byte array of actual decoded image PixelDataProvider data = await decoder.GetPixelDataAsync(); byte[] bytes = data.DetachPixelData(); // create encoder for saving the tile image BitmapPropertySet propertySet = new BitmapPropertySet(); // create class representing target jpeg quality - a bit obscure, but it works BitmapTypedValue qualityValue = new BitmapTypedValue(TargetJpegQuality, PropertyType.Single); propertySet.Add("ImageQuality", qualityValue); // create the target jpeg decoder BitmapEncoder be = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, tileStream, propertySet); be.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, width, height, 96.0, 96.0, bytes); // crop the image, if it's too big if (width > MaxImageWidth || height > MaxImageHeight) { BitmapBounds bounds = new BitmapBounds(); if (width > MaxImageWidth) { bounds.Width = MaxImageWidth; bounds.X = (width - MaxImageWidth) / 2; } else bounds.Width = width; if (height > MaxImageHeight) { bounds.Height = MaxImageHeight; bounds.Y = (height - MaxImageHeight) / 2; } else bounds.Height = height; be.BitmapTransform.Bounds = bounds; } // save the target jpg to the file await be.FlushAsync(); } } private static async Task GetProperDecoder(IRandomAccessStreamWithContentType stream, string imagePath) { string ext = Path.GetExtension(imagePath); switch (ext) { case ".jpg": case ".jpeg": return await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, stream); case ".png": return await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, stream); case ".gif": return await BitmapDecoder.CreateAsync(BitmapDecoder.GifDecoderId, stream); } return null; } 

在此示例中,我们打开一个文件,将其解码为字节数组,然后将其编码回具有不同大小/格式/质量的新文件。

结果是即使在Windows运行时组件类和没有WriteableBitmapEx库的情况下也可以完全运行图像处理。

这是更短的版本,没有访问像素数据的开销。

 using (var sourceFileStream = await sourceFile.OpenAsync(Windows.Storage.FileAccessMode.Read)) using (var destFileStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite)) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceFileStream); BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(destFileStream, decoder); enc.BitmapTransform.ScaledWidth = newWidth; enc.BitmapTransform.ScaledHeight = newHeight; await enc.FlushAsync(); await destFileStream.FlushAsync(); } 

我只花了最后一个半小时试图弄清楚这一个,我有一个字节数组是一个JPG并尝试给出的答案……我无法让它工作所以我提出了一个新的答案……希望这会帮助其他人…我正在将JPG转换为250/250像素

 private async Task ByteArrayToBitmapImage(byte[] byteArray) { BitmapImage image = new BitmapImage(); using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) { using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0))) { writer.WriteBytes((byte[])byteArray); writer.StoreAsync().GetResults(); } image.SetSource(stream); } image.DecodePixelHeight = 250; image.DecodePixelWidth = 250; return image; } 

如果你想要高质量的图像然后在BitmapTransform中添加InterpolationMode = BitmapInterpolationMode.Fant,这里是示例

`public static async Task ResizeImage(Windows.Storage.StorageFile imgeTOBytes,int maxWidth,int maxHeight){

  using (var sourceStream = await imgeTOBytes.OpenAsync(FileAccessMode.Read)) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream); double widthRatio = (double)maxWidth / decoder.OrientedPixelWidth; double heightRatio = (double)maxHeight / decoder.OrientedPixelHeight; double scaleRatio = Math.Min(widthRatio, heightRatio); uint aspectHeight = (uint)Math.Floor((double)decoder.OrientedPixelHeight * scaleRatio); uint aspectWidth = (uint)Math.Floor((double)decoder.OrientedPixelWidth * scaleRatio); BitmapTransform transform = new BitmapTransform() { InterpolationMode = BitmapInterpolationMode.Fant, ScaledHeight = aspectHeight, ScaledWidth = aspectWidth }; PixelDataProvider pixelData = await decoder.GetPixelDataAsync( BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.DoNotColorManage); using (var destinationStream = await imgeTOBytes.OpenAsync(FileAccessMode.ReadWrite)) { BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destinationStream); encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, aspectWidth, aspectHeight, 96, 96, pixelData.DetachPixelData()); await encoder.FlushAsync(); } }`