12

In the following code, I'm trying to draw two lines: One with a subpixel width (0.5) and the other with 1px width:

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

However, in the generated image, both lines appear the same width:

enter image description here

Is there a way for the top line to appear sub-pixel (0.5px)?

Edit: After some research, AGG might be the way to go, of which there is a c# port.

FredL
  • 1,035
  • 9
  • 23

5 Answers5

8

You could hack it by drawing everything x2 and then scale it down:

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

enter image description here

gordy
  • 9,360
  • 1
  • 31
  • 43
  • Interesting suggestion. You've demonstrated that it's possible to display subpixel lines in a png. It looks like it's just a limitation of GDI+ that this can't be done without scaling down. – FredL Mar 03 '12 at 00:45
  • It's not really subpixel - that usually refers to rendering technique that considers the underlying red blue and green subpixel layout. This is just a fractional width line that should be possible anywhere but GDI+ apparently doesn't deal with width < 1. See http://freespace.virgin.net/hugo.elias/graphics/x_wuline.htm – gordy Mar 03 '12 at 01:08
4

Subpixel lines can be drawn as thin rectangles.
Here's how to do this:

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

Example:

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

Result of example:
enter image description here

encePence
  • 61
  • 2
2

According to the documentation for Pen ,

The Width property is set to the value specified in the width parameter. A width of 0 will result in the Pen drawing as if the width were 1.

It may be that that applies to any width less than one, not just widths that are precisely equal to 0.

Dathan
  • 7,266
  • 3
  • 27
  • 46
0

I do not think it makes sense to use subpixel here in this case. You are exporting it to an image file anyway. Try greater width value instead.

myin528
  • 522
  • 2
  • 7
  • Do you mean that it's not possible to draw a sub-pixel line when exporting to an image file? – FredL Mar 02 '12 at 23:17
  • An image file is basically a matrix of pixels. How do you think it can possibly represent sub-pixel information in it? – myin528 Mar 02 '12 at 23:34
  • I see. This is odd that it behaviors differently with small width, it even treats width=0 as 1. Thanks for pointing this out to me! – myin528 Mar 03 '12 at 06:07
0

The article Scaling the Pen object mentions a bug in GDI+ Pen that makes it unable to scale down properly when its width is less than 1.5.

Also, if you try to draw three lines with widths 2.0F, 2.5F and 3.0F respectively, you'll see a visual difference, so your case really looks like some issue with GDI+.

Three Pens with 0.5 width increment

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Yuriy Guts
  • 2,180
  • 1
  • 14
  • 18