Optimizing Repeated PictureBox.Paints

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • The Confessor

    #1

    Optimizing Repeated PictureBox.Paints

    I'm trying to develop a graphically-simple, gameplay-complex RPG, and I
    decided to create a graphical Proof of Concept just to confirm that
    graphics would be... well, simple.

    Predictably, they've proven otherwise.

    This is my PictureBoxMap_P aint() Sub, which a tiles a PictureBox with 19*
    17 40*40-pixel images, two rows and two columns of which are painted
    beyond the current bounds of the picturebox to facilitate scrolling. The
    If/Else statements handle the bounds of the map... At the moment they
    simply 'roll over' and display the graphics on the other side.

    Dim A, B As Integer
    For A = -9 To 9
    For B = -8 To 8
    Dim X, Y As Integer
    If A + CurrentCoords.E ast < 0 Then
    X = Cell.GetUpperBo und(0) + (A + CurrentCoords.E ast) + 1
    ElseIf A + CurrentCoords.E ast > Cell.GetUpperBo und(0) Then
    X = (A + CurrentCoords.E ast) - Cell.GetUpperBo und(0) - 1
    Else
    X = A + CurrentCoords.E ast
    End If
    If B + CurrentCoords.N orth < 0 Then
    Y = Cell.GetUpperBo und(1) + (B + CurrentCoords.N orth) +
    ElseIf B + CurrentCoords.N orth > Cell.GetUpperBo und(1) Then
    Y = (B + CurrentCoords.N orth) - Cell.GetUpperBo und(1) - 1
    Else
    Y = B + CurrentCoords.N orth
    End If
    e.Graphics.Draw Image(CellImage s(Cell(X, Y,
    CurrentCoords.U p).Image), ((A + 9) * 40) - 40 + XOffSet, (520 - ((B + 6)
    * 40) + YOffSet))
    Next
    Next

    The XOffSet and YOffSet are used to produce the scrolling effect, as seen
    in this code from my Form_KeyDown procedure.

    If e.KeyCode = System.Windows. Forms.Keys.Left Then
    For XOffSet = 0 To 40 Step 4
    PictureBox_Map. Refresh()
    Next
    XOffSet = 0
    If CurrentCoords.E ast > 0 Then
    CurrentCoords.E ast = CurrentCoords.E ast - 1
    Else
    CurrentCoords.E ast = Cell.GetUpperBo und(0)
    End If
    PictureBox_Map. Refresh()
    End If

    Unfortunately, this produces a very slow scroll on my Pentium III 933MHZ;
    I've had to add the equivalent of frame-skipping using 'Step 4' just to
    make it comparable to the speed in, say, Final Fantasy.

    My theory for the reason behind this slowness was the reassembly of the
    map with each PictureBox_Map. Refresh(), and I therefore searched for a
    way to offset a preassembled graphic, reassembling only when necessary.

    Unfortunately, I could not then force PictureBox_Map to display this
    preassembled graphic in such a way that it could be offset as necessary
    to produce scrolling.

    An IRC acquaintance who otherwise has been very helpful suggested that
    calling PictureBox_Map. Refresh might be producing some unintended
    overhead, and that calling the PictureBox_Pain t() routine directly from
    KeyDown might be better.

    Unfortunately, I could not call it in such a way that my entry for the
    PaintEventArgs argument would not completely break the PictureBox_Pain t
    routine.

    Does anybody have any suggests as to where, exactly, most of my overhead
    is coming from and how I might reduce or eliminate it?

    Are repliers have my sincere gratitude.
  • Larry Lard

    #2
    Re: Optimizing Repeated PictureBox.Pain ts


    The Confessor wrote:[color=blue]
    > I'm trying to develop a graphically-simple, gameplay-complex RPG, and I
    > decided to create a graphical Proof of Concept just to confirm that
    > graphics would be... well, simple.[/color]
    [snip]

    I won't go into details, because I think your performance problems can
    be best addressed by conceptual changes rather than particular code
    changes.

    The way to handle a picturebox that displays a complex graphics is not
    to build the graphics in the Paint event, but rather to maintain a
    Bitmap, which holds the actual image, and in the Paint event just
    DrawImage the bitmap onto the Graphics provided. When the image is
    required to change, draw the changes onto the Bitmap, then Invalidate
    the picturebox to force it to paint. Here is a short example:

    ' VS2003/2005
    ' create a new Windows Forms app
    ' drop a picturebox and two buttons on a form
    ' add this at the top of the code file
    Imports System.Drawing

    'add this in the form, before the End Class line
    Private myImage As Bitmap
    Private myImageGraphics As Graphics
    'probably more correct to create the Graphics
    'whenever we want to draw
    'but this is quicker

    Private Sub Form1_Load(ByVa l sender As Object, ByVal e As
    System.EventArg s) Handles MyBase.Load
    myImage = New Bitmap(PictureB ox1.Width, PictureBox1.Hei ght,
    Imaging.PixelFo rmat.Format32bp pArgb)
    myImageGraphics = Graphics.FromIm age(myImage)

    myImageGraphics .Clear(Color.Wh ite)
    End Sub

    Private Sub PictureBox1_Pai nt(ByVal sender As Object, ByVal e As
    System.Windows. Forms.PaintEven tArgs) Handles PictureBox1.Pai nt
    'all we do in here is draw myImage
    e.Graphics.Draw Image(myImage, e.ClipRectangle , e.ClipRectangle ,
    GraphicsUnit.Pi xel)
    End Sub

    Private Sub Button1_Click(B yVal sender As System.Object, ByVal e As
    System.EventArg s) Handles Button1.Click
    'create a complex image
    Dim x As Integer, y As Integer
    Dim b As Brush, c As Color
    Dim r As New Random

    For x = 0 To myImage.Width Step 10
    For y = 0 To myImage.Height Step 8
    c = Color.FromArgb( r.Next(0, 255), r.Next(0, 255),
    r.Next(0, 255))
    b = New SolidBrush(c)
    Try
    myImageGraphics .FillRectangle( b, x, y, 10, 8)
    Finally
    b.Dispose()
    End Try
    Next
    Next

    'and force painting
    PictureBox1.Inv alidate()
    End Sub

    Private Sub Button2_Click(B yVal sender As System.Object, ByVal e As
    System.EventArg s) Handles Button2.Click
    'make some change to the image
    Dim oldImage As Bitmap = myImage
    myImage = New Bitmap(PictureB ox1.Width, PictureBox1.Hei ght,
    Imaging.PixelFo rmat.Format32bp pArgb)
    myImageGraphics = Graphics.FromIm age(myImage)

    myImageGraphics .ResetTransform ()
    myImageGraphics .TranslateTrans form(-myImage.Width \ 2,
    -myImage.Height \ 2, Drawing2D.Matri xOrder.Append)
    myImageGraphics .RotateTransfor m(10,
    Drawing2D.Matri xOrder.Append)
    myImageGraphics .TranslateTrans form(myImage.Wi dth \ 2,
    myImage.Height \ 2, Drawing2D.Matri xOrder.Append)
    myImageGraphics .DrawImage(oldI mage, 0, 0)

    PictureBox1.Inv alidate()
    End Sub



    Your situation is going to involve creating the main image with an
    extra strip of cells, as currently, and some error-prone transformation
    stuff working out exactly which portion of the image to draw in the
    Paint event, so have fun with that :) The key point is that we only do
    the 'hard' work (that nested loop that creates the image) when we have
    to, ie when we scrol to a region we haven't seen before. While
    examining a portion of the image we *have* created, all we do is just
    DrawImage.

    --
    Larry Lard
    Replies to group please

    Comment

    Working...