How to resize Image in C# WinRT/winmd?

前端 未结 6 675
执念已碎
执念已碎 2020-11-27 18:54

I\'ve got simple question, but so far I\'ve found no answer: how to resize jpeg image in C# WinRT/WinMD project and save it as new jpeg?

I\'m developing Windows 8 Me

相关标签:
6条回答
  • 2020-11-27 19:05

    I just spent the last hour and half trying to figure this one out, I have a byte array that is a JPG and tried the answer given... I could not get it to work so I am putting up a new answer... Hopefully this will help someone else out... I am converting the JPG to 250/250 pixels

    private async Task<BitmapImage> 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;            
        }
    
    0 讨论(0)
  • 2020-11-27 19:14

    Example of how to scale and crop taken from here:

    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
    
    }
    
    0 讨论(0)
  • 2020-11-27 19:22

    So here is my solution I came with after lot of googling and trial/error coding:

    The goal here was to find out, how to manipulate images in WinRT, specifically in Background Tasks. Background Tasks are even more limited than just regular WinRT projects, because they must be of type Windows Runtime Component. 99% of available libraries on NuGet targeting WinRT are targeting only the default WinRT projects, therefore they cannot be used in Windows Runtime Component projects.

    At first I tried to use the well-known WriteableBitmapEx library - porting the necessary code to my winmd project. There is even branch of the WBE project targeting winmd, but it is unfinished. I made it compile after adding [ReadOnlyArray], [WriteOnlyArray] attributes to method parameters of type array and also after changing the project namespace to something not starting with "Windows" - winmd project limitation.

    Even though I was able to use this library in my Background Task project it wasn't working, because, as I discovered, WriteableBitmap must be instantiated in UI thread and this is not possible as far as I know in Background Task.

    In the meantime I have also found this MSDN article about Image manipulation in WinRT. Most of samples there are only in the JavaScript section, so I had to convert it to C# first. I've also found this helpful article on StackOverflow about image manipulation in 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<BitmapDecoder> 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;
    }
    

    In this sample we open one file, decode it into byte array, and encode it back into new file with different size/format/quality.

    The result is fully working image manipulation even in Windows Runtime Component Class and without WriteableBitmapEx library.

    0 讨论(0)
  • 2020-11-27 19:26

    More simpler code to re-size the image, not crop. The below code re-size the image as 80x80

    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();
        }
    }
    

    Source

    0 讨论(0)
  • 2020-11-27 19:26

    Here is even shorter version, without overhead of accessing pixel data.

    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();
    }
    
    0 讨论(0)
  • 2020-11-27 19:26

    if you want quality image then add InterpolationMode = BitmapInterpolationMode.Fant in BitmapTransform , here is example

    ` 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();
                }
            }`
    
    0 讨论(0)
提交回复
热议问题