[uwp]ImageSource和byte[]相互轉換


最近做一個小app遇到一個問題,到目前還沒有比較好的解決方法(可能是我查的資料不夠多)

需求如下:

1.把一個Image中的圖像保存到字節數組;

2.把字節數組轉換為ImageSource,通過Image控件展示圖像.

上面兩個需求恰恰是相反的過程,為了實現這個,我倒網上找了好多,但基本都是wp7,wp8,wpf的方案,在win10上沒法用。。糾結。

后來在知乎日報uwp的源碼中發現了一個把ImageSource存儲為文件的方法。(github:https://github.com/sherlockchou86/ZhiHuDaily.UWP),感謝作者為win10生態圈的貢獻。

代碼如下:

public async Task SaveImageAsync(WriteableBitmap image, string filename)
        {
            try
            {
                if (image == null)
                {
                    return;
                }
                Guid BitmapEncoderGuid = BitmapEncoder.JpegEncoderId;
                if(filename.EndsWith("jpg"))
                        BitmapEncoderGuid = BitmapEncoder.JpegEncoderId;
                else if(filename.EndsWith("png"))
                        BitmapEncoderGuid = BitmapEncoder.PngEncoderId;
                else if(filename.EndsWith("bmp"))
                        BitmapEncoderGuid = BitmapEncoder.BmpEncoderId;
                else if(filename.EndsWith("tiff"))
                        BitmapEncoderGuid = BitmapEncoder.TiffEncoderId;
                else if(filename.EndsWith("gif"))
                        BitmapEncoderGuid = BitmapEncoder.GifEncoderId;
                var folder = await _local_folder.CreateFolderAsync("images_cache", CreationCollisionOption.OpenIfExists);
                var file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);

                using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
                    Stream pixelStream = image.PixelBuffer.AsStream();
                    byte[] pixels = new byte[pixelStream.Length];
                    await pixelStream.ReadAsync(pixels, 0, pixels.Length);
                    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
                              (uint)image.PixelWidth,
                              (uint)image.PixelHeight,
                              96.0,
                              96.0,
                              pixels);
                    await encoder.FlushAsync();
                }
            }
            catch
            {

            }
        }
SaveImageAsync

 

有了這個,我的需求就能解決了,因為在上面的代碼中,有一個隨機訪問流ras,所以我們可以把ras利用拓展方法AsStream()轉化為普通的流,然后用Read()或者ReadAsync()讀取到字節數組即可。

於是需求一的代碼如下:

public static async Task<byte[]> SaveToBytesAsync(this ImageSource imageSource)
        {
            byte[] imageBuffer;
            var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
            var file = await localFolder.CreateFileAsync("temp.jpg", CreationCollisionOption.ReplaceExisting);
            using (var ras = await file.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
            {
                WriteableBitmap bitmap = imageSource as WriteableBitmap;
                var stream = bitmap.PixelBuffer.AsStream();
                byte[] buffer = new byte[stream.Length];
                await stream.ReadAsync(buffer, 0, buffer.Length);
                BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, ras);
                encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)bitmap.PixelWidth, (uint)bitmap.PixelHeight, 96.0, 96.0, buffer);
                await encoder.FlushAsync();

                var imageStream = ras.AsStream();
                imageStream.Seek(0, SeekOrigin.Begin);
                imageBuffer = new byte[imageStream.Length];
                var re = await imageStream.ReadAsync(imageBuffer, 0, imageBuffer.Length);
                
            }
            await file.DeleteAsync(StorageDeleteOption.Default);
            return imageBuffer; 
        }

值得注意的幾點:

  1.用了拓展方法;

  2.ImageSource一般可以通過BitmapImage和WriteableImage來設置,但這兒只能用WriteableImage,因為要用到接口IWriteableBitmap;

  3.ras隨機訪問流轉為普通FCL普通流后,用Seek將Positon設置到0,讀出的數據出錯;

  4.注意圖像編碼的設置,這兒只針對jpeg格式;

  5.這是一個投機的辦法,先存儲為文件,再存儲為字節數組,但核心還是一個ras(IRandomAccessSteam).

 

需求二代碼如下:

public static async Task<ImageSource> SaveToImageSource(this byte[] imageBuffer)
        {
            ImageSource imageSource = null;
            using (MemoryStream stream = new MemoryStream(imageBuffer))
            {
                var ras = stream.AsRandomAccessStream();
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, ras);
                var provider = await decoder.GetPixelDataAsync();
                byte[] buffer = provider.DetachPixelData();
                WriteableBitmap bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
                await bitmap.PixelBuffer.AsStream().WriteAsync(buffer, 0, buffer.Length);
                imageSource = bitmap;
            }
            return imageSource;
        }

 

注意點如下:

  1.解碼用的還是jpeg;

  2.還是用的WriteableBitmap,主要還是他的接口決定的;

 

tips:如果照的上面的代碼寫,某些方法或屬性下面還是有紅色波浪線的話,記着選中它,然后按Shift+alt+F10,Visual Studio幫你解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM