2016-05-21 2 views
-1

В моем приложении мне нужно нарисовать петлю сетку из тысяч прямоугольников, а производительность рисования часто важна для визуальных эффектов. Когда я работал над приложением, как, что в Java, я перекрытая paintComponent метод в следующим образом:Зачем рисовать тысячи прямоугольников в C# PictureBox намного медленнее, чем в Java jPanel?

public void paintComponent(Graphics g)  
{ 
    super.paintComponent(g);    
    Graphics2D graphics2D = (Graphics2D)g; 
    rectanglesGrid.draw(graphics2D); 
} 

rectenglesGrid класс содержит 2D ArrayList прямоугольников по имени cellsGrid. Код draw метод очень похож на следующий код:

public void draw(Graphics2D graphics2D) 
{ 
    for (ArrayList<Rectangle2D.Float> cells : cellsGrid) 
    { 
     for (Rectangle2D.Float cell : cells) 
     { 
      graphics2D.setPaint(someColor); 
      graphics2D.draw(cell); 
      graphics2D.fill(cell); 
     } 
    }  
} 

Чтобы заставить JPanel перекрашивать, я создал свой собственный TimerTask класс и в run вызове метода jPanel.repaint() а. Рисование прямоугольников в Java таким образом выполняется быстро.

Теперь я пытаюсь написать подобное приложение на C#. Чтобы нарисовать прямоугольник, я создал следующий EventHandler:

private void cellsGrid_pb_Paint(object sender, PaintEventArgs e) 
{ 
    rectanglesGrid.Draw(e.Graphics); 
} 

Снова rectanglesGrid класс содержит 2D массив прямоугольников и рисовать метод похож на Java одно:

foreach(Rectangle[] cells in cellsGrid) 
    foreach(Rectangle cell in cells) 
    { 
     pen.Color = brush.Color = someColor; 
     graphics.DrawRectangle(pen, cell); 
     graphics.FillRectangle(brush, cell); 
    } 

Чтобы заставить PictureBox перерисовки я использую Timer и в вызове обработчика события TickpictureBox.Refresh(). Рисование прямоугольников в C# таким образом намного медленнее, чем в Java.

Интересно, почему? Я не думаю, что это просто потому, что «приложения Java работают быстрее, чем C#» ... Мой код на C# действительно похож на Java, я даже использую эквивалентные инструменты для рисования. Как повысить производительность рисования в приложении форм Windows C#?

Я попытался установить DoubleBuffered свойство формы в true, если это имеет значение.


EDIT - лучшее описание моей проблемы

I'w работает над проектом, который визуализирует cellular automaton. Это означает, что на самом деле у меня нет 2D сетки прямоугольников, а 2D сетка ячеек. Я сохраняю эти ячейки в 2D-массиве (или ArrayList в Java). Эта сетка ячеек содержится в классе, например 'CellularAutomaton'. Ячейки представляют собой структуры, состоящие из State (enum), Rectangle, статические поля с структурой и методом Color, который возвращает правильное значение Color в соответствии с состоянием ячейки. Ячейки меняют свое состояние на следующих временных шагах в методе NextStep() в пределах класса CellularAutomaton. CellularAutomaton класс также содержит Draw() способ, который принимает Graphics пример как параметр.Вот точный код этого метода в C#:

public void Draw(Graphics graphics) 
{ 
    foreach(Cell[] cells in cellsGrid) 
     foreach(Cell cell in cells) 
     { 
      pen.Color = brush.Color = cell.Color; 
      Rectangle rectangle = cell.Rectangle; 
      graphics.DrawRectangle(pen, rectangle); 
      graphics.FillRectangle(brush, rectangle); 
     } 
} 

Java:

public void draw(Graphics2D graphics2D) 
{ 
    for (ArrayList<Cell> cells : cellsGrid) 
    { 
     for (Cell cell : cells) 
     { 
      graphics2D.setPaint(cell.getColor()); 
      graphics2D.draw(cell.getRectangle()); 
      graphics2D.fill(cell.getRectangle()); 
     } 
    }  
} 

В C# в обработчике события клеща таймер я зову:

pictureBox.Refresh(); 
cellularAutomaton.NextStep(); 

В Java в методе run от TimerTask, класс I:

jPanel.repaint(); 
cellularAutomaton.nextStep(); 

