【问题标题】:How to draw a subpixel line如何绘制亚像素线
【发布时间】:2012-03-02 23:01:22
【问题描述】:

在以下代码中,我尝试绘制两条线:一条具有子像素宽度 (0.5),另一条具有 1px 宽度:

        var img = new Bitmap(256, 256);
        Graphics graphics = Graphics.FromImage(img);
        graphics.SmoothingMode = SmoothingMode.AntiAlias;

        // Draw a subpixel line (0.5 width)
        graphics.DrawLine(new Pen(Color.Red, (float)0.5), 0, 100, 255, 110);

        // Draw a single pixel line (1 width)
        graphics.DrawLine(new Pen(Color.Red, (float)1), 0, 110, 255, 120);

        img.Save(@"c:\temp\test.png", ImageFormat.Png);

        graphics.Dispose();

        img.Dispose();

但是,在生成的图像中,两条线的宽度相同:

有没有办法让顶线出现亚像素(0.5px)?

编辑:经过一些研究,AGG 可能是要走的路,其中有一个c# port

【问题讨论】:

  • 请不要在标题前加上“C#”之类的前缀。这就是标签的用途。
  • 假设您的目标是有意义的。推断一下,您希望笔宽为 0.001 像素的线条看起来如何?
  • 如果您使用图形应用程序缩小具有 1 像素线的图像,生成的图像将显示暗线。

标签: c# .net graphics system.drawing


【解决方案1】:

您可以通过将所有内容绘制成 x2 然后将其缩小来破解它:

        Image img2x = new Bitmap(256*2, 256*2);
        Graphics g2x = Graphics.FromImage(img2x);
        g2x.SmoothingMode = SmoothingMode.AntiAlias;
        g2x.DrawLine(new Pen(Color.Red, 0.5f*2), 0, 100*2, 255*2, 110*2);

        Image img = new Bitmap(256, 256);
        Graphics g = Graphics.FromImage(img);
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.DrawImage(img2x, 0, 0, 256, 256);

        g.DrawLine(new Pen(Color.Red, 1f), 0, 110, 255, 120);

        img.Save(@"c:\tmep\test.png", ImageFormat.Png);

【讨论】:

  • 有趣的建议。您已经证明可以在 png 中显示亚像素线。看起来这只是 GDI+ 的一个限制,如果不按比例缩小就无法做到这一点。
  • 这不是真正的亚像素——通常是指考虑底层红蓝绿亚像素布局的渲染技术。这只是一条小数宽度线,应该可以在任何地方使用,但 GDI+ 显然不处理宽度 freespace.virgin.net/hugo.elias/graphics/x_wuline.htm
【解决方案2】:

子像素线可以绘制成细长的矩形。
这样做的方法如下:

    public static class GraphicsExtensions
    {
        public static void DrawLineAnyWidth(this Graphics gr, Pen pen, PointF pt1, PointF pt2)
        {
            if (pen.Width > 1.5f)
            {
                gr.DrawLine(pen, pt1, pt2);
            }
            else
            {
                var poly = new PointF[5];
                var h = pen.Width * 0.5f;
                var br = new SolidBrush(pen.Color);
                poly[0] = SidePoint(pt1, pt2, -h);
                poly[1] = SidePoint(pt1, pt2, h);
                poly[2] = SidePoint(pt2, pt1, -h);
                poly[3] = SidePoint(pt2, pt1, h);
                poly[4] = poly[0];
                gr.FillPolygon(br, poly);
                br.Dispose();
            }
        }
        static PointF SidePoint(PointF pa, PointF pb, float h)
        {
            float Dx = pb.X - pa.X;
            float Dy = pb.Y - pa.Y;
            float D = (float)Math.Sqrt(Dx * Dx + Dy * Dy);
            return new PointF(pa.X + Dy * h / D, pa.Y - Dx * h / D);
        }
    }

例子:

    var bmp = new Bitmap(200, 350);
    using (var gr = Graphics.FromImage(bmp))
    {
        gr.Clear(Color.White);
        gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        var pen = new Pen(Color.IndianRed);
        for (int i = 1; i < 30; i++)
        {
            var pa = new PointF(50, 20 + i * 10);
            var pb = new PointF(150, 30 + i * 10);
            pen.Width = i * 0.1f;
            gr.DrawLineAnyWidth(pen, pa, pb);
        }
    }

示例结果:

【讨论】:

    【解决方案3】:

    根据documentation for Pen

    Width 属性设置为 width 参数中指定的值。宽度为 0 将导致 Pen 绘制就好像宽度为 1。

    这可能适用于任何小于 1 的宽度,而不仅仅是精确等于 0 的宽度。

    【讨论】:

      【解决方案4】:

      我认为在这种情况下使用亚像素是没有意义的。无论如何,您正在将其导出到图像文件。请尝试更大的宽度值。

      【讨论】:

      • 你的意思是导出到图片文件时不能画亚像素线?
      • 图像文件基本上是一个像素矩阵。你觉得它怎么可能代表其中的亚像素信息?
      • 我明白了。奇怪的是它的行为与小宽度不同,它甚至将 width=0 视为 1。感谢您向我指出这一点!
      【解决方案5】:

      文章Scaling the Pen object 提到了GDI+ Pen 中的一个错误,当它的宽度小于1.5 时,它无法正常缩小。

      此外,如果您尝试分别绘制宽度为 2.0F2.5F3.0F 的三条线,您会看到视觉上的差异,因此您的案例看起来确实是 GDI+ 的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-30
        相关资源
        最近更新 更多