0

I am working on making my application that reads data from the serial port and updates a gauge on the UI more efficient and I wanted to ask for some advice on my code that processes the UI changes. I have a timer set to check for data being sent to the COM port and another timer that updates the UI with the variable received from the COM port. Basically what is happening is I am rotating a gauge. Here is my code for handling the graphics...

void timer_Tick(object sender, EventArgs e) //Timer regulates how often the gauge is     updated on the UI
{
    if (pictureBox1.Image != null)
        pictureBox1.Image.Dispose(); // dispose old image (you might consider reusing it rather than making a new one each frame)

    Point test = new Point((int)_xCor, (int)_yCor);
    Image img = new Bitmap(400, 400); // The box tht contains the image <--- Play around with this more
    pictureBox1.Image = img; // Setting the img Image to the pictureBox class?


    Graphics g = Graphics.FromImage(pictureBox1.Image); // G represents a drawing surface
    Matrix mm1 = new Matrix();
    //
    mm1.RotateAt((float)(90 + (((12.5 * state) - 20.95) * 6)), new Point((int)_xrotate, (int)_yrotate), MatrixOrder.Append);
    GraphicsPath gp = new GraphicsPath();
    g.Transform = mm1; // transform the graphics object so the image is rotated
    g.DrawImage(imgpic, test); // if the image needs to be behind the path, draw it beforehand
    mm1.Dispose();// prevent possible memory leaks
    gp.Dispose();// prevent possible memory leaks
    g.Dispose(); // prevent possible memory leaks
    pictureBox1.Refresh();
}

I am wondering if there is a more efficient way that I can rotate the Image on screen. i feel like there has to be but I can't figure it out.

4
  • 1
    Can you be more specific about how you want it to be "more efficient"? For example, do you feel the timer callback is called when it isn't necessary (no change since the last callback), is the method too slow, using too much memory? Are you seeing some measurable performance problem? Commented Feb 5, 2013 at 14:48
  • It is more of a performance issue, the response is very slow when I try to run the application on my laptop. For example, the response to the sensor will appear 1-3 seconds after it should. Commented Feb 5, 2013 at 14:49
  • The title is asking about updating the UI using a timer, but you're actually having concerns about graphics efficiency? You're including a lot of extraneous details that just muddy the water. Commented Feb 5, 2013 at 14:50
  • I am mainly concerned if I am doing too much in order to rotate the image, if there is a simpler way to simply rotate an image on screen Commented Feb 5, 2013 at 14:56

4 Answers 4

2

This is the second time I provide a WPF solution for a winforms problem.

Just copy and paste my code in a file -> new project -> WPF Application and see the results for yourself.

