如果在應用中,如果想要給app 添加模糊濾鏡,可能第一想到的是第三方類庫,比如 Win2d、lumia Imaging SDK 、WriteableBitmapEx,不可否認,這些類庫功能強大,效果也挺多的。不足就是增加了生成包尺寸,由於增加了相應 dll 的引用,在 app運行時也會增加記憶體占用。 ...
如果在應用中,如果想要給app 添加模糊濾鏡,可能第一想到的是第三方類庫,比如 Win2d、lumia Imaging SDK 、WriteableBitmapEx,不可否認,這些類庫功能強大,效果也挺多的。不足就是增加了生成包尺寸,由於增加了相應 dll 的引用,在 app運行時也會增加記憶體占用。如果只使用一種濾鏡效果,建議直接添加幾十行代碼自己實現,這樣開發、維護成本都會很少。並且由於 .net native 使得 uwp 的運算速度與 C++演算法的運行速度沒有差別了。
這裡只討論高斯模糊濾鏡,感覺這個濾鏡在應用中適當的運用,會讓頁面形象生動,比如圖片背景使用這個濾鏡,會有一些磨砂玻璃的效果。針對高斯模糊的演算法網上也有很多,這裡使用戴震軍
大哥的曾經移植過的 windows phone7 的演算法。這裡主要解決的就是 silverlight(wpf)中 WriteableBitmap 中圖片數據 int[] 數組到 windows runtime(uwp)中 WriteableBitmap中 byte[] 的轉換。
Demo 的運行效果:
1)當不運用濾鏡時 level 為 0(範圍 0-40):
2)當 level 為 2時:
3)當 level 為 10時:
4)當 level 為 40時:
1、顏色值的分析:
在 silverlight 或者 uwp 中,指定一個字體的前景色為綠色半透明:
<TextBlock Text="節約用電" FontSize="30" Foreground="#8800FF00"/>
顯示為:
則在前景色中顏色的設置信息:
2、分析 Silverlight 中 WriteableBitmap 對象中的 int[] 數組
對於 silverlight、wpf 平臺上圖片(jpg、png)的編輯 WriteablBitmap 對象中使用的 int[] 數組,每個 int 值同時包含了 A(alpha)、R(red)、G(green)、B(Blue)四個信息值,每個值的範圍是 0~255。
1)在 silverlight中,獲取圖片像素數據( int[] 數組 ):
// 100像素寬,100像素高 WriteableBitmap bitmap = new WriteableBitmap(100, 100); // 獲取表示點陣圖 2D 紋理的數組。 int[] data = bitmap.Pixels;
當然,這個 int[] 數組是一維數組,線性排列的。
3、分析 UWP 中 WriteableBitmap 類中的 byte[] 數組
對於 Windows Runtime (uwp) 中 WriteableBitmap 類來說,存儲的圖片數據為 byte[] 數組。
例如,獲取一張圖片中的像素數據(msdn 文檔鏈接,WriteableBitmap):(WriteableBitmap 的圖像源數據是基礎像素緩衝區。WriteableBitmap.PixelBuffer不能直接寫入)
using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read)) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream); // Scale image to appropriate size BitmapTransform transform = new BitmapTransform() { ScaledWidth = Convert.ToUInt32(Scenario4WriteableBitmap.PixelWidth), ScaledHeight = Convert.ToUInt32(Scenario4WriteableBitmap.PixelHeight) }; PixelDataProvider pixelData = await decoder.GetPixelDataAsync( BitmapPixelFormat.Bgra8, // WriteableBitmap 使用的是 BGRA 格式 BitmapAlphaMode.Straight, transform, ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation ColorManagementMode.DoNotColorManage ); // 包含圖片的解碼數據。在呈現之前可以被修改 byte[] sourcePixels = pixelData.DetachPixelData(); // 打開一個 image 的流,並且複製到 WriteableBitmap 的 pixel 緩衝區中 using (Stream stream = writeableBitmap.PixelBuffer.AsStream()) { await stream.WriteAsync(sourcePixels, 0, sourcePixels.Length); } }
同樣,對於 1)中描述 100X100 的圖片的數據存儲,因為一個 int 可以包含 4個 byte位的信息,所以對於 100x100 的 int[] 數組值,也就是 10000個 int值組成。當轉換為 byte[] 數組值時,數組長度擴大四倍,變為 100x100x4 = 40000個 byte值。並且 byte 值是按照 B、G、R、A 的順序排列:
4、int[] 與 byte[] 的互相轉換
因為戴震軍大哥移植的濾鏡演算法為 windows phone7 的工程,所以圖片數據的計算是採用 silverlight中 int[] 數組的計算方式,所以這裡我只做了兩件事,一個是把 uwp 工程中源 WriteableBitmap 對象中 byte[] 數組的存儲方式,轉換為 silverlight 中的 int[] 數組的存儲方式,然後對圖片數據進行添加濾鏡演算法的處理,當處理完成時,再把處理結果 int[] 數組的數據再轉換為 uwp 中 WriteableBitmap 的 byte[] 數組的存儲方式:
1)uwp 中 byte[] 數組,轉換為 silverlight 的 int[] 數組:
// 把 uwp 的 WriteableBitmap 對象的 PixelBuffer屬性(IBuffer)轉換為 byte[] 數組 byte[] colorBytes = BufferToBytes(image.PixelBuffer); // 轉換為 silverlight 的 int[] 數組時,長度為 byte[] 數組的四分之一 colorArray = new int[colorBytes.Length / 4]; int a,r, g, b; int i = 0; // 通過 i 自加,來遍歷 byte[] 整個數組 // 通過圖片的寬、高,分別設置 int[] 數組中每個像素的 ARGB 信息 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // int[] 數組的索引 int index = y * width + x; b = colorBytes[i++]; // Blue g = colorBytes[i++]; // Green r = colorBytes[i++]; // Red a = colorBytes[i++]; // Alpha colorArray[index] = (a << 24) | (r << 16) | (g << 8) | b; // 4個 byte值存儲為一個 int 值 } }
2)當把上面的圖片數據,添加高斯模糊濾鏡效果之後,把 silverlight 中 int[] 數組的計算結果,轉換為 uwp 的 byte[] 數組:
// 拷貝 WriteableBitmap new_bitmap = await Utility.BitmapClone(wb); // 添加高斯濾鏡效果 MyImage mi = new MyImage(new_bitmap); GaussianBlurFilter filter = new GaussianBlurFilter(); filter.Sigma = level; filter.process(mi); // 圖片添加完濾鏡的 int[] 數組 int[] array = mi.colorArray; // byte[] 數組的長度是 int[] 數組的 4倍 byte[] result = new byte[array.Length * 4]; // 通過自加,來遍歷 byte[] 數組中的值 int j = 0; for (int i = 0; i < array.Length; i++) { // 同時把 int 值中 a、r、g、b 的排列方式,轉換為 byte數組中 b、g、r、a 的存儲方式 result[j++] = (byte)(array[i]); // Blue result[j++] = (byte)(array[i] >> 8); // Green result[j++] = (byte)(array[i] >> 16); // Red result[j++] = (byte)(array[i] >> 24); // Alpha } // Open a stream to copy the image contents to the WriteableBitmap's pixel buffer using (Stream stream = new_bitmap.PixelBuffer.AsStream()) { await stream.WriteAsync(result, 0, result.Length); } img.Source = new_bitmap;// 把最終 WriteableBitmap 對象賦值給 Image 控制項
5、代碼中會用到的自定義幫助類:
namespace BlurEffect_demo { class Utility { /// <summary> /// WriteableBitmap 的拷貝 /// </summary> /// <param name="bitmap">原</param> /// <returns></returns> public static async Task<WriteableBitmap> BitmapClone(WriteableBitmap bitmap) { WriteableBitmap result = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight); byte[] sourcePixels = Get_WriteableBitmap_bytes(bitmap); //byte[] resultPixels = new byte[sourcePixels.Length]; //sourcePixels.CopyTo(resultPixels, 0); using (Stream resultStream = result.PixelBuffer.AsStream()) { await resultStream.WriteAsync(sourcePixels, 0, sourcePixels.Length); } return result; } /// <summary> /// 獲取 WriteableBitmap 對象中的 byte[] 數組數據 /// </summary> public static byte[] Get_WriteableBitmap_bytes(WriteableBitmap bitmap) { // 獲取對直接緩衝區的訪問,WriteableBitmap 的每個像素都寫入直接緩衝區。 IBuffer bitmapBuffer = bitmap.PixelBuffer; //byte[] sourcePixels = new byte[bitmapBuffer.Length]; //Windows.Security.Cryptography.CryptographicBuffer.CopyToByteArray(bitmapBuffer, out sourcePixels); //bitmapBuffer.CopyTo(sourcePixels); using (var dataReader = DataReader.FromBuffer(bitmapBuffer)) { var bytes = new byte[bitmapBuffer.Capacity]; dataReader.ReadBytes(bytes); return bytes; } } } }
6、高斯濾鏡演算法這裡就不列出來了,具體可以參考 demo 工程
工程地址:link
參考閱讀:
1)戴震軍 :
blog : http://www.cnblogs.com/daizhj
github : https://github.com/daizhenjun/ImageFilterForWindowsPhone
2)WriteableBitmap.PixelBuffer property :https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.writeablebitmap.pixelbuffer.aspx
3)WriteableBitmap class : https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.writeablebitmap.aspx
4)WriteableBitmapEx : http://writeablebitmapex.codeplex.com/releases/view/612952
5)Getting Pixels of an Element(WriteableBitmap) // https://social.msdn.microsoft.com/Forums/en-US/39b3c702-caed-47e4-b7d3-b51d75cbca9b/getting-pixels-of-an-element-writeablebitmap?forum=winappswithcsharp
6) Windows 8 WriteableBitmap Pixel Arrays in C# and C++ : http://www.tuicool.com/articles/fQNvUz
7) 各種流轉換 : http://www.cnblogs.com/hebeiDGL/p/3428743.html
8) XAML images sample : https://code.msdn.microsoft.com/windowsapps/0f5d56ae-5e57-48e1-9cd9-993115b027b9/
9) 重新想象 Windows 8 Store Apps (29) - 圖片處理 : http://www.cnblogs.com/webabcd/archive/2013/05/27/3101069.html