Как нарисовать субпиксельную линию

В следующем коде я пытаюсь нарисовать две линии: одну с шириной субпикселя (0,5), а другую с шириной 1 пикселя:

        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,5 пикселя)?

Изменить: После некоторых исследований, AGG может быть путь, из которого есть порт C#.

5 ответов

Решение

Согласно документации для Pen,

Свойству Width присваивается значение, указанное в параметре width. Ширина 0 приведет к рисованию пером, как если бы ширина была 1.

Может случиться так, что это относится к любой ширине меньше единицы, а не только к ширине, которая точно равна 0.

Вы можете взломать его, нарисовав все 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);

Линии субпикселей можно рисовать как тонкие прямоугольники.
Вот как это сделать:

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

Результат примера:

Я не думаю, что имеет смысл использовать субпиксель здесь в этом случае. Вы все равно экспортируете его в файл изображения. Вместо этого попробуйте большее значение ширины.

В этой статье упоминается ошибка в GDI+ Pen, из-за которой он не может правильно масштабироваться, когда его ширина меньше 1.5,

Также, если вы попытаетесь нарисовать три линии шириной 2.0F, 2.5F а также 3.0F соответственно, вы увидите визуальную разницу, поэтому ваш случай действительно выглядит как проблема с GDI+.

Три ручки с шагом 0,5 ширины

Другие вопросы по тегам