PictureBox/jPanel Перекраска вызывает перетаскивание сетки ячеек (поскольку я переопределял метод paintComponent в Java и написал обработчик событий для события рисования на C#, как я писал ранее).

Примеры параметров, для которых Java-код работает намного быстрее: 15k 5x5 прямоугольников и 1 миллисекундный интервал между каждой перекраской и CellularAutomaton.

Реализации метода NextStep() очень похожи на C# и Java, поэтому я предполагаю, что замедление в C# вызвано рисованием (конечно, этот рисунок вызывает замедление, я буду измерять его позже).


UPDATE

Я измерил, что NextStep() работает немного быстрее в C#. Однако чертеж 200 раз 10k 5x5 прямоугольников из 2D-массива Cell в CellularAutomaton класс в C# занимает около 1,8 секунды, а в Java занимает около 300 миллисекунд. Так, как я думал, проблема в том, что рисование на C# намного медленнее.

+0

Двойной буфер должен быть установлен как true для элемента управления, на который вы рисуете, но для pictureBox по умолчанию установлено значение true. Вы хотите, чтобы процесс рисования был видимым? Btw: GDI + является __really old__ по сравнению с тем, что работает внутри Java, поэтому я не удивлюсь, если он будет медленнее. – TaW

+0

Наследовать от 'Control'. Включите 'UserPaint',' AllPaintingInWmPaint', 'OptimizedDoubleBuffer' в конструкторе. Переопределите 'OnPaint' и поместите туда логику рисования. –

+0

@TaW Нет, я не хочу, чтобы процесс рисования был видимым. Я хочу показать окончательный результат рисования всех прямоугольников за один раз. – Darko

ответ

0

Попробуйте вставить все в растровое изображение, а затем установите это растровое изображение как изображение PictureBox.

var bmp = new Bitmap(width, height); 
using (var g = Graphics.FromImage(bmp) 
{ 
    // draw rects 
} 

pb.Image = bmp; 

EDIT

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

// generate some random rects 
for (int i = 0; i < 5000; i++) 
{ 
    list.Add(new Rectangle(rnd.Next(0, pictureBox1.Width), rnd.Next(0, pictureBox1.Height), rnd.Next(1, pictureBox1.Width), rnd.Next(1, pictureBox1.Height))); 
} 

var bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); 

using (var g = Graphics.FromImage(bmp)) 
using (var path = new System.Drawing.Drawing2D.GraphicsPath()) 
{ 
    path.AddRectangles(list.ToArray()); 
    g.FillPath(Brushes.Red, path); 
    g.DrawPath(Pens.Black, path); 
} 

pictureBox1.Image = bmp; 

EDIT # 2

Следующие работает в менее 300мс в моей системе (без отладчика присоединенные):

private class Cell 
{ 
    public Color Color { get; set; } 
    public Rectangle Rectangle { get; set; } 
} 

private Cell[] cellsGrid; 
private Random rnd = new Random(); 

// generate 100k random cells 
private void btnNextStep_Click(object sender, EventArgs e) 
{ 
    this.cellsGrid = new Cell[100000]; 

    for (int i = 0; i < this.cellsGrid.Length; i++) 
    { 
     this.cellsGrid[i] = new Cell() 
     { 
      Color = Color.FromArgb(rnd.Next()), 
      Rectangle = new Rectangle(rnd.Next(0, pictureBox1.Width), rnd.Next(0, pictureBox1.Height), rnd.Next(1, 5), rnd.Next(1, 5)) 
     }; 
    } 

    this.DrawCells(); 
} 


// draw them and measure time taken 
private void DrawCells() 
{ 
    var start = DateTime.Now; 
    var bmp = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); 
    using (var g = Graphics.FromImage(bmp)) 
    using (var p = new Pen(Color.Black)) 
    using (var b = new SolidBrush(Color.Black)) 
    { 
     for (int i = 0; i < this.cellsGrid.Length; i++) 
     { 
      b.Color = this.cellsGrid[i].Color; 
      g.FillRectangle(b, this.cellsGrid[i].Rectangle); 

      p.Color = this.cellsGrid[i].Color; 
      g.DrawRectangle(p, this.cellsGrid[i].Rectangle); 
     } 
    } 

    this.pictureBox1.Image = bmp; 
    MessageBox.Show((DateTime.Now - start).TotalSeconds.ToString()); 
} 
+0

Я пробовал, но это тоже медленно. – Darko

+0

Сделал ли вы внешний вид экрана в фоновом потоке ui или ina? – TaW

+0

Ina таймер-обработчик событий, поэтому я не уверен, что это за поток :) – Darko

0

Лучшее решение, которое я нашел, чтобы улучшить производительность рисования прямоугольники в PictureBox - группировать прямоугольники одним цветом кисти в массивы и использовать метод Graphics.FillRectangles(Brush, Rectangle[]) вместо заполнения каждого прямоугольника отдельно с помощью Graphics.FillRectangle(Brush, Rectangle). Это ускоряет рисование дважды в моем случае, когда у меня обычно есть 3 разных цвета кисти. Однако это все равно в три раза медленнее, чем рисовать каждый прямоугольник отдельно в jPanel.

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

  • Нет связанных вопросов^_^