Issue with pixel-accurate fillRect


#1

Hi

I’m facing an issue with how rectangles are filled on Android devices.
Basically I’m drawing a grid of filled rectangles, separated by 1 pixel gaps.
What happens is that, depending on the bounds sizes, the 1 pixel gaps quite often disappear.

Here a video of the issue, showing the difference between the MacOS and the Android version.

Here you can find a minimal example that reproduces the issue:

The key drawing code is in the RectanglesMatrix class paint() method, here.

To see the effect that subtle Component bounds changes have, they are updated from the MainComponent class,
here.

The desktop version (Mac OS in the case of the repo) does not present this issue.

Here some screenshots:

GalaxyTabA (2016)

Here a screenshot from Samsung S7:

an interesting detail: the gaps never disappear on the S7, but their sizes change irregularly, apparently fluctuating between 1 and 2 pixels (never zero).

Since the drawing mathematics is purely integer-based, I suspect that there is some issue related to how Android reports screen size and / or how Juce handles the pixel coordinates transformations from “logical” pixel resolution to the actually lit “physical” pixel.

@fabian / @timur / anyone else: is there any way to fix this issue?

Regards!


#2

If under android juce uses the same graphic software-render routines like windows, than this is maybe related to this Subtle drawing bug(?) -> fillRect (integer version) scaled


#3

if there is scaling involved, the integer version of fillRect still aligns to pixel, which is inconsistent, to other platforms like mac


#4

Hi! :slightly_smiling_face: any insights @fabian / @timur ?

Regards


#5

Just to confirm that this is a scaling bug, can you change line juce_android_Windowing.cpp:942 to

d.scale = 1.0;

?

Thanks!


#6

Hi @fabian,

thanks for suggesting the test, it indeed looks like it fixes the missing pixels issue!

See the below new screenshots.

Samsung Galaxy Tab A (2016)

Samsung S7

The only thing now is that our application has become “smaller” (like when you disable DPI scaling on Windows and everything becomes really crisp, but also really small).

Thanks again for your very effective collaboration


#7

To elaborate on our current situation: as stated in the previous post, the only issue now is that our UI elements have become smaller / much smaller.
On the Samsung S7 for example, given its high pixel density, it’s really tiny :smile:

What would you suggest we do for the time being?

  1. Shall we use for now JUCE with the modified line (we already have a private fork with some modifications we needed), and compensate in our code to have a density-independent UI?
  2. Does it make sense and is it architecturally / logically possible to offer low-level pixel-accurate access in JUCE? Could it be a (Component-level?) flag?

It has to be noted that on iOS we don’t have this issue on any of our devices (iPad mini, iPad air, iPad Pro, iPhone6 / 7).

Regards


#8

Two tips

  • Don‘t rely on physical pixels
  • Don‘t use the int version of fillRect (its inconsistent to other platforms), use float version instead.

#9

Yes @chkn is right, this is not really a JUCE bug. However, due to the way we calculate the scaling, it will mostly be a non-integral number on Android. Would it make sense for JUCE to maybe always round it to the next integer? For example:

d.scale = rountToInt (masterScale * (d.dpi / 150.));

#10

maybe always round it to the next integer?

IMHO no, than things may be too small or too big.

BTW, the special behavior of the int-version of fillRect should be somehow clear in the api, it produces different results either with Software Renderer or CoreGraphics (look at the other thread) (I would call it a bug)


#11

No, as pointed out by chkn, the interface may become a little too small and that’s not such a big issue, but it could also grow a little and some important UI elements may end up falling outside the screen, making them unreachable.

If you decide for a rounding, I think it would therefore make sense to do it always in the direction of making the UI a little smaller than the screen rather than bigger.


#12

The pixels may even have not a squared geometry, not sure if rounding makes sense. (Pentile Matrix)

Also modern display have a very high Pixel densisty.

If you round down 1.9 this a huge change.


#13

Hi :slight_smile:
I just wanted to let you know l that the “issue” also affects iOS devices.
No vertical/horizontal pixel lines disappear due to the displays being high-resolution (like on the Samsung S7), however the gaps have irregular sizes.

It looks to me that if pixel-accurate rendering is wanted, the most straightforward way is that of setting d.scale = 1.0 here:

as suggested by @fabian, and then explicitly deal with resolution-independent scaling. I’m not sure there’s a way of using this scaling only on selected Components. It looks like it might not be a simple endeavour.

For the time being we’ll just increase the pixels-gap to 2 on Android devices, since they tend to have a lower pixel density, then maybe switch to using an image to delegate downsampling to the underlying renderer (while OpenGL might use mipmaps, I’m not sure how CoreGraphics would fare.)

Regards


#14

As mentioned above, this is not a JUCE bug. This is a problem with your app using integer geometry where the scale factor is non-integer. You need to change your drawing routines to use the floating point equivalent functions.