Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Graphics
  4. Drawing in background thread

Drawing in background thread

Scheduled Pinned Locked Moved Graphics
graphicshelptutorialquestionannouncement
7 Posts 3 Posters 3 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Aaron Schaefer
    wrote on last edited by
    #1

    Help! I'm working on a control that does a fair amount of work to render itself. I've got it set up where it basically does all of its drawing to a bitmap that it holds on to, and then in the Paint method just copies the data from the bitmap to the client area, depdning on how much of it is currently visible, zoom factor, etc. Certain operations by the user can cause this bitmap to need to be redrawn. This is a time consuming operation (might take 5-10 seconds), and I'd like to update the bitmap in a worker thread, so that the app doesn't freeze while its doing its rendering. Anyone know how to go about this? I created a BackgroundWorker, as follows: I initialize the worker here: worker = new BackgroundWorker(); worker.WorkerReportsProgress = false; worker.WorkerSupportsCancellation = false; worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.DoWork += new DoWorkEventHandler(worker_DoWork); Then, if my bitmap needs to be redrawn, I start it here: worker.RunWorkerAsync(); void worker_DoWork(object sender, DoWorkEventArgs e) { // Create a new bitmap for the log int width = (int)Math.Round(Width * fDPIX); int height = LogHeightPixels; bmp = new Bitmap(width, height); DrawLogToBitmap(bmp); e.Result = bmp; } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Bitmap bmpNew = (Bitmap)e.Result; if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; } In the DoWork handler, I create a new bitmap, do the drawing, and then assign the bitmap to the Result parameter in the RunWorkerCompletedEventArgs. It looks fine here, but then I get back to the main thread (in the RunWorkerCompleted) and the bitmap looks like garbage there. Any ideas what I might be doing wrong? Thanks In Advance, Aaron

    M 1 Reply Last reply
    0
    • A Aaron Schaefer

      Help! I'm working on a control that does a fair amount of work to render itself. I've got it set up where it basically does all of its drawing to a bitmap that it holds on to, and then in the Paint method just copies the data from the bitmap to the client area, depdning on how much of it is currently visible, zoom factor, etc. Certain operations by the user can cause this bitmap to need to be redrawn. This is a time consuming operation (might take 5-10 seconds), and I'd like to update the bitmap in a worker thread, so that the app doesn't freeze while its doing its rendering. Anyone know how to go about this? I created a BackgroundWorker, as follows: I initialize the worker here: worker = new BackgroundWorker(); worker.WorkerReportsProgress = false; worker.WorkerSupportsCancellation = false; worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.DoWork += new DoWorkEventHandler(worker_DoWork); Then, if my bitmap needs to be redrawn, I start it here: worker.RunWorkerAsync(); void worker_DoWork(object sender, DoWorkEventArgs e) { // Create a new bitmap for the log int width = (int)Math.Round(Width * fDPIX); int height = LogHeightPixels; bmp = new Bitmap(width, height); DrawLogToBitmap(bmp); e.Result = bmp; } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Bitmap bmpNew = (Bitmap)e.Result; if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; } In the DoWork handler, I create a new bitmap, do the drawing, and then assign the bitmap to the Result parameter in the RunWorkerCompletedEventArgs. It looks fine here, but then I get back to the main thread (in the RunWorkerCompleted) and the bitmap looks like garbage there. Any ideas what I might be doing wrong? Thanks In Advance, Aaron

      M Offline
      M Offline
      Mark Salsbery
      wrote on last edited by
      #2

      What language is that code in? If the "bmp" created in worker_DoWork is the same one disposed of in worker_RunWorkerCompleted, and it's the same one passed in e.Result, then doesn't Bitmap bmpNew = (Bitmap)e.Result; <-- isn't bmpNew == bmp here? if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; } dispose the bitmap before you use it?

      "Great job, team. Head back to base for debriefing and cocktails." (Spottswoode "Team America")

      A 1 Reply Last reply
      0
      • M Mark Salsbery

        What language is that code in? If the "bmp" created in worker_DoWork is the same one disposed of in worker_RunWorkerCompleted, and it's the same one passed in e.Result, then doesn't Bitmap bmpNew = (Bitmap)e.Result; <-- isn't bmpNew == bmp here? if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; } dispose the bitmap before you use it?

        "Great job, team. Head back to base for debriefing and cocktails." (Spottswoode "Team America")

        A Offline
        A Offline
        Aaron Schaefer
        wrote on last edited by
        #3

        You're right, I wan't paying attention there. Updated code looks like this: void worker_DoWork(object sender, DoWorkEventArgs e) { int width = (int)Math.Round(Width * fDPIX); int height = LogHeightPixels; Bitmap bmpNew = new Bitmap(width, height/*, System.Drawing.Imaging.PixelFormat.Gdi/*.Format32bppArgb*/); DrawLogToBitmap(bmpNew); e.Result = bmpNew; } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Bitmap bmpNew = (Bitmap)e.Result; if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; Refresh(); } Refresh ends up calling my Draw method here: public override void Draw(Graphics gfx, Rectangle rect) { gfx.ScaleTransform(zoom, zoom); // Update the bitmap (only as needed) if (bDirty) { UpdateBitmap(gfx); } if (bmp == null || worker.IsBusy) { gfx.DrawString("Updating Bitmap, please wait!", this.Font, Brushes.Black, new PointF(0, 0)); return; } RectangleF rcSource = new RectangleF(-horizontalOffset, -verticalOffset, ClientRectangle.Width/zoom, ClientRectangle.Height/zoom); RectangleF rcDest = new RectangleF(ClientRectangle.Location.X, ClientRectangle.Location.Y, ClientRectangle.Size.Width/zoom, ClientRectangle.Size.Height/zoom); try { gfx.DrawImage(bmp, rcDest, rcSource, GraphicsUnit.Pixel); } catch (Exception exc) { gfx.DrawString(exc.Message, Font, Brushes.Black, new PointF(0,0)); } etc.... } Now, the bitmap seems OK, but I get an out of memory exception when I call DrawImage (hence the try/catch block). Using the exact same drawing code in the main thread causes no problems, even though it's a pretty big bitmap (2496 * 49152 * 32 bpp). If I try using a bitmapp with a different pixel format, say 16bpp to conserve memory, performance becomes terrible, but I don't get the exception.

        M V 2 Replies Last reply
        0
        • A Aaron Schaefer

          You're right, I wan't paying attention there. Updated code looks like this: void worker_DoWork(object sender, DoWorkEventArgs e) { int width = (int)Math.Round(Width * fDPIX); int height = LogHeightPixels; Bitmap bmpNew = new Bitmap(width, height/*, System.Drawing.Imaging.PixelFormat.Gdi/*.Format32bppArgb*/); DrawLogToBitmap(bmpNew); e.Result = bmpNew; } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Bitmap bmpNew = (Bitmap)e.Result; if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; Refresh(); } Refresh ends up calling my Draw method here: public override void Draw(Graphics gfx, Rectangle rect) { gfx.ScaleTransform(zoom, zoom); // Update the bitmap (only as needed) if (bDirty) { UpdateBitmap(gfx); } if (bmp == null || worker.IsBusy) { gfx.DrawString("Updating Bitmap, please wait!", this.Font, Brushes.Black, new PointF(0, 0)); return; } RectangleF rcSource = new RectangleF(-horizontalOffset, -verticalOffset, ClientRectangle.Width/zoom, ClientRectangle.Height/zoom); RectangleF rcDest = new RectangleF(ClientRectangle.Location.X, ClientRectangle.Location.Y, ClientRectangle.Size.Width/zoom, ClientRectangle.Size.Height/zoom); try { gfx.DrawImage(bmp, rcDest, rcSource, GraphicsUnit.Pixel); } catch (Exception exc) { gfx.DrawString(exc.Message, Font, Brushes.Black, new PointF(0,0)); } etc.... } Now, the bitmap seems OK, but I get an out of memory exception when I call DrawImage (hence the try/catch block). Using the exact same drawing code in the main thread causes no problems, even though it's a pretty big bitmap (2496 * 49152 * 32 bpp). If I try using a bitmapp with a different pixel format, say 16bpp to conserve memory, performance becomes terrible, but I don't get the exception.

          M Offline
          M Offline
          Mark Salsbery
          wrote on last edited by
          #4

          I've never done C#....what thread does the worker_RunWorkerCompleted() get executed on? Thanks, Mark

          "Great job, team. Head back to base for debriefing and cocktails." (Spottswoode "Team America")

          A 1 Reply Last reply
          0
          • M Mark Salsbery

            I've never done C#....what thread does the worker_RunWorkerCompleted() get executed on? Thanks, Mark

            "Great job, team. Head back to base for debriefing and cocktails." (Spottswoode "Team America")

            A Offline
            A Offline
            Aaron Schaefer
            wrote on last edited by
            #5

            That one runs on the main thread. The handler for the DoWork event runs in the worker thread. Aaron

            M 1 Reply Last reply
            0
            • A Aaron Schaefer

              That one runs on the main thread. The handler for the DoWork event runs in the worker thread. Aaron

              M Offline
              M Offline
              Mark Salsbery
              wrote on last edited by
              #6

              Hmm then with a (close to) 500MB image maybe the thread adds just enough to put memory use over the top? When you do this: Bitmap bmpNew = (Bitmap)e.Result; That just copies a reference, right? It doesn't make a new duplicate object?

              "Great job, team. Head back to base for debriefing and cocktails." (Spottswoode "Team America")

              1 Reply Last reply
              0
              • A Aaron Schaefer

                You're right, I wan't paying attention there. Updated code looks like this: void worker_DoWork(object sender, DoWorkEventArgs e) { int width = (int)Math.Round(Width * fDPIX); int height = LogHeightPixels; Bitmap bmpNew = new Bitmap(width, height/*, System.Drawing.Imaging.PixelFormat.Gdi/*.Format32bppArgb*/); DrawLogToBitmap(bmpNew); e.Result = bmpNew; } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Bitmap bmpNew = (Bitmap)e.Result; if (bmp != null) { bmp.Dispose(); bmp = null; } bmp = bmpNew; Refresh(); } Refresh ends up calling my Draw method here: public override void Draw(Graphics gfx, Rectangle rect) { gfx.ScaleTransform(zoom, zoom); // Update the bitmap (only as needed) if (bDirty) { UpdateBitmap(gfx); } if (bmp == null || worker.IsBusy) { gfx.DrawString("Updating Bitmap, please wait!", this.Font, Brushes.Black, new PointF(0, 0)); return; } RectangleF rcSource = new RectangleF(-horizontalOffset, -verticalOffset, ClientRectangle.Width/zoom, ClientRectangle.Height/zoom); RectangleF rcDest = new RectangleF(ClientRectangle.Location.X, ClientRectangle.Location.Y, ClientRectangle.Size.Width/zoom, ClientRectangle.Size.Height/zoom); try { gfx.DrawImage(bmp, rcDest, rcSource, GraphicsUnit.Pixel); } catch (Exception exc) { gfx.DrawString(exc.Message, Font, Brushes.Black, new PointF(0,0)); } etc.... } Now, the bitmap seems OK, but I get an out of memory exception when I call DrawImage (hence the try/catch block). Using the exact same drawing code in the main thread causes no problems, even though it's a pretty big bitmap (2496 * 49152 * 32 bpp). If I try using a bitmapp with a different pixel format, say 16bpp to conserve memory, performance becomes terrible, but I don't get the exception.

                V Offline
                V Offline
                vineas
                wrote on last edited by
                #7

                Since your Draw method makes sure that while the background thread is busy, a draw doesn't happen on the bitmap - you don't need to keep creating a new bitmap. In fact, looking at the size of the bitmap you're creating, I bet a lot of your 5-10 second draw is taken up by simply creating that bitmap object. This is of course making the assumption that your bitmap size isn't constantly changing - if that is the case, then ignore this post - if the bitmap size is (fairly) constant, then keep reading.

                class SomeForm : Form
                {
                private Bitmap _theImage;
                private bool _working = false;

                void worker_DoWork(object sender, DoWorkEventArgs e)
                {
                // make sure the image is initialized (may as well do it on the background thread).
                if (_theImage == null)
                {
                int width = (int)Math.Round(Width * fDPIX);
                int height = LogHeightPixels;
                _theImage = new Bitmap(width, height/*, System.Drawing.Imaging.PixelFormat.Gdi/*.Format32bppArgb*/);
                }
                // note, you may need to clear the bitmap in this method call.
                // it all depends on what the draw log method is doing.
                DrawLogToBitmap(_theImage);
                }

                void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
                {
                _working = false;
                Refresh();
                }

                public override void Draw(Graphics gfx, Rectangle rect)
                {
                gfx.ScaleTransform(zoom, zoom);

                // Update the bitmap (only as needed)
                if (bDirty)
                {
                   // I'm assuming this method starts the worker thread - if so:
                   \_working = true;
                   UpdateBitmap(gfx);
                }
                
                if (\_working || worker.IsBusy)
                {
                   gfx.DrawString("Updating Bitmap, please wait!", this.Font, Brushes.Black, new PointF(0, 0));
                   return;
                }
                
                RectangleF rcSource = new RectangleF(-horizontalOffset, -verticalOffset, ClientRectangle.Width/zoom, ClientRectangle.Height/zoom);
                RectangleF rcDest = new RectangleF(ClientRectangle.Location.X, ClientRectangle.Location.Y, ClientRectangle.Size.Width/zoom, ClientRectangle.Size.Height/zoom);
                
                try
                {
                   gfx.DrawImage(\_theImage, rcDest, rcSource, GraphicsUnit.Pixel);
                }
                catch (Exception exc)
                {
                   gfx.DrawString(exc.Message, Font, Brushes.Black, new PointF(0,0));
                }
                etc....
                

                }
                }

                I may have missed something (I've been up for FAR too long, and can't sleep for some reason), but that's the gist of it. You'll probably want to do a few more checks to make sure the bitmap isn't being accessed from multiple threads at the same

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • World
                • Users
                • Groups