2009-12-17 5 views
2

Я программирую в Visual Studio .Net и используя C#.Эффективное использование OnPaint

Я создаю свой собственный элемент управления, который рисует волну на основе значений, которые я получаю от аналого-цифрового преобразователя (АЦП). Я беру входящие точки и преобразовываю их в точки X и Y, чтобы правильно рисовать граф под моим контролем.

У меня есть цикл внутри моего метода OnPaint, который проходит через все точки и вызывает метод DrawLine между текущей точкой и следующей точкой.

Однако это очень неэффективно, так как некоторые из этих графиков имеют 8192 точки, и система фактически имеет девять АЦП, которые я хотел бы показать одновременно. Каждый раз, когда страница перерисовывается, для перерисовки всех графиков (особенно во время отладки) требуется почти секунда.

Кроме того, у меня есть функциональность, которая позволяет вам увеличивать и панорамировать волны, чтобы получить лучший обзор (действует так же, как на картах Google), и все 9 волн увеличивают масштаб и панорамируют вместе.

Все эти функции очень «отрывистые», потому что я вызываю invalidate на mousewheel и mousemove. В принципе, все работает, но не так гладко, как хотелось бы.

Мне было интересно, есть ли способ создать предписанный объект из данных, а затем просто нарисуйте расширенную и переведенную версию изображения в области рисования.

Любая помощь будет принята с благодарностью, даже если она просто указала мне в правильном направлении.

+0

Обращаясь к комментариям ниже, так как я не был ясно в своем посте. Данные НЕ транслируются. Я запрашиваю набор данных, а затем работаю с этим набором, поэтому статическое изображение является возможным решением. Просто хотел уточнить. Теперь я изучаю другие предложения. – EatATaco

ответ

6

Создайте объект Bitmap и примените к нему.

В обработчике Paint просто поместите Bitmap на экран.

Это позволит вам отменить изменение масштаба, от повторного рендеринга данных.

+0

Хорошо для панорамирования, но как это работает с масштабированием (и текстовой маркировкой в ​​одно и то же время). –

+0

При масштабировании вы можете повторно отобразить на новом уровне масштабирования в фоновом режиме. Способ, которым Google Maps показывает вам нечеткую, взорванную версию вашего старого представления, пока он не будет воспроизводиться на изображениях с новым уровнем масштабирования. –

3

Вы можете установить DoubleBuffered в true на свой контроль/форму. Или вы можете попробовать использовать свое собственное изображение, чтобы создать эффект двойной буферизации.

Мой DoubleBufferedGraphics класс:

public class DoubleBufferedGraphics : IDisposable 
{ 
    #region Constructor 
    public DoubleBufferedGraphics() : this(0, 0) { } 

    public DoubleBufferedGraphics(int width, int height) 
    { 
     Height = height; 
     Width = width; 
    } 
    #endregion 

    #region Private Fields 
    private Image _MemoryBitmap; 
    #endregion 

    #region Public Properties 
    public Graphics Graphics { get; private set; } 

    public int Height { get; private set; } 

    public bool Initialized 
    { 
     get { return (_MemoryBitmap != null); } 
    } 

    public int Width { get; private set; } 
    #endregion 

    #region Public Methods 
    public void Dispose() 
    { 
     if (_MemoryBitmap != null) 
     { 
      _MemoryBitmap.Dispose(); 
      _MemoryBitmap = null; 
     } 

     if (Graphics != null) 
     { 
      Graphics.Dispose(); 
      Graphics = null; 
     } 
    } 

    public void Initialize(int width, int height) 
    { 
     if (height > 0 && width > 0) 
     { 
      if ((height != Height) || (width != Width)) 
      { 
       Height = height; 
       Width = width; 

       Reset(); 
      } 
     } 
    } 

    public void Render(Graphics graphics) 
    { 
     if (_MemoryBitmap != null) 
     { 
      graphics.DrawImage(_MemoryBitmap, _MemoryBitmap.GetRectangle(), 0, 0, Width, Height, GraphicsUnit.Pixel); 
     } 
    } 

