Windows8.1画热度图 - 坑

想要的效果

如上是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.

Note  Windows Phone: The contents of a WebView control can‘t be rendered into a RenderTargetBitmap.

  • Content that can‘t be captured      will appear as blank in the captured image, but other content in the same      visual tree can still be captured and will render (the presence of content      that can‘t be captured won‘t invalidate the entire capture of that XAML composition).
  • Content that‘s in the XAML      visual tree but offscreen can be captured, so long as it‘s not Visibility=Collapsed or in the other restricted      cases.

From <https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.rendertargetbitmap.aspx>

3、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模式画的图如下:

4、读取要处理图形的每个像素

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

5、悲催的地方
通过上面第二部分读到的像素值,是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部分中,对像素颜色进行了对调。

但是,得到的是这么一个悲催的图像:

颜色不对!热度效果也没有!

求指点,请帮助!

-------------------------------------------------------

6、仔细检查了一下,第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缘故?

时间: 2024-11-20 08:46:52

Windows8.1画热度图 - 坑的相关文章

如何使用excel画甘特图

甘特图小伙伴们都非常的熟悉,首先小编简单的向各位小伙伴介绍一下上面是甘特图,甘特图内在思想简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间.基本是一条线条图,横轴表示时间,纵轴表示活动(项目),线条表示在整个期间上计划和实际的活动完成情况.它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比.管理者由此可便利地弄清一项任务(项目)还剩下哪些工作要做,并可评估工作进度. 还记得在软考的时候,甘特图经常和PERT图放在一起,考察我们,既然提到了PERT

推荐几款画韦恩图的在线工具

(1)http://bioinformatics.psb.ugent.be/webtools/Venn/ 操作简单,不仅画出了韦恩图,还很贴心的给出了数据统计的结果并列出了各个部分对应的ID,且提供直接下载.缺点是颜色和大小不可调. (2)http://bioinfogp.cnb.csic.es/tools/venny/index.html 这款比较漂亮,操作也很简单,但是不能根据列表大小调整生成的圆圈大小. (3)http://www.cs.kent.ac.uk/people/staff/pj

利用Graphviz画出图

graphviz官网:http://www.graphviz.org/ 背景:有画图需要,之前见到别人用graphviz画,画出来的图漂亮,且自动帮你排版安排布局,所以自己想尝试用它画. 其中遇到的几个问题 win系统下载msi安装包后进行安装,安装不成功,报错信息是win电脑却少什么包,最后转向Mac画图. 找到两个graphviz online的网站(http://www.webgraphviz.com/  和),都不太好用,不是对中文支持不好,就是稍微复杂一些的图他就画不出来了. Mac

python中matplotlib画折线图实例(坐标轴数字、字符串混搭及标题中文显示)

最近在用python中的matplotlib画折线图,遇到了坐标轴 "数字+刻度" 混合显示.标题中文显示.批量处理等诸多问题.通过学习解决了,来记录下.如有错误或不足之处,望请指正. 一.最简单的基本框架如下:已知x,y,画出折线图并保存.此时x和y均为数字. 1 # -*- coding: utf-8 -*- 2 3 import matplotlib.pyplot as plt #引入matplotlib的pyplot子库,用于画简单的2D图 4 import random 5

iOS 画环形图

由于新项目的的需求,需要画环形图,由于以前都没接触过这一类(我是菜鸟),去cocochina山找到了一个案例,个人觉得还可以,分享一下 github 地址https://github.com/zhouxing5311/ZZCircleProgress 这个使用起来非常的简单 作者开放了很多接口,可以根据自己的需求进行更改 一般就是声明一个累类就Ok了 具体实现的代码如下 可能我的代码有点不一样,我自己加入了一些元素进去 //无小圆点.同动画时间 self.zzCircleProgress = [

使用Mysql Workbench 画E-R图

MySQL Workbench 是一款专为MySQL设计的ER/数据库建模工具.你可以用MySQL Workbench设计和创建新的数据库图示,建立数据库文档,以及进行复杂的MySQL 迁移.这里介绍使用MySQL Workbench画E-R图. 方法/步骤 在开始菜单里找到软件,点击打开 启动软件过后,不需要连接数据库(很多人就直接连接数据库了所以找不到设计ER模型的地方) 点击"+" ,进入模型设计界面 双击Add Diagram 进入如下设计界面 点击工具栏表格,并在设计区域点击

XCL-Charts画面积图(AreaChart) 例1

面积图的一个例子,做面积图时发现注释位置放图上方更好处理.改了下. 面积图的网格和轴处理与其它图不一样, 它是用覆盖的范围来表示其势力范围,标签轴无需在突出一段出来.这里处理了下. 代码: //图基类 chart = new AreaChart(); //图所占范围大小 chart.setChartRange(0, 0, this.mScrWidth , this.mScrHeight ); //标签1对应的数据集 LinkedList<Double> valueA= new LinkedLi

Astah画类图——第八周作业

使用Astah画UML类图经验总结 从学习需求工程与UML开始,就开始接触到Astah这款软件,但是当时完全是为了对UML各种图的了解加深才使用了这款软件.当时画图,都是完全凭借自己想,并没有考虑实际情况,而且画的图都是很简单的,甚至有些图是已经给定的,只要在软件上再画一遍来了解软件的使用.所以当时,对与这款软件并没有很好的使用.在最近进行软件项目管理的时候,需要画类图,我再次使用了这个软件.由于这次的工程涉及的类很多(和我之前遇到的相比),所以画了比较长的的时间,我也从这个软件中,摸索到了一些

画时序图工具TimingDesigner 9.2 安装指导

画时序图工具TimingDesigner 9.2 安装指导 先上文件下载链接:http://bbs.eetop.cn/viewthread.php?tid=250446&;highlight=timing%2Bde%24igner 安装.破解方法:一.安装1)解压“TimingDesigner_v9.2_win.zip”后有两个文件:“td.win32.td92.exe”和“TDlic.dat”:2)安装“td.win32.td92.exe”到C盘或D盘,此处选择安装目录为“D:\Program