BR: fillAll doesn't fill all

It leaves glitches around the component. Example, I do this:

		g.setColour (juce::Colours::red);
		g.fillRect (getLocalBounds().expanded (1));
		g.fillAll (juce::Colours::darkgrey);

image

Then I see a red border on the component. This is a dumb example, but the actual issue is if the component is opaque then the border is random data.

Code: juce_bugs/MainComponent.h at master · FigBug/juce_bugs · GitHub

1 Like

You’re trying to paint outside the bounds of the Component

Rail

Yes but assuming the component’s drawing is clipped, it won’t be seen. Even calling fillAll() is technically “trying to paint outside the bounds of the component”.

The issue is that the second call, which should be filling the whole component with grey, still leaves a strip of red down the right.

2 Likes

Depends on if you have Component::setPaintingIsUnclipped() set to true or false

Rail

It’s set to false. We don’t use setPaintingisUnclipped anywhere in our code. This component is part of a scaled hierarchy. So the main component gets scaled to the window size. The whole UI gets bigger/smaller. This child component is setOpaque(true), and a fillAll should fill every pixel. But it doesn’t.

If it were unclipped, the grey fill would have filled the whole screen. Which is what I meant by fillAll() technically painting outside the bounds - it’s only safe to call fillAll() when the component’s painting is clipped otherwise you will end up painting outside the bounds.

1 Like

Does it happen on Windows and not on Mac?

I guess the problem is that fillAll is based on fillRect(Rectangle), which has a bug since ages, it paints based on pixel boundaries, even when used in scaled context. The behaviour is different on windows vs Mac.
Maybe it would be time to solve the problem and align the behaviour of the GraphicContexts to each other.

PS: workaround, use g.fillRect (getLocalBounds().toFloat());

4 Likes

It’s failing for me on macOS.

Is it always the right-hand edge that’s still red? Does it change if you move the component, and/or change its size?

It can be any side. In my demo app you can drag the box around and watch it change.

It also happens on Windows.

It’s not a bug, or anything specific to juce, it’s just an artefact of how anti-aliasing is done.

If your rectangle isn’t perfectly pixel-aligned (and you can’t really expect things to be pixel aligned very often in these days of odd scale factors and high-res displays), then its edges will be drawn as a semi-transparent line based on its coverage.

So if those pixels have e.g. 50% coverage, if you draw them first in red and then in grey, they’ll still have a red tinge showing through.

That’s just the nature of coverage-based AA systems. It also means that if you draw two rectangles whose edges line up perfectly on a non-pixel boundary, you’ll probably get a faint line between them.

The only way to get a perfect result would be to use a totally different approach. One would be to render the entire window with no anti-aliasing into a scaled-up canvas, then down-sample it to the display at the end. Or instead of filling shapes immediately, you’d queue them and simplify/merge them before actually doing any drawing.

4 Likes

Yes, you are right!

One possibility is to use coordinates that prevent such artifacts from occurring by explicitly overlapping the shapes.

But the fillRect oddity still remains, I will add something to the other thread :wink:

So “fillAll” is NOT meant to fill the entire component? Should the documentation be updated?

We can’t rely on “fillAll” to do the right thing? We have to use work arounds?

@jules with all due respect, I think you’re wrong here. This is not about some random fillRect function call. This is “fillAll” we’re talking about. It has to do the right thing and fill all pixels of that component. The fact that a workaround without artifacts exists proves you wrong. The work around does not fill outside the clip rect. It’s “fillAll” that stays too far inside.

JUCE itself is using it to fill components, the app background window, menus, etc. it has to be reliable and work without artifacts, no matter the scale.

2 Likes

Rather than fillAll being the problem could it not be the clipping that is the issue. i.e. the component is not being clipped to the clipBounds? or at least the rounding being used in fillAll and the clipping given the same bounds produces different results.

1 Like

I need to do more research, but I think the issue is that fillAll() anti aliases when it gets to the edges, but the rest of the painting system when it sees an opaque component thinks it doesn’t need to paint anything underneath. But this isn’t true when scaling is involved, so I’m seeing uninitialized memory get painted. I’ll need to update my test program to more like my actual app.

2 Likes

Yes, but the component doesn’t have integer bounds anymore, because it’s scaled. And when the boundary is between two pixels, there is a 50% alpha blend on the outside.
When you analyse the red line you see, its not the pure red, its a mix between the red and grey and and little black (background), and that is the intended behaviour of this rendering engine.

At least on Mac, on windows it might be pure red (with a little black), because of the other fillRect-bug (fillAll is based on fillRect), because on windows it uses physical pixels which is inconsistent, and the grey doesn’t alpha overlap.

Subtle drawing bug(?) -> fillRect (integer version) scaled - #7 by chkn

PS: but there might be a third factor, which is the clipping region, which seems to rely on integers only.

PS2: I checked the behaviour on windows, with 100% physical scaling, and I have no red lines at all.

  1. artefacts are normal
  2. windows fillAll works different (uses physical pixels boundaries)
  3. windows clipping works different then CoreGraphics clipping

Please stop repeating yourselves. We all fully understand what the problem is. Our issue is that the “fillAll” (all means everything) is NOT doing its job. I don’t care what the internal implementation does. I don’t care that it (right now) uses the integer clip-bounds instead of transforming them correctly.

It’s a bug. It should do it correctly. I can’t believe we are arguing about a reproducible bug. The name of the function is “fillAll”. Here is its description from the docs:

Fills the context's entire clip region with the current colour or brush.

I think we can all agree that is NOT what is happening. It’s buggy. Unreliable. Not doing what it says on the tin. It is, in fact, NOT filling the entire clip region. Evidently, if we force a larger rect, it actually fills the entire clip region and thus has no artifacts.

2 Likes

Here is a video of the actual issue: Screen Recording 2022 08 18 at 12 55 12 PM - YouTube

Look when I’m on the modulation page near the User LFO 1 depth. Then I switch to the arp. You can still see the depth slider poking through above the time bar, which is an opaque component. If this was just an antialiasing issue the background of the time bar would be blended with the background of the arp page. But instead, it’s getting antialiased with garbage data. If I’m on a different page before I switch to the arp, then the garbage data is in a different location. If I don’t make the components opaque, then the garbage data is gone. There is definitely an issue with fillAll() + setOpaque(true) + scaling.

1 Like

Please stop repeating yourselves.

Actually I really trying to analyse whats going on. I really putting effort into it. I compiled the example on mac and windows. I have different results on windows and mac. I really think my observation is part of the problem.

@RolandMR

thanks, for the example, it makes it much more understandable. Good luck!