Also take a look at how simple this code really is (I'm using random values, so you can remove that and adapt it to your needs).

The drawing I used (the <Path/> part in XAML) is not adequate for a Gauge. I just had that Path already drawn and I'm too lazy to create a new one. You should create a new drawing (I recommend using Expression Blend). But you can see the Rotation being applied and how fast it works.

using System;
using System.Threading;
using System.Windows;
using System.ComponentModel;

namespace WpfApplication4
{
    public partial class Window2
    {
        public Window2()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel: INotifyPropertyChanged
    {
        private double _value;
        public double Value
        {
            get { return _value; }
            set
            {
                _value = value;
                NotifyPropertyChange("Value");
            }
        }

        private int _speed = 100;
        public int Speed
        {
            get { return _speed; }
            set
            {
                _speed = value;
                NotifyPropertyChange("Speed");
                Timer.Change(0, value);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private System.Threading.Timer Timer;

        public ViewModel()
        {
            Rnd = new Random();
            Timer = new Timer(x => Timer_Tick(), null, 0, Speed);
        }

        private void Timer_Tick()
        {
            Application.Current.Dispatcher.BeginInvoke((Action) (NewValue));
        }

        private Random Rnd;
        private void NewValue()
        {
            Value = Value + (Rnd.Next(20) - 10);
        }
    }
}

XAML:

<Window x:Class="WpfApplication4.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" WindowState="Maximized">
    <DockPanel>
        <StackPanel DockPanel.Dock="Top">
            <TextBlock Text="Delay (MS):" Margin="2"/>
            <Slider Width="200" Minimum="100" SmallChange="1" LargeChange="10" Maximum="1500" Value="{Binding Speed}" Margin="2"/>
            <TextBlock Text="Current Value:" Margin="2"/>
            <TextBox Text="{Binding Value}" Margin="2"/>
        </StackPanel>

        <Path Data="M0.95991516,0.5 L73.257382,1.866724 90.763535,1.866724 90.763535,90.822725 66.430534,90.822725 66.430534,26.075016 0.5,24.828653 z" Fill="#FF506077" RenderTransformOrigin="0.861209625003783,0.507482926584064" Stretch="Fill" Stroke="Black">
            <Path.LayoutTransform>
                <TransformGroup>
                    <ScaleTransform ScaleY="1" ScaleX="-1"/>
                    <SkewTransform AngleY="0" AngleX="0"/>
                    <RotateTransform Angle="{Binding Value}" x:Name="Rotation"/>
                    <TranslateTransform/>
                </TransformGroup>
            </Path.LayoutTransform>
        </Path>
    </DockPanel>
</Window>
Sign up to request clarification or add additional context in comments.

Comments

1

It's difficult to answer your question because your asking for "more efficient" rotation of the image is pretty vague. I'm not sure if by more efficient you mean:

  • better performance;
  • less memory usage;
  • or simply less, or more elegant, code

In any case, unless you are talking about making the code more "elegant" than the only thing that I can come up with is that you could, and probably should, re-use the same image/bitmap. Instead of creating a new one each time you could just clear the one you are using and re-draw your image.

You might also want to check the refresh rate of your timer that is used to update the UI. A frame rate of about 24 - 30 fps should be enough. Anything more is overkill in this scenario and it will mostly just waste CPU cycles.

You should also enable double buffering to prevent flickering.

EDIT

Based on your comments, it sounds like the problem is not performance but a discrepancy between the interval of the COM port timer and the UI timer. It sounds the timer that updates the UI doesn't run fast enough to detect a change.. What are your intervals?

8 Comments

As a side comment, if you want Better Performance, Less memory usage, and/or simply less, or more elegant, code drop winforms altogether and use WPF.
@HighCore - I don't necessarily agree with you. WPF is great for a lot of reasons but it doesn't implicitly provide any of the above. It certainly does not use less memory. It doesn't always give better performance. And lastly, as far as elegance of code - that depends a lot on the skill of the programmer.. You can write beautiful code in and language, and using any framework (the reverse is also true)!
Really? look at this
@MikeDinescu Did you read the comment I linked to? Just an aside comment: winforms sucks and is slow as hell. It doesn't provide a serious binding framework and doesn't allow you to customize anything except by overrinding OnRender() and doing a hell lot of code. Its a dinasour, get over it.
Yes - really. I've used both for various applications. I just can't agree with blanket statements like that. Plus - you're not really solving the problem of the poster.
|
1

Looks like you're doing this in Windows Forms? Use:

Graphics.RotateTransform

If I may humbly suggest, though, if you're trying to do anything even remotely interesting graphically, it may be worth the investment to step up to WPF. Windows Forms relies on the old GDI apis which are not hardware accelerated (unlike WPF which is built on DirectX), making it a poor platform for any kind of serious graphics. No matter how 'efficient' you get with winforms, you'll never be able to compete with anything that's backed by hardware acceleration.

4 Comments

I will disagree with you. WPF is pretty good about most of the graphics that might happen in a normal UI but if you're doing anything serious WPF will get in your way because even though it is based on DirectX it's not direct more, but rather intermediate mode DX. If you need really fast drawing that you can drop WPF and just go C++/Direct2D
This is absolutely correct. WPF is intended to be for developing user interfaces, not games. But even on this front it is still miles ahead of Winforms.
In many respects yes. It is a better framework. But the irony is that if you have a UI where you need to do a lot of custom drawing - albeit rare - you will find yourself fighting with WPF pretty soon. Here's an example: real-time graphing of a signal..
In many respects.. hahaha.. can you mention a single thing where winforms has an advantage... please...
0

Rotating a bitmap using GDI+ is going to be slow, period. The biggest performance increase you could probably make is to stop using a bitmap for this purpose and just custom draw your gauge with GDI+ vector graphics. You could still use a bitmap for the background and use vector graphics for drawing the gauge's needle, if applicable. That would be orders of magnitude faster than rotating a bitmap.

The next thing I'd look it is whether using a picture box in conjunction with a dynamic bitmap (i.e. constantly changing) is really the right way to go; the picture box might be doing extra processing on your bitmap every time it's updated, which is really just wasted cycles. Why not just draw the bitmap onto the screen yourself? Also, make sure that your bitmap is created with the correct pixel format for optimal drawing performance (PArgb32bpp).

Finally, unless your input data is a constant stream of changing values, I would consider ditching the timer entirely and just using BeginInvoke to signal your UI thread when it's time to redraw the screen. Your current solution might be suffering from unnecessary latency between timer ticks, and it might also be redrawing the gauge more often than is necessary.

2 Comments

I don't understand how else to do it is the problem. I have been working on this for a while now and I have a really hard time understanding the Graphics libraries. How can I alter my code so that it dosen't use a picturebox with a dynamic bitmap
It sounds like you need to work through a couple of tutorials on custom controls (not to be confused with user controls). Graphics methods in GDI+ are quite simple compared to some other frameworks; you don't need to worry about vertex buffers or selecting brushes, etc... For your particular case you'd probably just need the DrawImage method and maybe DrawLine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.