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. .NET (Core and Framework)
  4. How to shift a bitmap efficiently (Spectrogram Application) [modified]

How to shift a bitmap efficiently (Spectrogram Application) [modified]

Scheduled Pinned Locked Moved .NET (Core and Framework)
graphicsc++winformsgame-devperformance
16 Posts 2 Posters 0 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.
  • L Luc Pattyn

    If I understand correctly, you have at most a 1024*1024 color image, that is scrolling horizontally and gets up to 25 columns added per second, with a producer you control. That should not be any problem. Yes, I think I have used ScrollWindows in the dark ages of Win32, I wouldn't need it in .NET I would consider a Control (say a Panel) that would show a Bitmap and accept a columnar data update. Give me 24 hours, I'll experiment a bit in C#, and get back to you. What would you want for initial content? axes? grid lines? legend? BTW: I found a plotting graph here: High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^]. Haven't looked inside yet. :)

    Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

    Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

    B Offline
    B Offline
    bimbambumbum
    wrote on last edited by
    #7

    Hi Luc, Sounds cool! looking forward to see what you come up with. For the spectrogram I wouldn't worry about axis for my app but if you want to write an article you might want to put a frequency and time axis and possible a colorbar which maps the spectrogram colors to an intensity for the frequency components. Let me know if I can contribute in any way...going to bed know Thx Tom

    1 Reply Last reply
    0
    • B bimbambumbum

      Hi I'm working on a Spectrum analyzer application and need some help to get the graphics updated in an optimal manner. Whenever my program receives a new block of audio data I do an FFT and I then update the oldest FFT data (a column) in my 2D-matrix which is then copied to a bitmap for display. So this spectrogram is shifted horizontally as data arrives in blocks. I see two options for achieving the shifting of the NxM bitmap whenever a new FFT output block arrives: 1. Copy columns [0:M-2] to [1:M-1] and then copy in the new FFT data...hope the notation makes sense. This requires a lot of memory copying and I hope to avoid that using GDI. 2. Replace the "oldest" column (the matrix works as a circular buffer in the time-direction) and let GDI ensure that when wrap-around happens GDI the remaining columns (the columns up to the column where the new FFT data resides) are copied to the bitmap. I once did a spectrum Analyzer in C++ project using OpenGL that supports this functionality. Can find the project anymore..I tried to look for it to find the name of the instruction used. Does GDI/GDI+ support this? Do any of you guys know how I get to do this efficiently? Thx Tom

      modified on Sunday, September 19, 2010 8:00 PM

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #8

      Hi Tom, 1. I studied the article High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^] a bit; it has a Plotter class which is a UserControl displaying a number of traces, each trace defined as a sequence of points. So there is no bitmap, no image, involved at all. It basically is vector graphics, which makes it high-performance. 2. I did some experiments with a bitmap-based plotter-like Panel, where one column of pixels would be updated at a time, using the circular buffer approach we discussed earlier. It is double-buffered to avoid flickering, and it works remarkably smooth, in fact it looks much better than the other article's demo, however I suspect that is due to the demo having fewer data points than the plotter is wide, causing the graph to jump all the time. At 512*512 my plotter happily handles 30 updates (frames) per second, using between 10 and 15% of a middle-of-the-road dual-core (I use both cores, as one executes the main thread, i.e. the painting, whereas the other calculates the new column in a BackgroundWorker). Yet I am convinced it takes too much CPU load for the net result. So I think I should look into other technologies, being OpenGL and DirectX. And I will, in time. 3. For the moment, I'm convinced if all that gets shown basically is a line graph, one should not resort to images at all. It will be much more lightweight when the graphs are drawn straight from a sequence of points, no matter what technology is used for painting images. ADDED Of course, when doing so, there is no problem whatsoever to make it scroll. /ADDED :)

      Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

      Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

      modified on Monday, September 20, 2010 10:43 PM

      B 1 Reply Last reply
      0
      • L Luc Pattyn

        Hi Tom, 1. I studied the article High Speed, Feature Rich, and Easy-To-Use Graphs and Charts[^] a bit; it has a Plotter class which is a UserControl displaying a number of traces, each trace defined as a sequence of points. So there is no bitmap, no image, involved at all. It basically is vector graphics, which makes it high-performance. 2. I did some experiments with a bitmap-based plotter-like Panel, where one column of pixels would be updated at a time, using the circular buffer approach we discussed earlier. It is double-buffered to avoid flickering, and it works remarkably smooth, in fact it looks much better than the other article's demo, however I suspect that is due to the demo having fewer data points than the plotter is wide, causing the graph to jump all the time. At 512*512 my plotter happily handles 30 updates (frames) per second, using between 10 and 15% of a middle-of-the-road dual-core (I use both cores, as one executes the main thread, i.e. the painting, whereas the other calculates the new column in a BackgroundWorker). Yet I am convinced it takes too much CPU load for the net result. So I think I should look into other technologies, being OpenGL and DirectX. And I will, in time. 3. For the moment, I'm convinced if all that gets shown basically is a line graph, one should not resort to images at all. It will be much more lightweight when the graphs are drawn straight from a sequence of points, no matter what technology is used for painting images. ADDED Of course, when doing so, there is no problem whatsoever to make it scroll. /ADDED :)

        Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

        Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

        modified on Monday, September 20, 2010 10:43 PM

        B Offline
        B Offline
        bimbambumbum
        wrote on last edited by
        #9

        Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas

        modified on Tuesday, September 21, 2010 12:09 AM

        L 2 Replies Last reply
        0
        • B bimbambumbum

          Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas

          modified on Tuesday, September 21, 2010 12:09 AM

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #10

          bimbambumbum wrote:

          .do you have a way to get rid of this?

          yes. do it correctly. and no. there is no perfect solution, switching from one image to the next always takes a finite amount of time, it is a matter of getting it minimal. you will not see flickering when the repaint time is small compared to the steady time. I suggest you show the relevant code.

          bimbambumbum wrote:

          mapping from FFT log-outputs to a colorrange

          technically, have an array of colors, one for each integer in a limited range of possible values (if need be, divide by some constant so only say 256 colors remain). esthetically, whatever you like. You could create a hue-wig, i.e. all colors with same (probably max) saturation and brightness but different hue values. :)

          Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

          Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

          B 1 Reply Last reply
          0
          • B bimbambumbum

            Hi Luc Glad to hear you are in good progress. I will take a look at the article you are refering to. I've advanced a bit tonight myself because the spectrogram is now up running. I'm currently using drawImage() which seems to do well. My laptop (Lenovo T61p) uses about 20% of it's resources but the app also does FFTs 50 times a second and computes the log-spectrum of these FFT outputs which costs a little too so I'm pretty impressed with the GDI performance. Even though I running double-buffering I still think my 768x1024 pixel spectrogram does some flickering..do you have a way to get rid of this? Also, I need an efficient way of mapping from FFT log-outputs to a colorrange...you know of an easy way of doing this? Sorry for all the question...I have a lot to learn with .NET (and programming in general besides Matlab) Addition: I guess the flickering comes because I'm manipulating the bitmap directly and not bitmapdata. If that is the case maybe drawimage is not such a good option???? Thx Thomas

            modified on Tuesday, September 21, 2010 12:09 AM

            L Offline
            L Offline
            Luc Pattyn
            wrote on last edited by
            #11

            bimbambumbum wrote:

            I'm manipulating the bitmap directly

            so do I. The alternative with LockBits prevents multi-threading, i.e. you can no longer have concurrent image updates and image repaints by different threads. However, SetPixel() is a very slow method. My test does two DrawLine() to update one column (one full-height background, one foreground). :)

            Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

            Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

            B 1 Reply Last reply
            0
            • L Luc Pattyn

              bimbambumbum wrote:

              .do you have a way to get rid of this?

              yes. do it correctly. and no. there is no perfect solution, switching from one image to the next always takes a finite amount of time, it is a matter of getting it minimal. you will not see flickering when the repaint time is small compared to the steady time. I suggest you show the relevant code.

              bimbambumbum wrote:

              mapping from FFT log-outputs to a colorrange

              technically, have an array of colors, one for each integer in a limited range of possible values (if need be, divide by some constant so only say 256 colors remain). esthetically, whatever you like. You could create a hue-wig, i.e. all colors with same (probably max) saturation and brightness but different hue values. :)

              Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

              Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

              B Offline
              B Offline
              bimbambumbum
              wrote on last edited by
              #12

              Hi Luc Here is the core of my bitmap shifting. If you run it you can see that the shifting flickers a little and I would like to avoid that. Hope you have a simple way of getting rid of the flickering. Sorry for the messy code....Mind you I'm a newbie :)

              using System;
              using System.Collections.Generic;
              using System.ComponentModel;
              using System.Data;
              using System.Drawing;
              using System.Drawing.Drawing2D;
              using System.Drawing.Imaging;
              using System.Linq;
              using System.Text;
              using System.Windows.Forms;
              using System.Runtime.InteropServices;
              using fftwlib;

              namespace GDI_evaluation
              {
              public partial class Form1 : Form
              {
              Timer timer = new Timer();
              Graphics g;

                  int XREF = 50;
                  int YREF = 50;
              
                  // Bitmap
                  Bitmap myBitmap;
                  Random rnd;
                  int tIndx = 0;
              
                  public Form1()
                  {
                      InitializeComponent();
              
                      rnd = new Random();
              
                      this.SetStyle(
                     ControlStyles.UserPaint |
                     ControlStyles.AllPaintingInWmPaint |
                     ControlStyles.OptimizedDoubleBuffer, true);
              
                      myBitmap = new Bitmap(768, 512, PixelFormat.Format24bppRgb);
              
                      timer.Tick += new EventHandler(timer\_tick);
                      timer.Interval = 1000 / 25;      // opdater grafik 50 /sekund!!!
                      timer.Enabled = true;
                      timer.Start();
                  }
              
                  void timer\_tick(object sender, EventArgs e)
                  {
                      this.Refresh(); // Update GUI
                  }
              
                  private void Form1\_Paint(object sender, PaintEventArgs e)
                  {
                  }
              
                  protected override void OnPaint(PaintEventArgs pe)
                  {
                      g = pe.Graphics;
              
                      Rectangle block1 = new Rectangle(XREF + tIndx, YREF, myBitmap.Width - tIndx, myBitmap.Height);
                      g.DrawImage(myBitmap, block1, 0, 0, myBitmap.Width - tIndx, myBitmap.Height, GraphicsUnit.Pixel);
                      Rectangle block2 = new Rectangle(XREF, YREF, tIndx, myBitmap.Height);
                      g.DrawImage(myBitmap, block2, myBitmap.Width - tIndx, 0, tIndx, myBitmap.Height, GraphicsUnit.Pixel);
              
              
                      int tIndx2 = tIndx - 1;
                      if (tIndx2 < 0)
                          tIndx2 = myBitmap.Width - 1;
              
                      // Insert new FFT data
                      for (int ii = 0; ii < myBitmap.Height; ii++)
                          myBitmap.SetPixel(myBitmap.Width - 1 - tIndx2, ii, Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)));
              
                      if (++tIndx == myBitmap.Width)
              
              L 1 Reply Last reply
              0
              • B bimbambumbum

                Hi Luc Here is the core of my bitmap shifting. If you run it you can see that the shifting flickers a little and I would like to avoid that. Hope you have a simple way of getting rid of the flickering. Sorry for the messy code....Mind you I'm a newbie :)

                using System;
                using System.Collections.Generic;
                using System.ComponentModel;
                using System.Data;
                using System.Drawing;
                using System.Drawing.Drawing2D;
                using System.Drawing.Imaging;
                using System.Linq;
                using System.Text;
                using System.Windows.Forms;
                using System.Runtime.InteropServices;
                using fftwlib;

                namespace GDI_evaluation
                {
                public partial class Form1 : Form
                {
                Timer timer = new Timer();
                Graphics g;

                    int XREF = 50;
                    int YREF = 50;
                
                    // Bitmap
                    Bitmap myBitmap;
                    Random rnd;
                    int tIndx = 0;
                
                    public Form1()
                    {
                        InitializeComponent();
                
                        rnd = new Random();
                
                        this.SetStyle(
                       ControlStyles.UserPaint |
                       ControlStyles.AllPaintingInWmPaint |
                       ControlStyles.OptimizedDoubleBuffer, true);
                
                        myBitmap = new Bitmap(768, 512, PixelFormat.Format24bppRgb);
                
                        timer.Tick += new EventHandler(timer\_tick);
                        timer.Interval = 1000 / 25;      // opdater grafik 50 /sekund!!!
                        timer.Enabled = true;
                        timer.Start();
                    }
                
                    void timer\_tick(object sender, EventArgs e)
                    {
                        this.Refresh(); // Update GUI
                    }
                
                    private void Form1\_Paint(object sender, PaintEventArgs e)
                    {
                    }
                
                    protected override void OnPaint(PaintEventArgs pe)
                    {
                        g = pe.Graphics;
                
                        Rectangle block1 = new Rectangle(XREF + tIndx, YREF, myBitmap.Width - tIndx, myBitmap.Height);
                        g.DrawImage(myBitmap, block1, 0, 0, myBitmap.Width - tIndx, myBitmap.Height, GraphicsUnit.Pixel);
                        Rectangle block2 = new Rectangle(XREF, YREF, tIndx, myBitmap.Height);
                        g.DrawImage(myBitmap, block2, myBitmap.Width - tIndx, 0, tIndx, myBitmap.Height, GraphicsUnit.Pixel);
                
                
                        int tIndx2 = tIndx - 1;
                        if (tIndx2 < 0)
                            tIndx2 = myBitmap.Width - 1;
                
                        // Insert new FFT data
                        for (int ii = 0; ii < myBitmap.Height; ii++)
                            myBitmap.SetPixel(myBitmap.Width - 1 - tIndx2, ii, Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)));
                
                        if (++tIndx == myBitmap.Width)
                
                L Offline
                L Offline
                Luc Pattyn
                wrote on last edited by
                #13

                Hi, I did not run your code, I have some comments just by looking at it: 1. you paint onto your Form, I always avoid that. I prefer adding a Panel to the Form, then painting to the Panel. Make the Panel the exact size of the plot, and make the Panel double-buffered, not the entire Form. ADDED and only repaint (i.e. Invalidate or Refresh) the panel. /ADDED 2. I see you modify your data inside OnPaint; one should not do that, as OnPaint may be called by the system whenever it feels a need (e.g. when an unrelated window gets shown, then disappears, uncovering your window). 3. you should use a local variable to store the value of myBitmap.Height, and not call any unnecessary property inside a loop; it will typically not be optimized away! 4.

                bimbambumbum wrote:

                opdater grafik 50 /sekund!!!

                ? :)

                Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                modified on Tuesday, September 21, 2010 5:58 PM

                B 1 Reply Last reply
                0
                • L Luc Pattyn

                  Hi, I did not run your code, I have some comments just by looking at it: 1. you paint onto your Form, I always avoid that. I prefer adding a Panel to the Form, then painting to the Panel. Make the Panel the exact size of the plot, and make the Panel double-buffered, not the entire Form. ADDED and only repaint (i.e. Invalidate or Refresh) the panel. /ADDED 2. I see you modify your data inside OnPaint; one should not do that, as OnPaint may be called by the system whenever it feels a need (e.g. when an unrelated window gets shown, then disappears, uncovering your window). 3. you should use a local variable to store the value of myBitmap.Height, and not call any unnecessary property inside a loop; it will typically not be optimized away! 4.

                  bimbambumbum wrote:

                  opdater grafik 50 /sekund!!!

                  ? :)

                  Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                  Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                  modified on Tuesday, September 21, 2010 5:58 PM

                  B Offline
                  B Offline
                  bimbambumbum
                  wrote on last edited by
                  #14

                  Hi Luc Thanks for your comments. I get your points - I think :) Will try to follow your recipe when I have time tomorrow. If you have an example I can steal from that would be great... Thanks Thomas

                  1 Reply Last reply
                  0
                  • L Luc Pattyn

                    bimbambumbum wrote:

                    I'm manipulating the bitmap directly

                    so do I. The alternative with LockBits prevents multi-threading, i.e. you can no longer have concurrent image updates and image repaints by different threads. However, SetPixel() is a very slow method. My test does two DrawLine() to update one column (one full-height background, one foreground). :)

                    Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                    Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                    B Offline
                    B Offline
                    bimbambumbum
                    wrote on last edited by
                    #15

                    One last thing... Will my spectrogram shift more smoothly if I do it the panel way? Cheers Tom

                    L 1 Reply Last reply
                    0
                    • B bimbambumbum

                      One last thing... Will my spectrogram shift more smoothly if I do it the panel way? Cheers Tom

                      L Offline
                      L Offline
                      Luc Pattyn
                      wrote on last edited by
                      #16

                      My plotting Panel is perfectly smooth, however I can't compare, I never do it the Form way, as 1) it isn't really object-oriented 2) too many things probably are going on with Forms. :)

                      Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum

                      Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.

                      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