想要的效果
如上是silverlight版本。原理是设定一个调色板,为256的渐变色(存在一个png文件中,宽度为256,高度为1),然后针对要处理的距离矩阵图形,取图片中每个像素的Alpha值作为索引,对应到调色板的颜色。每个像素处理之后,则形成上面的热度图。该图主要表达了一个数据分布的密度。
网络上有一个Gildor.HeatmapDemos工程,我主要参考了SL版本。要注意elipseRadius,如果过小,即每个圆彼此不相交,则看不到热度效果,所以代码设置初始值为100。(上图的数值初始化部分,对应代码如下)
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版本中实现类似功能。
1、读取调色板文件
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(); }
2、把UIElement转换为图形
Windows 8.1之前,没有RenderTargetBitmap类。最开始我采用了codeplex上的WriteableBitmapExtensions类,后来发现8.1中已经增加了这个类。
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(); await renderTargetBitmap.RenderAsync(canvasSpitList);//, (int)pdfBorder.Width, (int)pdfBorder.Height);
第二行就会把UIElement及所有子element写入到bitmap中。
关于RenderTargetBitmap有无数坑,msdn如下:
There are a few scenarios for XAML-composed visual content that you can't capture to a RenderTargetBitmap:
- Video content in a MediaElement or CaptureElement can't be captured using RenderTargetBitmap. That includes capturing frames from within video content.
- Custom DirectX content (your own swap chain) inside a SwapChainBackgroundPanel or SwapChainPanel can't be captured using RenderTargetBitmap.
- Content that's in the tree but with its Visibility set to Collapsed won't be captured.
- Content that's not directly connected to the XAML visual tree and the content of the main window won't be captured. This includes Popup content, which is considered to be like a sub-window.