Windows8.1画热度图 - 坑

时间:2015-01-24 00:16:14   收藏:0   阅读:337

想要的效果

技术分享

如上是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));
        }
View Code

 

我需要在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();
            }
View Code

2、把UIElement转换为图形

Windows 8.1之前,没有RenderTargetBitmap类。最开始我采用了codeplex上的WriteableBitmapExtensions类,后来发现8.1中已经增加了这个类。

技术分享
            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
            await renderTargetBitmap.RenderAsync(canvasSpitList);//, (int)pdfBorder.Width, (int)pdfBorder.Height);            
View Code

第二行就会把UIElement及所有子element写入到bitmap中。

关于RenderTargetBitmap有无数坑,msdn如下:

There are a few scenarios for XAML-composed visual content that you can‘t capture to a RenderTargetBitmap:

技术分享

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

 

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

舍弃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);
View Code

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());
        }
View Code

得到的像素顺序:
技术分享

 

比如第一个亮黄颜色,在这里与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
View Code

 

但是热度效果依然不对,难道是因为LinearGradientBrush缘故?

技术分享

 

原文:http://www.cnblogs.com/juqiang/p/4245155.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!