2010-10-25 14 views
7

Estoy intentando crear una aplicación de gráficos simple en WPF C#. El objetivo es dibujar 10000 * 10000 rectángulos de tamaño 4 píxeles cada uno.Implementar una operación más rápida de gráficos en WPF Canvas

He modificado el método OnRender del lienzo para dibujar los rectángulos. Los dibujos se realizan para un número menor de rectángulos (digamos 50 * 50 o 100 * 100 rectángulos de 4 píxeles cada uno), pero se está desacelerando a medida que aumente el número. de rectángulos.

que sigue es mi código:

protected override void OnRender(DrawingContext dc) 
    { 
     base.OnRender(dc); 

     FillCells(dc); 

     if (_ShowGrids) 
     { 
      DrawGrid(dc); // draw grid lines 
     } 
    } 
void FillCells(DrawingContext dc) 
    { 

     int cellSize=4; 

     for (int i = 0; i < MaxRow; i++) 
     { 
      for (int j = 0; j < MaxColumn; j++) 
      { 
       dc.DrawRectangle(GetRectBrush(i,j), GetRectPen(i,j), new Rect(j * cellSize , i * cellSize , cellSize - 1, cellSize - 1)); 

      } 
     } 
    } 

El código anterior se lleva más de un minuto para dibujar rectángulos 1000 * 1000.

¿Hay algún método para acelerar este proceso? ¿Hay alguna otra cosa que pueda usar en lugar de esto?

Gracias.

+0

¿Ha considerado usar un visualBrush? – TerrorAustralis

+0

No, no lo hice. ¿Podría darme algunos ejemplos de Visual Brush por favor? –

+0

¿Está almacenando los colores de cada rectangel en la memoria? Si no, ¿qué está haciendo GetRectBrush (i, j)? – TerrorAustralis

Respuesta

11

El propósito es dibujar 10000 * 10000 rectángulos de tamaño 4 píxeles cada uno.

NO las dibuje. Así de simple. Esto sería de 40k a 40k píxeles.

La mayoría no estarán visibles. Entonces no deben dibujarse. Básicamente solo dibuja aquellos que son visibles en el lienzo. Cuando cambie el tamaño o el desplazamiento, vuelva a pintar de todos modos, luego haga lo mismo, solo dibuje los que sean visibles.

La virtualización es la clave del rendimiento aquí. Saque las cosas del ciclo de dibujo lo antes posible. Las cosas no visibles por definición no necesitan dibujarse en absoluto.

La siguiente alternativa sería no utilizar un lienzo. Pruebe un mapa de bits. Prepárelo en un hilo separado, luego dibuje este a la vez.

+0

Esta es la única forma de aumentar el rendimiento, creo. También pensé en implementar este mecanismo para dibujar pero no pude calcular lo visible. del lienzo. También hice una pregunta para este problema aquí, busqué en Google pero no tuve la suerte. –

+1

+1. Dibujar 100M de cualquier cosa es una locura. – alxx

+0

+1 @alxx. Pero, cuando se trata de controles como DataGrid o algunos controles irregulares de cuadrícula. Está absolutamente bien. En algunos casos puede requerir incluso más de 100 M. –

0

Quizás trate de superponer el lienzo con un VisualBrush.
Para este visualBrush simplemente agrega el rectángulo 4 * 4 y haz que se repita en modo mosaico. Alternativamente, puede agregar las líneas para que no se superponga a los bordes del rectángulo ... su elección :)

Su problema está en la creación del pincel ... Una ejecución de prueba indicó que este código

int limit = 10000 * 10000; 
var converter = new BrushConverter(); 
for (int i = 0; i < limit; i++) 
{ 
    var blueBrush = converter.ConvertFromString("Blue") as Brush; 
} 

tardan 53 segundos en ejecutarse. Está tratando de crear 100,000,000 pinceles :) Si es posible, use un pincel visual con patrones, si no es posible ... busque otra solución. La sobrecarga de almacenamiento que muchos cepillos en la memoria se encuentra en la Gigabytes

+0

cepillo visual no servirá para el propósito porque los rectángulos pueden ser de diferentes colores. Pincel visual replica cualquier elemento visual. –

+0

¿Cómo coloreas los rectángulos? se ve en su ejemplo como si todos fueran azules :) – TerrorAustralis

+0

He modificado el código ahora para obtener el color de las rectas. –

2

No es necesario volver a crear el pincel para cada iteración del bucle, ya que utilizan el mismo color y otra vez:

SolidColorBrush blueBrush = new SolidColorBrush(Colors.Blue) 
SolidColorPen bluePen = new SolidColorPen(blueBrush) 

for (int i = 0; i < MaxRow; i++) 
{ 
    for (int j = 0; j < MaxColumn; j++) 
    { 
     dc.DrawRectangle(blueBrush, bluePen, 1), new Rect(j * cellSize , i * cellSize , cellSize - 1, cellSize - 1)); 
    } 
} 

Esto puede acelerar el ciclo un poco.

+0

Un poco más rápido, pero aún lento :( –

+0

No es el problema real, pero es un buen consejo –

3

Deberías probar StreamGeometry entonces. http://msdn.microsoft.com/en-us/library/system.windows.media.streamgeometry.aspx

Para geometrías complejas que no necesitan a ser modificado después de su creación, debe considerar el uso StreamGeometry en lugar de PathGeometry como una actuación optimización. StreamGeometry funciona como PathGeometry, excepto que puede solo rellenarse mediante código de procedimiento. Su nombre extraño se refiere a un detalle aplicación: Para utilizar menos memoria (y menos de la CPU), sus PathFigures y PathSegments son almacenado como un flujo de bytes compacto en lugar de un gráfico de objetos .NET.

Citado del libro de Adam Nathan WPF Unleashed.

+0

WHOW - eso fue algo inaudito para mí. Algo para leer. – TomTom

+0

+1 para WPF Unleashed. Impresionante libro. – Grozz

2

Una sugerencia más que todo lo que todos han dicho, asegúrese de que los bolígrafos y las brochas estén congelados: si crea el pincel, llame a Congelar antes de usarlo (los pinceles de la clase Pinceles (Pinceles.piedras blancas) ya están congelados).

+0

+1 en rendimiento. ¡Gracias! –

1

El enfoque del mapa de bits puede acelerar más: BitmapSource tiene un método Create que toma los datos brutos como una matriz o un puntero a la memoria insegura. Debe ser un poco más rápido establecer valores en una matriz que dibujar rectángulos reales; sin embargo, debe verificar los formatos de píxeles para establecer correctamente los píxeles individuales.

Cuestiones relacionadas