none
还是关于画点的几个疑问? RRS feed

  • 问题

  • 我现在需要在canvas上进行画点,用的办法也是版主建议的:

                EllipseGeometry dot= new EllipseGeometry();
                dot.RadiusX = dot.RadiusY = 0.3;// radius;
                dot.Center = position;

    我有几个疑问:

    第一:关于圆半径的设定。如果我只需要一个像素,是不是就设为0呢?

        实际上,我尝试设为0之后,发现每个像素的亮度和表现的效果都不太好,给人一种不饱满的感觉。

    第二:我尝试用画一个像素的线来替代这种方法:

                LineGeometry line = new LineGeometry();
                line.StartPoint = position;
                line.EndPoint = position;

       在试验的过程中,我发现 像上面那么写是不会有任何图形的,于是我就把          

      line.EndPoint =new Point( position.X+1,position.Y);

      这样的话,图形就出现了,但我不确定这是不是一个点像素?如果不是的话,改怎么设定结束点的位置呢?

    第三:我感觉使用第二种方法比第一种方法,程序执行起来要快一些,不知道是不是我的错觉?

    第四:就像第二个问题提到的那样,会经常性的看到很多点 有些模糊,“不饱和”的感觉,而且亮度也低了很多。是不是点的位置中横坐标或纵坐标 是不是整数的原因呢?

    第五:关于画大量的点,程序速度的问题。我看到之前版主回复的帖子上,有一个这方面的例子,但是水平有限,看不大懂。不知道有没有什么机制,可以加快画大亮点运算的速度呢?

    请教版主和诸位高手啊,多谢多谢!

     


    Best Regards! C.Lu
    2011年3月10日 3:43

答案

