Drawing just a position needle [solved]

Hello again. I have a waveform display, which I draw to an image once which is then scaled to the display. I thought it would be much faster than drawing the path every time. But it turns out that scaled images are really, really slow in Juce. I guess it’s all drawn in software, which is fair enough I guess.
Now I’ve added a moving needle to the display to show play position, and I want it to draw quickly.
My question is how do I just redraw JUST the needle’s position? Currently I’m repainting the whole waveform on a timer. Do I simply make a component needle? Or will it still do a entire waveform component redraw? I understand I’ll need to delete the old position - that’s not my question. :slight_smile:
Basically I need partial repaints. Will a separate needle component work?

Make a needle component. Also check out the JUCE audio thumbnail clasd

Having a child ‘Needle’ component triggers the whole parent to get a ‘paint’, when it moves, sometimes anyway, usually when the mouse moves near the area. Of course it does, all we have is a straight paint function.
I’m just going to go back to drawing a simple needle line after drawing the waveform image, a shame as it’s so slow at scaling the image.
There’s still a lot of audio people out there that use older computers.:man_shrugging:

In that case, just don’t scale the image :wink:

Create an Image of the size of the component and draw at the resize into the background. That you can draw 1:1 and draw your needle on top…

HTH

1 Like

Good idea, it will still render the whole thing as the user scales the window, but hopefully that won’t happen too much… :slight_smile:

I’ve done it. Wow just drawing with drawImageAt is just as slow!! :sweat_smile:
I am testing on a 4k screen, but still, who hasn’t got one of those? :grin:

*edit I see drawImageAt still calls the AffineMatrix internally.

Handling a paint request doesn’t necessarily mean you have to repaint the whole component. You can call getClipBounds() to see what part of the component that needs a repaint and just draw that part.

2 Likes

Brilliant, I didn’t know that function existed. So it will only return the area of my needle or any other child component that moves over the waveform Image?

Yeap. You shouldn’t normally need to paint anything outside that area.

In case anybody’s following this thread, in my waveform paint function I just did this:

    auto clip = g.getClipBounds();
g.drawImage(*waveImage, clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(),
					   clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight());

…It would have been handy to just use Rectangle, but hey.

This automatically wiped my old needle (child component) position out the way.
Finally! :slight_smile:

That does exactly the same thing as

drawImageAt (myImage, 0, 0);

because it’ll automatically get clipped anyway when it’s drawn.

The only purpose for using the clip region is if you need to e.g. optimise your code to avoid executing a slow drawing operation if it doesn’t lie within the clip region.

(Also, there’s never a reason to allocate an Image on the heap - you use it as a pass-by-value class, so having a pointer to an Image is probably a mistake).

…maybe something that’d help if you’re trying to optimise is JUCE_ENABLE_REPAINT_DEBUGGING

Thanks, I did squint a bit when I wrote that :slight_smile: I just need a new sized image every time someone resizes the waveform area. So it was a ScopedPointer that got nulled and remade every time.
So is this acceptable?:
visualiser = visualiser.rescaled(area.getWidth(), area.getHeight(), Graphics::ResamplingQuality::highResamplingQuality);

With this I get a large memory footprint when I change to full screen then small again. Does it free the image memory properly using this, or am I missing something?

Assuming “visualiser” is an Image? Yes, the class is just a reference to an underlying object which holds the data.

But don’t confuse the memory footprint that some high-level tool like activity manager shows you with what’s actually going on inside the application’s heap.

You may actually find it far easier to not deal with Image generation yourself, and use Component::setBufferedToImage, which would also make it easy to see whether using an Image is even the fastest way to render what you’re drawing (and in most cases it isn’t)

I’m using the Memory Profiler on Windows VS 2017. So I’m only slightly confused. :slight_smile: It turns out the memory usage increases anyway with resizing - not using any Image files. This is in Release. I only have 3 sliders showing. And a blank g.fillALL for the waveform. Oh well, I’m going to forget it for now. :smiley:

Well there’s a whole ton of stuff going on in the background to get those windows on the screen. If it ain’t actually leakin’, don’t fix it!

I have a question about that. How do you do this if the UI scale factor is not 1.0?

The easy part is getting a correctly sized image (eg. for a 200×200 component: at scale factor 1, use a 200×200 image, but at scale factor 2, use 400×400), but:

  • you have to line the image up with the physical pixel grid instead of the logical one, othwerwise your image will get blurred
  • you should avoid resampling the image, which I assume is the slow part, and which should be unnecessary.

So how do you guys do this in JUCE?

Like my suggestion earlier in the this thread: Don’t use your own image! Just use the Component::setBufferedToImage mechanism, which will take into account physical DPI scaling, regenerating the image when you call repaint, etc.

Yes but do you have direct access to that image? I know that usually you just draw paths directly and be done with it, but sometimes you have to render a bitmap directly, preferably at the physical resolution.

For instance, how do you change the Image Format demo so it displays images at scale factor 1:1 on the physical pixel grid, independent on DPI scaling, while still keeping the DPI scaling for the user interface?

Well, you can find out the DPI, there are methods in the Graphics and Component class to do that. But I’d expect that you’d very very rarely need to know it unless you genuinely are twiddling pixels directly for some reason.