domingo, 12 de julio de 2009

Implementación de Optimized Double Buffer

Existe un problema llamado flicker que nos va a aparecer siempre que vayamos a dibujar cosas muy complejas mediante Graphics. Nos podemos dar cuenta de ésto cuando el contenido del formulario parece titilar.

Podemos solucionar el problema utilizando las clases BufferedGraphics y BufferedGraphicsContext del .Net Framework.

Double Buffering consiste en usar un buffer en memoria asociado a las multiples operacinoes de pintado (rendering). Cuando Double Buffering está habilidado todas las operaciones de pintado son renderizadas primero al buffer de memoria en vez de dibujarlo directamente al formulario. Después de que se dibuja en el buffer, se copia el contenido de éste directamente al formulario asociado. Entonces como se realiza solamente una operación de pintado hacia el formulario, el flicker es eliminado.


public partial class Form1 : Form

    {

        Timer m_renderTimer;

        Timer m_drawTimer;

        BufferedGraphics m_buffer;

        BufferedGraphicsContext m_context;

        Rectangle m_rectangle;

        int m_xVel = 3;

        int m_yVel = 3;

        public Form1()

        {

            InitializeComponent();

            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            SetStyle(ControlStyles.UserPaint, true);

 

            this.Resize += new EventHandler(Form1_Resize);

 

            m_renderTimer = new Timer();

            m_renderTimer.Interval = 30;

            m_renderTimer.Tick += new EventHandler(m_renderTimer_Tick);

 

            m_drawTimer = new Timer();

            m_drawTimer.Interval = 20;

            m_drawTimer.Tick += new EventHandler(m_drawTimer_Tick);

 

            m_context = BufferedGraphicsManager.Current;

 

            m_rectangle = new Rectangle(0, 0, 10, 10);

 

            GetGraphics();

        }

 

        void Form1_Resize(object sender, EventArgs e)

        {

            GetGraphics();

        }

       private void GetGraphics()

        {

            m_buffer = m_context.Allocate(this.CreateGraphics(), this.DisplayRectangle);

        }

 

        void m_drawTimer_Tick(object sender, EventArgs e)

        {

            ProcessItem();

            DrawItems(m_buffer.Graphics);

        }

 

        private void ProcessItem()

        {

            if (m_rectangle.X > this.DisplayRectangle.Width-10 || m_rectangle.X <0)

                     m_xVel *= -1;

            if (m_rectangle.Y > this.DisplayRectangle.Height- 10 || m_rectangle.Y <0)

                m_yVel *= -1;

           m_rectangle.X += m_xVel;

            m_rectangle.Y += m_yVel;

        }

      

        private void DrawItems(Graphics graphics)

        {

            graphics.Clear(Color.Black);

            graphics.FillEllipse(Brushes.Red, m_rectangle);

        }

 

        void m_renderTimer_Tick(object sender, EventArgs e)

        {

            m_buffer.Render(this.CreateGraphics());

        }

 

        protected override void OnShown(EventArgs e)

        {

            m_renderTimer.Start();

            m_drawTimer.Start();

            base.OnShown(e);

        }

    }

Este ejemplo tiene dos Timer, uno para mover la pelota y dibujarla en el buffer y el segundo que copia el buffer al formulario para que lo dibuje. Jueguen un poco con las variables de los Timers para que entiendan bien.

Además de que cada vez que el formulario cambia de tamaño hay que crear el buffer,por eso que  existe un método GeBuffer y se llama al principio y dentro del evento de Resize del formulario.

Actualización: gracias al lector raul338 por corregir un error en una línea!

Gracias por leer!

2 comentarios:

  1. hay un error en esta linea:

    if (m_rectangle.X > this.DisplayRectangle.Width-10 || m_rectangle.X <>
    m_xVel *= -1;

    Justo que me interesaba este tema, tuve problemas hace un tiempo :P jeje

    ResponderEliminar
  2. Si, gracias, ya arreglé el error, puedes probar de nuevo el proyecto!

    ResponderEliminar