0

I got a for loop which has to run 200,000 times calling graphics.DrawLine. This is very slow and I look forward for your help in increasing the performance of this loop. when I tried, Task Parallellism, it will give the error"The calling thread must be STA, because many UI components require this." I have also tried splitting the loop into two and execute them parallel, that also results in the same error. I have removed the code related to that attempts and have given the actual code which has to be optimized for performance.

The code is

// Take data from arraylist which is the data points for the graph

    Graphics g = null;
    public Drawing(Graphics _g, DrawingData _Data)
    {
        g = _g;
        drawingData = _Data;           
    }
    private void FrameDrawLine1()
    {
        DataValueSet pds = null;
        DataValueSet ds = null;

        for (int i = 1; i < drawingData.data.Count; i++) // the count will be 100,000
        {               
            ds = (DataValueSet)drawingData.data[i];                           
            PlotColumne(pds, ds);
        }                    
    }

// Get the color and other informtion about the graph parameters

    private void PlotColumne(DataValueSet pds, DataValueSet ds)
    {
        try
        {
            double totalSeconds = (drawingData.valueArea.maxDate - drawingData.valueArea.minDate).TotalSeconds;
            double xFrom = GetTimePixel(pds.time, totalSeconds);
            double xTo = GetTimePixel(ds.time, totalSeconds);
            PlotprintingDataset[] pt = initialisePlotprintingDataset();

            _referenceColumnindex++;
            PlotColumn column = null;
            for (int i = 0; i < drawingData.plotColumns.Count; i++) // this count will be around 50
            {
                column = (PlotColumn)drawingData.plotColumns[i];
                if (column.active)
                {
                    if (ValueIsNumber(Convert.ToDouble(ds.values[column.index]), Convert.ToDouble(pds.values[column.index])))
                    {
                        if (pt[column.index].X < -10000F)
                        {
                            pt[column.index].X = xFrom;
                            pt[column.index].Y = calculateYValueInGraph(column, Convert.ToDouble(pds.values[column.index]));
                        }

                        if (DrawLineInGraph(pds, ds,  column, pt))
                        {
                            pt[column.index].X = xTo;
                            pt[column.index].Y = calculateYValueInGraph(column, Convert.ToDouble(pds.values[column.index]));
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            GenericFunctions.ErrorMessage("Error has occured while plotting graph columns. Please try again.");
        }
    }

// Check if the point is in graph area and will call draw line method

    private bool DrawLineInGraph(DataValueSet pds, DataValueSet ds, PlotColumn column, PlotprintingDataset[] pt)
    {
        double totalSeconds = (drawingData.valueArea.maxDate - drawingData.valueArea.minDate).TotalSeconds;
        double xTo = GetTimePixel(ds.time, totalSeconds);
        double value = Convert.ToDouble(ds.values[column.index]);
        double y2 = calculateYValueInGraph(column, Convert.ToDouble(ds.values[column.index]));
        double y1 = calculateYValueInGraph(column, Convert.ToDouble(pds.values[column.index]));

        if (IsPointInPlausibleArea(pt, column, pds, ds))
        {
               drawLine(column.color, pt[column.index].X, xTo, pt[column.index].Y, y2, column.interpolate, column.lineWidth);

            return true;
        }
        else
        {
            return false;
        }
    }

// Draw the lines in the graph

    private void drawLine(Color color, double xFrom, double xTo, double yFrom, double yTo,
        bool interpolate, int lineWidth)
    {
        try
        {
            System.Windows.Shapes.Line newline = new System.Windows.Shapes.Line();
            Pen linePen = new Pen(new SolidBrush(color), lineWidth);
            float x1 = (float)xFrom;
            float x2 = (float)xTo;
            float y1 = (float)yFrom;
            float y2 = (float)yTo;

            if (interpolate)
            {
                g.DrawLine(linePen, x1, y1, x2, y2);
            }
            else
            {
                g.DrawLine(linePen, x1, y1, x2, y1);
                g.DrawLine(linePen, x2, y1, x2, y2);
            }
        }
        catch (Exception ex)
        {
            GenericFunctions.ErrorMessage("Error has occured while drawing graph lines. Please try again.");
        }
    }
ArunVijai
  • 11
  • 4
  • 1
    Umm, you just start using `g` in `drawLine` where did it come from? – Scott Chamberlain Dec 01 '14 at 07:08
  • 3
    Don't compute when you are supposed to be drawing. Just do the computations once when the data they depend on changes, and then just draw a bunch of lines after that, based on the computations. You're not going to be able to parallelize the rendering part, because of the exception you ran into: if you're drawing to the screen, you have to do that drawing in the one UI thread. Even if you drew into an off-screen bitmap, the synchronization between the threads doing the drawing would be a big bottleneck. – Peter Duniho Dec 01 '14 at 07:13

1 Answers1

0

You should try to draw to an image. To Do this you need to create your own Graphics Context from a Bitmap.

Bitmap yourBitmap = new Bitmap(your controls width, height, PixelFormat. Format32bppArgb)
GRAPHICS G = Graphics.FromImage(yourBitmap)
// Draw your lines here

finally you have to draw the picture with your original Graphics context to the control. e.Graphics.DrawImageUnscaled(0,0, yourBitmap)

Additionally you can Tweak the parameters of the graphics Context. They have also a big influence on Performance.

Here a link to those parameters : https://stackoverflow.com/a/38262796/6439999

Community
  • 1
  • 1
Thomas Voß
  • 1,145
  • 8
  • 20