Tips for Reducing CPU / GPU load in Algorithm (Graphics)


#1

So I’m working on a small experimental application for my coding portfolio for school. It’s called “Pixel Tape Measure” and it does exactly what it says. You click, drag, and a green line is produced, along with a numerical output for the pixel distance. The idea is to have a small tool to quickly and “dirtily” measure distances in graphical designs.

Drawing lines/graphics, in real-time, is totally new to me and outside of my scope of knowledge, so my implementation is terrible and not resourceful at all. The GUI is clear, you simply place it on top of another item and measure. It works well when the GUI is relatively small, but when you resize the GUI, it becomes laggy, even with a decent CPU/GPU combo.

Here’s my mouseDown algorithm:

void Interface::mouseDrag (const MouseEvent& e)
{
    //[UserCode_mouseDrag] -- Add your code here...

    passPixelDistanceToLabel(e.getDistanceFromDragStart());

    // Create a new clear image
    Image graphicToDrawLineOn(Image::ARGB, getWidth(), getHeight(), true);

    // Load into an ImageComponent
    imageComponentforClearImage.setImage(graphicToDrawLineOn);
    imageComponentforClearImage.setBounds(0, 0, getWidth(), getHeight());
    addAndMakeVisible(imageComponentforClearImage);

    // Code to draw line that has circle at start point
    Graphics tapeMeasureLine(imageComponentforClearImage.getImage());
    tapeMeasureLine.setColour(Colours::green);
    tapeMeasureLine.drawLine(e.getMouseDownX(), e.getMouseDownY(), e.x, e.y);

    Rectangle<float> boundaryForStartingCircle(e.getMouseDownX() - 4, e.getMouseDownY() - 4, 8, 8);
    tapeMeasureLine.fillEllipse(boundaryForStartingCircle);

    //[/UserCode_mouseDrag]
}

The idea is to create, and destroy, a new clear image to draw a line on, every time the mouseDown function is called. For obvious reasons, it’s pretty inefficient. Any ideas to make a better algorithm that is less taxing on the system? Or maybe some JUCE code that does something similar that I missed? Thanks.


#2

RIght now, you are creating all your objects/components anew with every little mouse move. It would certainly help to make your image and the imageComponent a class member, and do the image creation, setting the image for the component and adding the component in mouseDown(…) instead of mouseDrag(…).


#3

Currently, the ImageComponent is a private data member of the class. The only object being created and destroyed is the Image, which is just a clear image.

The idea of the application is, the line is continuously redraw a single line as the mouse moves. If I only created the image once during mouseDown(), the line would be drawn, and displayed, multiple times on the same image, correct?


#4

Why are you using an Image at all?


#5

I thought the graphics class needed an image for the graphic to be drawn on?

Graphics (const Image &imageToDrawOnto)


#6

Image for reference

Uploading…

Hmm, strange… photos are not uploading.


#7

Well, it’d make more sense to say if you want to draw into an Image then you need a Graphics object.

But in practice you very rarely need to draw into an Image. If you look at any of the vast amount of example code, you’ll see copious use of Component::paint (Graphics&) methods that are how almost everything gets drawn.


#8

That’s how you would normally do it:

void MainContentComponent::paint (Graphics& g)
{
    g.fillAll(Colours::white);
    g.setColour(Colours::black);
    g.drawLine(downPos.x, downPos.y, currentPos.x, currentPos.y,3);
}


void MainContentComponent::mouseDrag(const juce::MouseEvent &e)
{
    currentPos = e.getPosition();
    downPos = e.getMouseDownPosition();
    repaint();
}

where currentPos and downPos would be class members (Point).


#9

Wow, I never even considered using the start and end points as class members then using the paint() method. Thanks a ton. 100 percent working. Seems to be much more efficient.

void Interface::paint (Graphics& g)
{
    g.setColour(Colours::green);
    
    // Create dot to indicate beginning of the line
    Rectangle<float> boundaryForStartingCircle(startingCoordinate.getX() - 4,
                                               startingCoordinate.getY() - 4, 8, 8);
    g.fillEllipse(boundaryForStartingCircle);
    
    // Draw line
    const int lineThickness = 3;
    g.drawLine(startingCoordinate.getX(), startingCoordinate.getY(),
               currentCoordinate.getX(), currentCoordinate.getY(), lineThickness);
}

void Interface::mouseDown (const MouseEvent& e)
{
    startingCoordinate.setXY(e.getMouseDownX(), e.getMouseDownY());
}

void Interface::mouseDrag (const MouseEvent& e)
{
    passPixelDistanceToLabel(e.getDistanceFromDragStart());
    currentCoordinate.setXY(e.x, e.y);
    repaint();
}

void Interface::mouseUp (const MouseEvent& e)
{
    passPixelDistanceToLabel(0);
    
    // Remove line by giving it length of zero
    startingCoordinate.setXY(0, 0);
    currentCoordinate.setXY(0, 0);
    repaint();
}