So, I've written an image viewer that allows zooming, moving it around while zooming and Async-image loading to keep everything responsive. It works well with very little lag problems...except when I start displaying animated GIF images. In my program I store images in my own format, where all frames in the GIF are stored as bitmaps of ARGB32 (fastest format). A thread runs to increment the displayed frame and force a redraw using control.invalidate. Some excerpts:
Image frame is incremented, firing an (Async, not UI!) event handled by any listener (controls):
Which in turn...:Code:Private Sub UpdateAnimation() Dim now As Date = Date.Now Dim lastFrameChange As Date = now While Not disposedValue AndAlso Not Me.m_animateThread.IsAborting now = Date.Now If now.Subtract(lastFrameChange).TotalMilliseconds >= Me.m_frameDelays(Me.m_frameIndex) Then lastFrameChange = now Me.CurrentFrame += 1 End If Me.m_animateThread.WaitOne(5) End While End Sub
And then to redraw the image this is called by onPaint in the control the image is drawn in:Code:Private Sub OnFrameChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Me.Invalidate() End Sub
This all works excellently. The image animates at the right speed. But there is one major problem: the continuous invalidation of the image displaying control causes all other menus and controls to redraw poorly or not at all. When hovering over buttons, they don't change state. When resizing the window the controls hardly redraw and glitch out. Similarly in menus they don't pop up or take very long to change state when hovering over a menu item. It gives the appearance that the program is unresponsive. I don't see this happening in webbrowsers and the like, and at the same time I am aware that you can not perform screen drawing on another thread. So what to do? What can I do to prevent the entire application from freezing all drawing operations? Is this a problem caused by 'Invalidate'?Code:Public Sub Draw(ByVal g As Graphics, ByVal interpolation As Drawing2D.InterpolationMode, ByVal screenSize As Size, ByVal Offset As Point, ByVal Zoom As Double) If Me.IsDisposed Then Return End If Dim srcRect As New RectangleF(0, 0, Me.Width, Me.Height) Dim destRect As New RectangleF(-Offset.X, -Offset.Y, Width * Zoom, Height * Zoom) ' Draw background gradient image if destRect does not cover the entire screen If destRect.Left > 0 OrElse destRect.Top > 0 OrElse destRect.Right < Me.Width OrElse destRect.Height < Me.Height Then If BackgroundImage.Width > 1 AndAlso BackgroundImage.Height > 1 Then Dim thumbSrcRect As New RectangleF(0.0F, 0.0F, BackgroundImage.Width - 1.0F, BackgroundImage.Height - 1.0F) g.InterpolationMode = Drawing2D.InterpolationMode.Low g.DrawImage(BackgroundImage, New RectangleF(0, 0, screenSize.Width, screenSize.Height), thumbSrcRect, GraphicsUnit.Pixel) End If End If ' Calculate the offset and zoom, and draw the image in the resulting rectangle If destRect.Left < 0 Then srcRect.X -= destRect.X / Zoom srcRect.Width -= srcRect.X destRect.Width += destRect.X destRect.X = 0 End If If destRect.Top < 0 Then srcRect.Y -= destRect.Y / Zoom srcRect.Height -= srcRect.Y destRect.Height += destRect.Y destRect.Y = 0 End If If destRect.Right > screenSize.Width Then srcRect.Width -= ((destRect.Right - screenSize.Width) / Zoom) - 1.0 destRect.Width = (screenSize.Width - destRect.Left) + Zoom End If If destRect.Bottom > screenSize.Height Then srcRect.Height -= ((destRect.Bottom - screenSize.Height) / Zoom) - 1.0 destRect.Height = (screenSize.Height - destRect.Top) + Zoom End If g.InterpolationMode = interpolation g.DrawImage(Image, destRect, srcRect, GraphicsUnit.Pixel) End Sub
Using a timer instead of a thread to routinely call invalidate during animation did not help.




Reply With Quote
