-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ui-spacetimechart: fixes blurry graduations #914
base: dev
Are you sure you want to change the base?
Conversation
const centerOffset = (Math.ceil(lineWidth) % 2) / 2; | ||
return Math.round(rawCoordinate - centerOffset) + centerOffset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took me a bit to double-check this formula, it seems to work perfectly fine on LoDPI screens :)
I'm not super sure about HiDPI though. On HiDPI screens, the numbers will be implicitly multiplied by the scale before drawing on screen. Ideally, on a devicePixelRatio = 2
setup, a 0.5 large line at rawCoordinate 0 would be painted at coordinate 0.25, right? Also, on a HiDPI screen it's not necessary to align a 2px large line between two pixels: painting at x = 0.5 will result in a crisp image, I think?
Should we read window.devicePixelRatio
here, and change our behavior based on that number? (Mainly how rounding is performed)
Or should we give up and passthrough any number which isn't an integer? Or do you see another solution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good point indeed... Let me check that again :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I ran some tests on my 3.5 DPR Android phone, and:
- I couldn't get pixel perfect lines (when I check my screen captures zoomed on GIMP at least)
- They appear very acceptable on screen to me, I wouldn't have guessed it wasn't pixel perfect
- The resolution is such that I couldn't see any difference from afar, in all the tests I ran
To give a bit more context, the solution that made the most sense to me was to update the formula as:
export function getCrispLineCoordinate(rawCoordinate: number, lineWidth: number): number {
const centerOffset = (Math.ceil(lineWidth * devicePixelRatio) % 2) / 2;
return Math.round(rawCoordinate - centerOffset) + centerOffset;
}
Then, for a 2px wide line, on my phone, it would be 7px wide effectively, and thus with a centerOffset
of 0.5. I checked with console.log
, and that's what I got. And here is how it was rendered:
Zoomed in, it's clearly not crisp, and it's not even symmetric. But on my phone, with the actual screen, it looks very crisp. So, at that point, I really don't know how to do better 😅 Also, it looks like that last formula breaks with lower DPIs.
I'd say we go with this for now, at least if we agree that the lines are crisp enough on high DPIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've written a bunch of tests to more easily play with the code and ensure it does what I'd expect:
describe('getCrispLineCoordinate', () => {
it.each([1, 3, 99])('should align a %ipx line on a LoDPI device', (lineWidth) => {
const devicePixelRatio = 1;
expect(getCrispLineCoordinate(0, lineWidth, devicePixelRatio)).toEqual(0.5);
expect(getCrispLineCoordinate(0.1, lineWidth, devicePixelRatio)).toEqual(0.5);
expect(getCrispLineCoordinate(0.5, lineWidth, devicePixelRatio)).toEqual(0.5);
expect(getCrispLineCoordinate(0.9, lineWidth, devicePixelRatio)).toEqual(0.5);
expect(getCrispLineCoordinate(1, lineWidth, devicePixelRatio)).toEqual(1.5);
expect(getCrispLineCoordinate(42, lineWidth, devicePixelRatio)).toEqual(42.5);
expect(getCrispLineCoordinate(-0.4, lineWidth, devicePixelRatio)).toEqual(-0.5);
expect(getCrispLineCoordinate(-0.7, lineWidth, devicePixelRatio)).toEqual(-0.5);
});
it.each([2, 4, 64])('should align a 2px line on a LoDPI device', (lineWidth) => {
const devicePixelRatio = 1;
expect(getCrispLineCoordinate(-0.4, lineWidth, devicePixelRatio)).toEqual(0);
expect(getCrispLineCoordinate(0, lineWidth, devicePixelRatio)).toEqual(0);
expect(getCrispLineCoordinate(0.4, lineWidth, devicePixelRatio)).toEqual(0);
expect(getCrispLineCoordinate(0.5, lineWidth, devicePixelRatio)).toEqual(1);
expect(getCrispLineCoordinate(1.5, lineWidth, devicePixelRatio)).toEqual(2);
expect(getCrispLineCoordinate(42, lineWidth, devicePixelRatio)).toEqual(42);
expect(getCrispLineCoordinate(-0.7, lineWidth, devicePixelRatio)).toEqual(-1);
});
it('should align a 2px line on a HiDPI device', () => {
const devicePixelRatio = 2;
const lineWidth = 2;
expect(getCrispLineCoordinate(0.5, lineWidth, devicePixelRatio)).toEqual(0.5);
});
it('should align a 0.5px line on a HiDPI device', () => {
const devicePixelRatio = 2;
const lineWidth = 0.5;
expect(getCrispLineCoordinate(0, lineWidth, devicePixelRatio)).toEqual(0.25);
});
});
I've simplified your code to the following:
export function getCrispLineCoordinate(rawCoordinate: number, lineWidth: number): number {
const centerOffset = lineWidth / 2;
return Math.round(rawCoordinate - centerOffset) + centerOffset;
}
and then I've extended it to handle HiDPI:
export function getCrispLineCoordinate(
rawCoordinate: number,
lineWidth: number,
devicePixelRatio = window.devicePixelRatio || 1
): number {
const centerOffset = lineWidth / 2;
return (
Math.round((rawCoordinate - centerOffset) * devicePixelRatio) / devicePixelRatio + centerOffset
);
}
Do you think this is a version we could use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it, and it looks good to me. I pushed your modifications to the PR.
This commit fixes OpenRailAssociation/osrd#10662. It ensures vertical and horizontal graduation lines are as thin as possible, so that they are as crisp as they can be. Signed-off-by: Alexis Jacomy <[email protected]> Co-authored-by: Simon Ser <[email protected]>
226738e
to
03e3760
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
This commit fixes OpenRailAssociation/osrd#10662.
It ensures vertical and horizontal graduation lines are as thin as possible, so that they are as crisp as they can be.