2

Visual Studio .Netでプログラミングしており、C#を使用しています。

アナログ-デジタルコンバーター(ADC)から取得した値に基づいて波を描画する独自のコントロールを作成しています。入ってくるポイントを取得し、それらをXポイントとYポイントに変換して、コントロールでグラフを適切に描画します。

OnPaintメソッド内に、すべてのポイントを通過し、現在のポイントと次のポイントの間でDrawLineメソッドを呼び出すループがあります。

ただし、これらのグラフの一部には8192ポイントがあり、システムには実際に同時に表示したい9つのADCがあるため、これは非常に非効率的です。ページが再描画されるたびに、すべてのグラフが再描画されるまでにほぼ1秒かかります(特にデバッグ中)。

その上、私はあなたがより良いビューを得るために波をズームインしてパンすることを可能にする機能を持っています(グーグルマップがするように機能します)そして9つの波すべてが一緒にズームインしてパンします。

私はmousewheelとmousemoveでinvalidateを呼び出しているので、この機能はすべて非常に「ぎくしゃく」しています。基本的にはすべて機能しますが、思ったほどスムーズではありません。

データから事前に描画されたオブジェクトを作成し、描画領域に画像の拡張および翻訳されたバージョンを描画する方法があるかどうか疑問に思いました。

それが私を正しい方向に向けているだけであっても、どんな助けでも大歓迎です。

4

5 に答える 5

7

ビットマップオブジェクトを作成し、それに描画します。

ペイントハンドラーで、ビットマップを画面にブリットします。

これにより、データの再レンダリングから、スケールの変更を切り離すことができます。

于 2009-12-17T20:31:47.120 に答える
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()オーバーロードを使用して、ソース画像の正しい領域を画面に描画する必要があります。データが変更されていない場合、線を再描画する理由はありません。

于 2009-12-17T20:30:35.947 に答える
1

追加する2つのポイントがあります:

  1. あなたは8192ポイントを持っていると言います。描画領域はおそらく1000以下です。10行ごとに追加するだけで、グラフの「解像度を下げる」ことができると思います。
  2. GraphicsPathクラスを使用して、必要なすべての線を格納し、Graphics.DrawPathを使用してそれらを一度に描画できます。

このようにして、パフォーマンスを向上させながら、静的ビットマップの使用を回避できます(ズームを可能にするため)。

于 2009-12-17T21:00:56.343 に答える
0

マルチラインを描くことができます。C# でそれがどのように見えるかはわかりませんが、そこにある必要があります (GDI/GDI+ ベースの API)。これにより、一度にすべてのポイントを指定でき、Windows で呼び出しを少し最適化できます (新しいポイントごとにコードに戻るのではなく、スタック プッシュ/ポップを少なくして描画アルゴリズム内にとどめます)。

編集:ただし、データが静的な場合は、出力のダブル バッファー/キャッシュ イメージを使用する方が、最初の描画について心配するよりも効率的です。

ここにリンクがあります:http://msdn.microsoft.com/en-us/library/system.windows.shapes.polyline.aspx

于 2009-12-17T20:45:41.507 に答える
0

可視範囲を計算して、これらの点のみを描画します。ダブルバッファリングを使用します。そして最後に、生のビットマップ データを使用して独自のマルチライン描画を作成できます。たとえば、LockBits を使用して、画像を形成するバイトに直接ピクセルの色を書き込みます。ウィンドウの一部を再描画するには、InvalidateRect(..) を使用します。

于 2009-12-17T21:58:03.987 に答える