全部回复

  • 你好 C.Lu,

    是的,根据我的经验,第二种会好点。

    你画的是一个像素的点。

    不饱和的感觉? 你改变一下UseLayoutRounding属性,参考这个链接:

    http://msdn.microsoft.com/zh-cn/library/system.windows.frameworkelement.uselayoutrounding.aspx

    我上传的那个例子我没看,不过我建议你看看WPF performance相关的文章,MSDN上面就有,我感觉你看这方面的文章比花费时间研究那个例子的机制会好些,收获的也更多。

    http://msdn.microsoft.com/zh-cn/library/aa970683.aspx

    http://msdn.microsoft.com/zh-cn/library/aa969767.aspx

    (有些地方中文翻译的不是很nice,你也可以看英文的。)

     

    Best regards,


    Sheldon _Xiao[MSFT]

    如果您对我们的论坛在线支持服务有任何的意见或建议,请通过邮件告诉我们。
    MSDN 论坛好帮手 立刻免费下载  MSDN 论坛好帮手

    2011年3月11日 5:30
    版主
  • 感谢您的答复。我再研究研究。


    Best Regards! C.Lu
    2011年3月11日 6:45
  • 绘制椭圆确实要慢很多,椭圆多复杂啊.想想就慢.

    LineGeometry应该会快一些,要更快些换用StreamGeometry

    想再快些,则不需要画1个像素的线,改为画连接每个点的线,只不过用DashStyle调整一下(1,1000),这样每画1个像素,就画一个巨大的空白,结果就是每个线段只画了最开始的1个点(注:没试过,理论上这应该比画1个像素的一把线要快)

    使用的Pen有个属性叫DashCap,默认是flat,改为Round效果会好一点,thickness调粗,嘿嘿,猜你喜欢粗一点的,是吧

    What?还不够快?你狠,WPF目前就这样了,再快得用direct3d了,可以通过互操作,把WPF绘图区交给direct3d设备来绘,用显卡硬件来画,现在500块的显卡每秒画几亿个点不成问题

     

     

    2011年3月11日 6:53
  • AceBear:

    您好!

    StreamGeometry咋画点?师范一下噻,谢谢啊。

    还有,我是在canvas上画点,用不到pen吧。

    又:

    “使用的Pen有个属性叫DashCap,默认是flat,改为Round效果会好一点,thickness调粗,嘿嘿,猜你喜欢粗一点的,是吧”

    我喜欢圆的。

     


    Best Regards! C.Lu
    2011年3月11日 7:27
  • 好吧,我知道怎么用这玩意画线了,是像下面这样吗?     

     

                StreamGeometry dot = new StreamGeometry();


                StreamGeometryContext context = dot.Open();

                context.BeginFigure(center, true, true);

                context.LineTo(center, true, true);


                context.Close();

     

    不过,速度真的会比画线的方法快吗?

    又:

    我设置了canvas的UseLayoutRounding ,还是会出现模糊,“不饱和”的现象.

    请问,您有什么高见?


    Best Regards! C.Lu
    2011年3月11日 7:34
  • <Window x:Class="T_Mar11.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Width="640" Height="480">
        <Grid>
            <Image x:Name="image" Width="800" Height="600"/>
        </Grid>
    </Window>
    private DrawingImage Build()
    {
        Random rand = new Random();
     
        var pts = new Point[10000];
        for (int i = 0; i < pts.Length; i++)
        {
            pts[i].X = rand.Next(0, 800);
            pts[i].Y = rand.Next(0, 600);
        }
     
        double thickness = 8.0;
        var ds = new double[(pts.Length - 1) * 2];
        for (int i = 0; i < pts.Length - 1; i++)
        {
            ds[2 * i] = 0;
            ds[2 * i + 1] = Point.Subtract(pts[i], pts[i + 1]).Length / thickness;
        }
     
        var streamDrawing = new StreamGeometry();
        using (var ctx = streamDrawing.Open())
        {
            ctx.BeginFigure(pts[0], truefalse);
     
            for (int i = 1; i < pts.Length; i++)
                ctx.LineTo(pts[i], truetrue);
        }
     
        var pen = new Pen(Brushes.DarkBlue, thickness);
        pen.DashCap = PenLineCap.Round;
        pen.StartLineCap = PenLineCap.Round;
        pen.EndLineCap = PenLineCap.Round;
        pen.DashStyle = new DashStyle(ds, 0);
        pen.MiterLimit = 1.0;
        pen.LineJoin = PenLineJoin.Round;
     
        var drawing = new GeometryDrawing(null, pen, streamDrawing);
     
        var source = new DrawingImage(drawing);
        source.Freeze();
     
        return source;
    }
    // 最后设置一下Source属性
    image.Source = Build();
    2011年3月11日 9:28
  • 哎呀,AceBear兄,这种方法确实快了不少。

    至少解决了我一半的问题,就是 在拖拉窗口的时候,还是会出现“模糊”的点,虽然我也设置了PenLineCap.Round。

    有没有其他的解决办法啊?


    Best Regards! C.Lu
    2011年3月14日 1:35
  • 这个称为 "光栅化" 问题

    当在点阵式显示设备上呈现图形时,设备上的某些点并不如理论图形有那么高的精度,它必须和周围的点进行插值运算,这个过程叫"光栅化"

    这里有个原因的简单解示http://www.ownself.org/oswpblog/?p=45

    简单点,对于你的圆就是,理论上圆的边界通过了显示屏上的某个点的中央,即,这个点的一部分在圆内,一部分在圆外,而这个点是显示器所能呈现的最小范围,它不可能一半画你的圆,一半画背景色;"光栅化"过程将进行处理,通过插值,这个点既不是你圆的前景色,也不是背景色,而是这两种结果的混和插值结果.

    UseLayoutRounding事实上就是对WPF的光栅化进行一些控制

    特别的,如果你的点只有一个像素,光栅化程序理解你的圆点理论上应覆盖了那个显示器上的小点(矩形点)的内切圆,内切圆之外应该是背景色,于是按占据面积的百分比,这点的实际呈现色会混合大部分的圆的前景色,小部分的背景色,最后看起来,就是点变模糊了

    估计你用"方形点"不用Round点,同时再设置UseLayoutRounding,不会出现模糊的点(没试过,理论上是这样),但圆形的外观就没了,熊与鱼掌不能兼得啊

    对这部分的控制很麻烦,交给WPF自己处理算了.

        可以考虑让点变大一点,确保点的正中能完全占据一整个像素,这样这个点的正中会很清晰,周围的点还是会模糊,但会保持较好的圆形感觉;事实上,这很并不所想象的那么简单,程序并不只在你的机器上运行,对方机器的物理特性会影响这个过程,比如,同样的程序,对方1024*768还是1280*768的分辨率,是32万色,还是16万色都会有不同的最终效果.


     

    2011年3月14日 3:07
  • 你好啊!

    确实,怎么弄都没有改善,总是会在某一尺寸下出现“光栅”点。

    改点的形状或pen的属性,或空间的UseLayoutRounding,或者几个方法一起用都不行。

    谢谢您的答复。


    Best Regards! C.Lu
    2011年3月14日 8:21
  • <Window x:Class="T_Mar11.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Width="640" Height="480">
        <Grid>
            <Image x:Name="image" Width="800" Height="600"/>
        </Grid>
    </Window>
    private DrawingImage Build()
    {
        Random rand = new Random();
     
        var pts = new Point[10000];
        for (int i = 0; i < pts.Length; i++)
        {
            pts[i].X = rand.Next(0, 800);
            pts[i].Y = rand.Next(0, 600);
        }
     
        double thickness = 8.0;
        var ds = new double[(pts.Length - 1) * 2];
        for (int i = 0; i < pts.Length - 1; i++)
        {
            ds[2 * i] = 0;
            ds[2 * i + 1] = Point.Subtract(pts[i], pts[i + 1]).Length / thickness;
        }
     
        var streamDrawing = new StreamGeometry();
        using (var ctx = streamDrawing.Open())
        {
            ctx.BeginFigure(pts[0], truefalse);
     
            for (int i = 1; i < pts.Length; i++)
                ctx.LineTo(pts[i], truetrue);
        }
     
        var pen = new Pen(Brushes.DarkBlue, thickness);
        pen.DashCap = PenLineCap.Round;
        pen.StartLineCap = PenLineCap.Round;
        pen.EndLineCap = PenLineCap.Round;
        pen.DashStyle = new DashStyle(ds, 0);
        pen.MiterLimit = 1.0;
        pen.LineJoin = PenLineJoin.Round;
     
        var drawing = new GeometryDrawing(null, pen, streamDrawing);
     
        var source = new DrawingImage(drawing);
        source.Freeze();
     
        return source;
    }
    // 最后设置一下Source属性
    image.Source = Build();

    我的问题要复杂一点,每次都只画一条短短的线段,但是要画几万次。

    上面的方法只能一次就把所有想画的都画出来,我需要每次来一个消息,就画一条线。

    2011年6月21日 6:57