Как нарисовать круги быстрее на форме окна? - PullRequest
2 голосов
/ 11 мая 2019

Мне нужно нарисовать маленькие круги в заданных координатах X, Y, но они могут достигать 6000 кругов на панели в окне. Как это очень медленно и занимает около 2-3 секунд на 5000 кругов. Как я мог нарисовать это быстрее?

 private void drawBGA_Pins(BGAmap PinCordinates, double ExternalZoomFactor, double ExternalOffset_X, double ExternalOffset_Y)
        {
            Graphics g = this.imgBox.CreateGraphics();
            double zoomFactor = (Math.Min(Math.Abs((imgBox.Width) / PinCordinates.width), Math.Abs((imgBox.Height) / PinCordinates.height)))*92/100 * ExternalZoomFactor;
            //g.Clear(Color.Transparent); //you can choose another color for your background here.
            Pen pen = new Pen(Color.Yellow);

            foreach (var p in PinCordinates.pkgCordinates)
            {
                try
                {
                    g.DrawEllipse(pen, (float)(ExternalOffset_X + (p.X* zoomFactor)), (float)(ExternalOffset_Y + (p.Y* zoomFactor)), 10, 10);
                }
                catch
                {

                }
            }
        }

Ответы [ 2 ]

1 голос
/ 11 мая 2019

Как отмечено в моем комментарии: код, который вы публикуете, имеет много проблем, и одна из них также является причиной отсутствия скорости ..

Графика Winforms правило № 1: Никогда не используйте control.CreateGraphics!

Также никогда не пытайтесь кэшировать объект Graphics! Либо рисуйте в Bitmap bmp, используя Graphics g = Graphics.FromImage(bmp), либо в Paint событии элемента управления, используя параметр e.Graphics ..

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

Правильный способ состоит в том, чтобы сохранить список вещей для рисования и всякий раз, когда этот список изменяется Invalidate элемент управления, на котором вы рисуете. Весь рисунок должен быть в событии Paint, используя e.Graphics там!

Это обсуждалось довольно часто здесь; но что еще интереснее, так это то, что правильный путь будет действительно быстрым по сравнению с неправильным, непостоянным.

Посмотрим:

enter image description here

Пропущенные моменты времени для кругов 50k и 100k с созданным графическим объектом составляют 5,4 с (против 0,18 с) и 10,9 с (против 0,41 с). (Файл анимации будет слишком длинным для них ..)

Так что e.Graphics может «нарисовать» круги ~ в 30-100 раз быстрее.

Как получилось? - На самом деле «правильный» путь будет только подготовить поверхность с двойной буферизацией управления внутри , но только толкнет его на дисплей один раз когда он закончится и успеет это сделать, тогда как неправильный способ доставит каждый круг напрямую . Эта оптимизация выполняется системой, а также ограничивает область вывода.

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

Вот испытательный стенд:

int count = 5000;
List<PointF> pinCoordinates = new List<PointF>();
Random rnd = new Random(9);
float zoomFactor = 1.5f;

private void Button1_Click(object sender, EventArgs e)
{
    // init a list of points
    pinCoordinates.Clear();
    Size sz = pictureBox1.ClientSize;
    Cursor = Cursors.WaitCursor;
    for (int i = 0; i < count; i++)
    {
        pinCoordinates.Add(new PointF(rnd.Next(sz.Width), rnd.Next(sz.Height)));
    }

    // now draw in one way or the other:
    if (radioButton1.Checked)
    {
        Graphics g = pictureBox1.CreateGraphics();
        DateTime dt0 = DateTime.Now;
        foreach (var p in pinCordinates)  DoDraw(g, p);
        sayTime(dt0);
    }
    else
    {
        pictureBox1.Invalidate();
    }
    Cursor = Cursors.Default;
}

private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    DateTime dt0 = DateTime.Now;
    foreach (var p in pinCoordinates)  DoDraw(e.Graphics, p);
    sayTime(dt0);
}

void DoDraw(Graphics g, PointF p)
{
    using (Pen pen = new Pen(Color.FromArgb(rnd.Next(1234567890))))
        g.DrawEllipse(pen, p.X * zoomFactor, p.Y * zoomFactor, 10, 10);
}

void sayTime(DateTime dt)
{
    DateTime dt1 = DateTime.Now;
    label1.Text = (dt1 - dt).ToString("s\\.ffff");
}

Последнее замечание: Вы можете ожидать одинаковую скорость, если вы рисуете в растровое изображение, используя Graphics g = Graphics.FromImage(bmp) ..

0 голосов
/ 11 мая 2019

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

  1. Вызов Control.SuspendLayout и Control.ResumeLayout после частых обновлений пользовательского интерфейса.

  2. проверьте, часто ли вы вызываете Control.Refresh, используйте Control.Invalidate вместо Control.Refresh

  3. закрасьте ТОЛЬКО круги, которые находятся в видимой области(Graphics.Clip), тогда кругов будет гораздо меньше.

  4. приостановить рисование на уровне Windows API.см. этот пост: Как приостановить рисование для элемента управления и его дочерних элементов?

...