想要的效果ios
如上是silverlight版本。原理是設定一個調色板,爲256的漸變色(存在一個png文件中,寬度爲256,高度爲1),而後針對要處理的距離矩陣圖形,取圖片中每一個像素的Alpha值做爲索引,對應到調色板的顏色。每一個像素處理以後,則造成上面的熱度圖。該圖主要表達了一個數據分佈的密度。web
網絡上有一個Gildor.HeatmapDemos工程,我主要參考了SL版本。要注意elipseRadius,若是太小,即每一個圓彼此不相交,則看不到熱度效果,因此代碼設置初始值爲100。(上圖的數值初始化部分,對應代碼以下)canvas
private List<Point> plist = new List<Point>(); private void drawHeatMap () { plist.Clear(); plist.Add(new Point(0.15*map.ActualWidth, 0.49*map.ActualHeight)); plist.Add(new Point(0.20 * map.ActualWidth, 0.25 * map.ActualHeight)); plist.Add(new Point(0.10 * map.ActualWidth, 0.15 * map.ActualHeight)); plist.Add(new Point(0.12 * map.ActualWidth, 0.05 * map.ActualHeight)); plist.Add(new Point(0.17 * map.ActualWidth, 0.34 * map.ActualHeight)); plist.Add(new Point(0.17 * map.ActualWidth, 0.33 * map.ActualHeight)); plist.Add(new Point(0.16 * map.ActualWidth, 0.33 * map.ActualHeight)); heatMap.Source = _heatMapGen.GenerateHeatMap ( plist, new Size (map.ActualWidth, map.ActualHeight)); }
我須要在windows 8.1的RT版本中實現相似功能。windows
一、讀取調色板文件網絡
Uri uri = new Uri("ms-appx:///assets/bookpage/Palette.bmp"); RandomAccessStreamReference streamRef = RandomAccessStreamReference.CreateFromUri(uri); using (IRandomAccessStreamWithContentType fileStream = await streamRef.OpenReadAsync()) { BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream); BitmapFrame frame = await decoder.GetFrameAsync(0); PixelDataProvider pixelProvider = await frame.GetPixelDataAsync(); this.palette = pixelProvider.DetachPixelData(); }
二、把UIElement轉換爲圖形app
Windows 8.1以前,沒有RenderTargetBitmap類。最開始我採用了codeplex上的WriteableBitmapExtensions類,後來發現8.1中已經增長了這個類。dom
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(); await renderTargetBitmap.RenderAsync(canvasSpitList);//, (int)pdfBorder.Width, (int)pdfBorder.Height);
第二行就會把UIElement及全部子element寫入到bitmap中。ide
關於RenderTargetBitmap有無數坑,msdn以下:ui
There are a few scenarios for XAML-composed visual content that you can't capture to a RenderTargetBitmap:this
Note Windows Phone: The contents of a WebView control can't be rendered into a RenderTargetBitmap.
三、RadialGradientBrush在Windows RT 8.1中沒有!只有LinearGradientBrush。MSDN說法這裏
GradientBrush is the parent class for LinearGradientBrush. The Windows Runtime XAML vocabulary doesn't support RadialGradientBrush.
From <https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.gradientbrush.aspx>
我在SL中用Linear模式畫的圖以下:
四、讀取要處理圖形的每一個像素
int width = renderTargetBitmap.PixelWidth; int height = renderTargetBitmap.PixelHeight; var buf = await renderTargetBitmap.GetPixelsAsync(); var stream = buf.AsStream(); byte[] srcPixels = new byte[stream.Length]; stream.Read(srcPixels, 0, (int)stream.Length);
捨棄R/G/B值,只保留A,而後讀取對應的調色板顏色,進行替換。
var dstPixels = new byte[4 * width * height]; for (int i = 0; i < srcPixels.Length; i += 4) { //int pixelIndex = ((srcPixels[i + 3] << 24) + (srcPixels[i + 2] << 16) + (srcPixels[i + 1] << 8) + (srcPixels[i + 0])); byte pixelIndex = srcPixels[i + 3];//只取Alpha通道的值 if ((srcPixels[i + 0] == 0) && (srcPixels[i + 1] == 0) && (srcPixels[i + 2] == 0) && (srcPixels[i + 3] == 0)) continue; //winform中,pixelProvider.DetachPixelData,顏色順序從低到高字節爲:A,R,G,B,包括開始的palette取到的也是A,R,G,B //metro中,renderTargetBitmap.GetPixelsAsync,顏色順序從低到高字節爲:B,G,R,A dstPixels[i + 0] = this.palette[(byte)(~(4 * pixelIndex + 3))];//B<->A dstPixels[i + 1] = this.palette[(byte)(~(4 * pixelIndex + 2))];//G<->R dstPixels[i + 2] = this.palette[(byte)(~(4 * pixelIndex + 1))];//R<->G dstPixels[i + 3] = this.palette[(byte)(~(4 * pixelIndex + 0))];//A<->B } var bmp = new WriteableBitmap(width, height);//(container, null); WriteableBitmapExtensions.FromByteArray(bmp, dstPixels);
五、悲催的地方
經過上面第二部分讀到的像素值,是A/R/G/B順序,用winform讀取,也是同樣的結果
private void button1_Click(object sender, EventArgs e) { StringBuilder sb = new StringBuilder(); using (Bitmap bmp = new Bitmap(@"c:\1.png")) { using (Graphics g = this.CreateGraphics()) { for (int i = 0; i < 256; i++) { var col = bmp.GetPixel(i, 0); sb.AppendFormat("{0:X8},", col.ToArgb()); using (SolidBrush brush = new System.Drawing.SolidBrush(col)) { g.FillRectangle(brush, new Rectangle(5 * i, 10, 3, 3)); } } } } MessageBox.Show(sb.ToString()); }
獲得的像素順序:
好比第一個亮黃顏色,在這裏與palette部分讀到都是A/R/G/B順序,可是在上面第4部分,讀到的確是B/G/R/A部分。因此第4部分中,對像素顏色進行了對調。
可是,獲得的是這麼一個悲催的圖像:
顏色不對!熱度效果也沒有!
求指點,請幫助!
-------------------------------------------------------
六、仔細檢查了一下,第2部分的palette取到的是R/G/B/A順序,因此第4部分的調色板代碼修改以下:
//winform中,顏色順序從低到高字節爲:A,R,G,B //palette中,pixelProvider.DetachPixelData取到的倒是R,G,B,A //metro中,renderTargetBitmap.GetPixelsAsync,顏色順序從低到高字節爲:B,G,R,A dstPixels[i + 0] = this.palette[(byte)((4 * pixelIndex + 2))];//B<->A dstPixels[i + 1] = this.palette[(byte)((4 * pixelIndex + 1))];//G<->R dstPixels[i + 2] = this.palette[(byte)((4 * pixelIndex + 0))];//R<->G dstPixels[i + 3] = this.palette[(byte)((4 * pixelIndex + 3))];//A<->B
可是熱度效果依然不對,難道是由於LinearGradientBrush緣故?