miércoles, 22 de agosto de 2012

Pintando un gráfico de tartas en un ImageBitMap

 

La verdad que esto del desarrollo sigue siendo muy interesante, aún en mi actual faceta de responsable de departamento y, por ende, con labores más de gestión que de picar código.

Le estoy echando una mano a un equipo que está desarrollando un Addin para Excel, y así pruebo qué tal va el VS2012 RTM, que ha mejorado bastante al ya de por sí bueno VS2012 RC.

Me encuentro que el programador anterior (que el demonio lo tenga en la más miserable celda posible en el infierno) debía tener conocimientos más bien cortitos y, para pintar un indicador que mostrara el porcentaje de un valor, no se le ocurrió otra cosa que hacer un bitmap por cada 10 grados de una imagen de una gráfica de tarta y, con cientos de líneas de código, montar un “chocho” impresionante para saber cual utilizar según el porcentaje.

En cuanto lo vi, me di cuenta que con un poquito de GDI+ lo tenia solucionado. Y así fue, casi mil líneas de código convertidas en los siguientes métodos:

   1: Using System.Drawing;
   2:  
   3: private Image PintarEnImageBitmap(Image imageIndicador, double valorPorcentual)
   4: {
   5:     // Aquí indico que voy a crear un nuevo Bitmap de 50x50px y con un formato ARGB de 32bpp
   6:     Bitmap dibujoDelValor = new Bitmap(50,50,System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
   7:     
   8:     // A continuación creo un nuevo objeto Graphics (sobre el que voy a pintar) a partir del Bitmap anterior
   9:     Graphics graficoDelValor = Graphics.FromImage(dibujoDelValor);
  10:     
  11:     // Declaro el pincel con el que voy a pintar la circunferencia que va a rodear la gráfica de tartaa
  12:     // incluyendo el color y el ancho del pincel        
  13:     System.Drawing.Pen pluma = new System.Drawing.Pen(System.Drawing.Color.White,2);
  14:     
  15:     // Declaro la brocha con la que voy a rellenar la gráfica de tarta
  16:     System.Drawing.SolidBrush brocha = new System.Drawing.SolidBrush(System.Drawing.Color.White);
  17:             
  18:     // Limpio el gráfico con el color negro. Ojo, en Excel el transparente no vá bien.
  19:     graficoDelValor.Clear(System.Drawing.Color.Black);
  20:  
  21:     // Le indico el grado de aliasing que quiero utilizar al dibujar
  22:     graficoDelValor.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  23:     
  24:     // Pinto la circunferencia que rodea a la gráfica de tarta
  25:     graficoDelValor.DrawEllipse(pluma, 0, 0, 47, 47);
  26:  
  27:     // Pinto la gráfica de tarta. Invoco a un método que me transforma el valorPorcentual a grados.
  28:     // 270 es para que empiece a pintar desde una linea vertical. Ya que 0 es la horizontal derecha.
  29:     graficoDelValor.FillPie(brocha, 0, 0, 47, 47, 270, PorcentajeAGrados(valorPorcentual));
  30:  
  31:     // Cargo el Bitmap resultante en un objeto Image
  32:     imageIndicador.Source = BitmapToSourceImage(dibujoDelValor);
  33:  
  34:     Return imageIndicador;
  35: }

El método PorcentajeAGrados, no puede ser más sencillo:



   1: private int PorcentajeAGrados(double valorPorcentual)
   2: {
   3:     return (int)(valorPorcentual * 3.6);
   4: }

Y el “truco del almendruco” es pasar el Bitmap GDI+ a un tipo ImageSource, que es el que puede leer los ImageBitmap de DirectX (y que por alguna extraña causa, el programador anterior prefirió utilizar en vez de los Bitmap GDI+).


Para ello, copio y pego el código que me he encontrando googleando, y que como no me lo he apuntado no lo puedo referenciar como debería hacer.



   1: /// <summary>
   2: /// Dado un objeto Bitmap lo convierte en un objeto ImageSource
   3: /// </summary>
   4: /// <param name="dibujoDelValor"></param>
   5: /// <returns></returns>
   6: private System.Windows.Media.ImageSource BitmapToSourceImage(System.Drawing.Bitmap dibujoDelValor)
   7: {
   8:     System.IO.MemoryStream ms = new System.IO.MemoryStream();
   9:     dibujoDelValor.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
  10:     System.Windows.Media.Imaging.BitmapImage bImg = new System.Windows.Media.Imaging.BitmapImage();
  11:     bImg.BeginInit();
  12:     bImg.StreamSource = new System.IO.MemoryStream(ms.ToArray());
  13:     bImg.EndInit();
  14:  
  15:     return bImg;
  16: }

Por lo cual, y resumiendo. Una llamada de ejemplo sería:



   1: Image imagenIndicador = new Image();
   2: imagenIndicador = PintarEnImageBitmap(imagenIndicador, 73);

Y el resultado, en el caso de la aplicación que estamos realizando es algo así:


image


Según me ha comentado Rodrigo, debería de investigar el no tener que hacer lo que estoy haciendo que es pintar en GDI+ y después utilizar objetos de DirectX, si no que debería pintar directamente con la propia API de Excel… pero eso es para nota.






Espero que sea de utilidad.

No hay comentarios: