Быстрый способ рисовать линии, используя разные цвета, используя GDI +? - PullRequest
4 голосов
/ 14 января 2010

У меня есть динамический список точек, новые точки могут быть добавлены в любое время. Я хочу нарисовать линии, чтобы соединить их, используя другой цвет. Цвет основан на индексе этих точек. Вот код:

    private List<Point> _points;
    private static Pen pen1 = new Pen(Color.Red, 10);
    private static Pen pen2 = new Pen(Color.Yellow, 10);
    private static Pen pen3 = new Pen(Color.Blue, 10);
    private static Pen pen4 = new Pen(Color.Green, 10);

    private void Init()
    {
        // use fixed 80 for simpicity
        _points = new List<Point>(80);

        for (int i = 0; i < 80; i++)
        {
            _points.Add(new Point(30 + i * 10, 30));
        }
    }

    private void DrawLinesNormal(PaintEventArgs e)
    {
        for (int i = 0; i < _points.Count-1; i++)
        {
            if (i < 20)
                e.Graphics.DrawLine(pen1, _points[i], _points[i + 1]);
            else if (i < 40)
                e.Graphics.DrawLine(pen2, _points[i], _points[i + 1]);
            else if (i < 60)
                e.Graphics.DrawLine(pen3, _points[i], _points[i + 1]);
            else
                e.Graphics.DrawLine(pen4, _points[i], _points[i + 1]);
        }
    }

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

[ОБНОВЛЕНИЕ] Я собираю некоторые возможные оптимизации:

  1. Использование GrahpicsPath, Оригинальный вопрос
  2. Измените качество графики (например, SmoothingMode / PixelOffsetMode ...), также вызовите SetClip, чтобы указать единственный необходимый регион для визуализации.

Ответы [ 7 ]

5 голосов
/ 14 января 2010

Вы не сможете выжать гораздо большую скорость из этого кода без потери качества или перехода на более быстрый рендерер (GDI, OpenGL, DirectX). Но GDI часто будет немного быстрее (возможно, в 2 раза), а DirectX / OpenGL может быть намного быстрее (возможно, в 10 раз), в зависимости от того, что вы рисуете.

Идея использования Path заключается в том, что вы объединяете много (в вашем примере 20) строк в один вызов метода, а не вызываете DrawLine 20 раз. Это принесет вам пользу только в том случае, если вы сможете упорядочить входящие данные в правильном формате списка точек для процедуры рисования. В противном случае вам придется скопировать точки в правильную структуру данных, и это приведет к потере значительного количества времени, которое вы получаете, группируя путь. В случае DrawPath вам может потребоваться создать GraphicsPath из массива точек, что может привести к экономии времени. Но если вам приходится рисовать один и тот же путь более одного раза, вы можете его кешировать, и тогда вы увидите чистую выгоду.

Если новые точки добавляются в список, но старые не удаляются (т. Е. Вы всегда просто добавляете новые строки на дисплей), то вы сможете использовать закадровое растровое изображение для хранения отрисованных линий. Таким образом, каждый раз, когда добавляется точка, вы рисуете одну линию, а не рисуете все 80 линий каждый раз.

Все зависит от того, что именно вы пытаетесь сделать.

2 голосов
/ 14 января 2010

На самом деле не помогает улучшить производительность, но я бы также поместил ручки в список и написал все эти строки следующим образом:

int ratio = _points.Count / _pens.Count;

for (int i = 0; i < _points.Count - 1; i++)
{
    e.Graphics.DrawLine(_pens[i / ratio], _points[i], _points[i + 1]);
}
1 голос
/ 13 сентября 2010

Слишком поздно, но, возможно, кому-то все еще нужно решение.

Я создал небольшую библиотеку GLGDI + с похожим (но не полным / равным) синтаксисом GDI +, который работает на OpenTK: http://code.google.com/p/glgdiplus/

Я не уверен насчет стабильности, у него есть некоторые проблемы с DrawString (проблема с TextPrint из OpenTK). Но если вам нужно повышение производительности для вашей утилиты (например, редактор уровней в моем случае), это может быть решением.

1 голос
/ 14 января 2010

Это примерно так же быстро, как вы собираетесь с System.Drawing. Возможно, вы получите небольшой выигрыш при использовании Graphics.DrawLines(), но вам нужно будет отформатировать данные по-разному, чтобы получить преимущество одновременного рисования нескольких линий одной и той же ручкой. Я серьезно сомневаюсь, что GraphicsPath будет быстрее.

Один верный способ улучшить скорость - снизить качество продукции. Установите Graphics.InterpolationMode на InterpolationMode.Low, Graphics.CompositingQuality на CompositingQuality.HighSpeed, Graphics.SmoothingMode на SmoothingMode.HighSpeed, Graphics.PixelOffsetMode на PixelOffsetMode.HighSpeed и Graphics.CompositingMode на CompositingMode.SourceCopy.

Я помню один тест скорости, когда кто-то сравнивал Graphics с P / Invoke с подпрограммами GDI и был весьма удивлен гораздо более высокими скоростями P / Invoke. Вы можете проверить это. Я посмотрю, смогу ли я найти это сравнение ... По-видимому, это было для Compact Framework, поэтому, скорее всего, оно не подходит для ПК.

Другой способ - использовать Direct2D, который может быть быстрее, чем GDI, если у вас есть подходящее оборудование.

0 голосов
/ 15 января 2010

Я думаю, вам нужно избавиться от объекта пера и объекта e.Graphics после рисования. Еще одна вещь, лучше написать код drawLine внутри onPaint ().

 just override onPaint() method it support better drawing and fast too.
0 голосов
/ 14 января 2010

Совсем не GDI (+), но совершенно другой способ решения этой проблемы: работа с блоком памяти, рисование линий в нем, преобразование его в объект Bitmap для мгновенного рисования там, где вам нужнопокажите свои линии.

Конечно, это зависит исключительно от быстрых способов

  • рисовать линии данного цвета в выбранном представлении памяти и
  • конвертировать этона Bitmap, чтобы показать.

Не в .NET Framework, я думаю, но, возможно, в сторонней библиотеке?Нет ли в Silverlight средства записи растровых изображений для подобных вещей?(Не слишком много в Silverlight ...)

По крайней мере, это может быть нестандартный подход к этому.Надеюсь, это поможет.

0 голосов
/ 14 января 2010

Возможно, вы захотите взглянуть на объект Brush, и это правда, что вы не достигнете почти производительности в реальном времени из программы GDI +, но вы можете легко поддерживать приличный fps, пока геометрия и количество объектов остаются. в разумных пределах. Что касается рисования линий, я не понимаю, почему нет.

Но если вы достигнете точки, когда вы делаете то, что считаете оптимальным, и все, что рисовать линии ... вам следует рассмотреть другой графический стек, и если вам нравится .NET, но есть проблемы с неуправляемыми API, такими как OpenGL и DirectX, используйте WPF или Silverlight, он довольно мощный.

В любом случае, вы можете попытаться настроить System.Drawing.Drawing2D.GraphicsPath и затем использовать System.Drawing.Drawing2D.PathGradientBrush для применения цветов таким образом. Это единственный буферизованный вызов отрисовки, и если вы не можете получить от этого достаточную производительность. Вам придется пойти с чем-то другим, кроме GDI +

...