    public void Reset() 
    { 
     if (_MemoryBitmap != null) 
     { 
      _MemoryBitmap.Dispose(); 
      _MemoryBitmap = null; 
     } 

     if (Graphics != null) 
     { 
      Graphics.Dispose(); 
      Graphics = null; 
     } 

     _MemoryBitmap = new Bitmap(Width, Height); 
     Graphics = Graphics.FromImage(_MemoryBitmap); 
    } 

    /// <summary> 
    /// This method is the preferred method of drawing a background image. 
    /// It is *MUCH* faster than any of the Graphics.DrawImage() methods. 
    /// Warning: The memory image and the <see cref="Graphics"/> object 
    /// will be reset after calling this method. This should be your first 
    /// drawing operation. 
    /// </summary> 
    /// <param name="image">The image to draw.</param> 
    public void SetBackgroundImage(Image image) 
    { 
     if (_MemoryBitmap != null) 
     { 
      _MemoryBitmap.Dispose(); 
      _MemoryBitmap = null; 
     } 

     if (Graphics != null) 
     { 
      Graphics.Dispose(); 
      Graphics = null; 
     } 

     _MemoryBitmap = image.Clone() as Image; 

     if (_MemoryBitmap != null) 
     { 
      Graphics = Graphics.FromImage(_MemoryBitmap); 
     } 
    } 
    #endregion 
} 

С его помощью в OnPaint:

protected override void OnPaint(PaintEventArgs e) 
{ 
    if (!_DoubleBufferedGraphics.Initialized) 
    { 
     _DoubleBufferedGraphics.Initialize(Width, Height); 
    } 

    _DoubleBufferedGraphics.Graphics.DrawLine(...); 

    _DoubleBufferedGraphics.Render(e.Graphics); 
} 

ControlStyles Я обычно набор, если я его с помощью (вы можете иметь различные потребности):

SetStyle(ControlStyles.UserPaint, true); 
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true); 

Редактировать:

ОК, поскольку данные являются статичными, вы должны рисовать изображение (перед вашим OnPaint), а затем в OnPaint используйте перегрузку Graphics.DrawImage(), чтобы нарисовать правильную область исходного изображения на экране. Нет причин перерисовывать строки, если данные не меняются.

+0

Двойная буферизация не делает вещи перерисовкой быстрее - она ​​просто удаляет мерцание. Здесь необходимо сделать так, чтобы данные были развернуты, масштабированы и т. Д., Без повторного рендеринга. –

+0

Согласно OP данные изменяются по мере поступления новых данных из АЦП, поэтому статическое изображение также не является решением. Двойная буферизация решает проблему, которую он описывает, и может привести к более быстрому рисованию. –

+0

Кори, данные не транслируются. Извините, если я не был в курсе об этом. – EatATaco

0

Вы можете нарисовать многострочный. Я не уверен, что это похоже на C#, но он должен быть там (его API на базе GDI/GDI +). Это позволяет вам указывать все точки за один раз и позволяет Windows оптимизировать вызов немного (меньше стека push/pop, чтобы оставаться внутри алгоритма рисования, а не возвращаться к вашему коду для каждой новой точки).

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

Вот ссылка: http://msdn.microsoft.com/en-us/library/system.windows.shapes.polyline.aspx

1

У меня есть две точки, чтобы добавить:

  1. Вы говорите, что у вас есть 8192 очков. Вероятно, ваша область рисования не превышает 1000. Полагаю, вы могли бы «уменьшить разрешение» своего графика, добавив только каждую десятую или около нее строк.
  2. Вы можете использовать класс GraphicsPath хранить все необходимые линии и сделать их все сразу с Graphics.drawPath

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

0

Просто рассчитать видимый диапазон и нарисовать только эти точки. Используйте двойную буферизацию. И, наконец, вы можете создать свою собственную реализацию многострочного рисования с использованием необработанных растровых данных, например. используйте LockBits и пишете цвета пикселей непосредственно в байтах, образуя изображение. Используйте InvalidateRect (..), чтобы перерисовать часть окна.

Смежные вопросы