Bug 274685

Summary: CSS transform: matrix/translate leave aliased gridlines google map using whole pixel transform
Product: WebKit Reporter: Reese Jones <reesedrjones>
Component: CompositingAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: karlcow, nicole, simon.fraser, webkit-bug-importer, zalan
Priority: P2 Keywords: InRadar
Version: Safari 17   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
screenshot of issue plus devtools
none
Screen shot of scaling issue in safari none

Description Reese Jones 2024-05-24 14:29:01 PDT
Created attachment 471506 [details]
screenshot of issue plus devtools

Hardware: pc and mac
OS
browsers: safari, chrome (not firefox)

Issue:
Google maps renders image tiles and moves them across the screen with css transform translate and matrix translations (no scaling). Whole value pixel numbers are always used. However when browser zoom is not a whole number (120% or 80%) these tiles are rendered with white lines between them even though they should be perfectly aligned. The monitor scale aslo factors into this. I dont know what the setting is for mac, but windows it can be accessed through Settings -> System -> Display -> Scale and Layout. On windows the browser zoom and the monitor scale affect the window.devicePixelRatio. On safari the window.devicePixelRatio typically only shows as 2, but these two settings cause the same issue regardless of what the window.devicePixelRatio says.

You can see this white gridline issue reproed at this link: https://jsfiddle.net/reesedrjones/ngcxkL1v/5/

My screenshot is of chrome on windows, but the same happens on mac in safari.

Change browser zoom such that the total scaling is not a whole number (scale + browser zoom != 100% | 200% | 300% etc...)
Pan the map. Observe white grid lines appear at specific intervals of translation.

Similar to https://g-issues.chromium.org/issues/40084005 But this issue describes only for non integer numbers, and we have this issue for whole numbers. And its happening on safari as well.

The code in Maps JS API ensures all transforms use whole numbers for matrices, translations and scales by rounding values before passing them to css properties. However white gridlines still appear between tiles.

This happens in Chrome, and Safari, but not in Firefox across desktop platforms (unable to repro on mobile platforms). Seems to stem from whenever the window.devicePixelRatio is not a whole number (except on safari, where the WDPR does not show zoom+scale, but its still an issue). This can be from a combination of monitor dpi scaling (eg large format displays often render at 125%, 150% etc..) AND browser zoom levels (80%, 110%, 175%). When ever the combination of these two values adds to a non-whole number window.devicePixelRatio transforms and translations can cause white alias gridline artifacts to appear.

For example if we have a tile transform translation(-83, 120) no gridlines are shown as long as window.devicePixelRatio is a natural number. At a WDPR of150%, transform translate looks like this: translation(-124.5, 180). And the white gridlines appear. I cant round this number because I never actually see it in the browser (we are using translate(-83, 120) or something similar) and this offset comes from behind the scenes from the browser renderer and platform monitor scaling and browser zooms.

This happens at periodic intervals based on how far from a whole number scaling value is. On a device with a monitor scaling of 100% I noticed the tiles line up (as in, no gridlines appear, and tiles are rendered correctly) at these intervals.

zoom 25% 4 pixels
zoom 33% 3 pixels
zoom 50% 2 pixels
zoom 66% 3 pixels
zoom 75% 4 pixels
zoom 80% 5 pixels
zoom 90% 10 pixels
zoom 100% 1 pixels
zoom 110% 10 pixels
zoom 125% 4 pixels
zoom 150% 2 pixels
zoom 175% 4 pixels
zoom 200% 1 pixels

I found out I could calculate this offset and make sure tiles always rendered at one of these steps to make sure tiles were always rendered at perfect intervals with no gridlines. However depending on the browser zoom and monitor scaling, this perfect interval could be between 1-10 pixels which creates a terrible panning experience.

1/ Math.abs(Math.round(window.devicePixelRatio) - window.devicePixelRatio) = pixel perfect multiple

This solution also doesn't work cross platform, the window.devicePixelRatio on safari typically appears as 2 regardless of monitor scaling.

What I would like is for our transforms using natural numbers with non whole number window.devicePixelRatios to line up to device pixels. Not sure what to do because I cannot round our values any more than they already are (apart from not using css transforms for moving our tiles, in fact if we went back to using top and left to position our tiles instead of transform our problem goes away. However it moved to this way to solve performance problems.)

Additional Comments
Seems like there is a significant difference between how rounding and scaling is done between chrome/safari and Firefox. For instance: https://codepen.io/reesedrjones/pen/poBjJNp This renders two div squares on top of each other. One at 100px size, the other at 1px size scaled to 100%. On Firefox these are always the same size, but on chrome and safari it would appear that the div with 1px size is scaled by window.devicePixelRatio first, then rounded to nearest whole number, then scaled by css transform. This can make its size vary widely based on zoom and monitor scale. 1px * (1.25 WDPR) = 1.25, but then rounds down to 1, then scaled by 100 results in 100px wide square, while the other will appear 125px on your monitor.

This might be a separate bug actually?
Comment 1 Reese Jones 2024-05-24 14:30:33 PDT
Created attachment 471507 [details]
Screen shot of scaling issue in safari

I wanted to include this screenshot of weird scaling behavior in safari when browser zoom and device scaling are not a whole number
Comment 2 Radar WebKit Bug Importer 2024-05-31 14:29:14 PDT
<rdar://problem/129081534>