Closed Bug 1258112 Opened 10 years ago Closed 6 days ago

Firefox's border pixel-snapping behavior causes thin borders to disappear entirely when scaled down (e.g. via CSS Transform or print-preview)

Categories

(Core :: Graphics: WebRender, defect)

44 Branch
defect

Tracking

()

RESOLVED FIXED
154 Branch
Tracking Status
firefox154 --- fixed

People

(Reporter: hildobijl, Assigned: gw)

References

(Blocks 2 open bugs)

Details

(Keywords: reproducible, testcase, webcompat:platform-bug)

User Story

webcompat:blocked-resources
user-impact-score:15

Attachments

(5 files)

User Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36 Steps to reproduce: Here's a JSFiddle as an example. https://jsfiddle.net/mxoa9s60/ In other words: make a div with a CSS scale property. Put inside of it a div with a (thin) border. Then watch how this border is affected by the scale. Actual results: The borders on the four sides are of different width. And this width is very unpredictable. Sometimes a border disappears altogether for low scales. But if you make the scale even lower, the border reappears and a border on some other side disappears. It's crazy. Expected results: The borders should all remain the same width. (And remain visible.) To be precise, when a border of "0.8px" is scaled by a factor "0.5", the resulting border should look exactly like it would've looked when given the width "0.4px" without scaling. Google Chrome does this well enough. Firefox doesn't.
Component: Untriaged → CSS Parsing and Computation
Product: Firefox → Core
Status: UNCONFIRMED → NEW
Has STR: --- → yes
Ever confirmed: true
Keywords: reproducible
See Also: → 1490361

Clarifying what sorts of outcomes are realistic here:

(In reply to Hildo Bijl from comment #0)

Expected results:

The borders should all ... remain visible

Let's focus on this^ request; I think this is the least-controversial expectation here.

To be
precise, when a border of "0.8px" is scaled by a factor "0.5", the resulting
border should look exactly like it would've looked when given the width
"0.4px" without scaling.

This is a reasonable intuition but it's a non-goal and is not actually how borders work in any browser.

In the absence of transforms, I think all browsers snap borders to whole-number display-pixel-values (at least, they do for borders less than 1px) -- so e.g. 0.4px will just round up to one display-pixel (and so will 0.01px), at computed-value-time (i.e. and it actually reserves & occupies that much layout space, rather than the specified tiny-fraction-of-a-pixel). This is interoperable and required for compatibility; you can try e.g. data:text/html,<div style="border: 0.01px solid black">Hi in various browsers and see that it occupies a full display-pixel (e.g. it's equivalent to border:1px solid black on a traditional non-HiDPI display resolution).

In contrast, transform:scale(0.5) is a paint-time effect and we don't get the opportunity to make the border specially-reserve-and-occupy a full display pixel of layout space when we apply a scaling transform. Chrome seems to use antialiasing to paint the scaled-down partial-pixel-width-border, whereas Firefox seems to still try to paint a whole number of pixels (possibly 0); we round down to 0px for sufficiently-small scales / sufficiently-thin borders. This outcome is not-great; ideally we should allow ourselves to paint fractional-width-borders as antialiased partial-pixels when we're scaling them, I think.

Also FWIW, I can see that the original testcase here renders without a top black border in old Firefox nightlies e.g. Firefox Nightly 48.0a1 (2016-03-19), but the whole black border paints in current Firefox Nightly (2022-06-21).

But if I reduce the scale to e.g. 0.2 or 0.5, I can still reproduce (the black border disappears).

I've got a clearer/more-comprehensive testcase on my dupe bug, which I'll repost here for clarity/convenience.

Attachment #8732527 - Attachment description: reporter's testcase → reporter's testcase (might not repro the bug anymore unless you use devtools to reduce the transform:scale(...) fraction)

screenshot of reduced testcase 1 in Firefox vs Chrome: https://bugzilla.mozilla.org/attachment.cgi?id=9282271

Summary: Firefox scales borders unequally, and sometimes the borders disappear altogether by scaling → Firefox's border pixel-snapping behavior causes thin borders to disappear entirely when scaled down (e.g. via CSS Transform or print-preview)
See Also: → 1784322
Severity: normal → S3

The severity field for this bug is relatively low, S3. However, the bug has 5 duplicates.
:emilio, could you consider increasing the bug severity?

For more information, please visit auto_nag documentation.

Flags: needinfo?(emilio)

Other browsers are going to match our behavior here and it's not clear there's a terribly-better behavior around. We could make WebRender force 1 dev px borders at paint-time too, but...

Flags: needinfo?(emilio)
See Also: → 1796718

(In reply to Emilio Cobos Álvarez (:emilio) from comment #12)

Other browsers are going to match our behavior here and it's not clear there's a terribly-better behavior around

Do we know if/when that's going to happen? I think some of it was happening in https://issues.chromium.org/issues/40178820 for a little while, but I'm still seeing Chrome and WebKit both render my attached reduced testcase 1 with fuzzy antialiased borders. I'm not sure if that's because they have special-cases for borders less than 1px wide, or if they never finished implementing this change, or something else.

Even if other browsers do end up matching us here, we might want to add some sort of mitigation for print-preview in particular (like the WebRender mitigation in comment 12), to avoid having borders mysteriously disappear in print-preview, per the last bit of bug 1775345 comment 0 (see screenshot https://bug1775345.bmoattachments.org/attachment.cgi?id=9282272 ).

(This came across my radar again today because I was print-previewing a page that had a <table> with 1px-thick borders, with some bordered empty cells on the final page. That final page just looked entirely blank in print-preview, which was confusing & looked like Firefox was mistakenly generating a final blank page.)

Looking at the Blink bug about border-snapping, it seems their feature-flag for this was called SnapBorderWidthsBeforeLayout which shipped to release in v109 per https://chromestatus.com/feature/4651561863610368 and seems to have stuck. I think that (and the associated WebKit changes) are the thing that Emilio was referring to in comment 12 as other-browsers-going-to-match-us (correct me if I'm wrong though).

I think the last paragraph of my comment 3 is still an accurate description of the compat situation here -- Firefox optimizes for keeping borders crisp, even in the face of transform with fractional scales, whereas other browsers do not (and hence scaled-down borders still show up [albeit in a fuzzy/antialiased form] for them, whereas they entirely disappear for us).

Duplicate of this bug: 1888270

I think we should move this to graphics given the comments above. If we were to fix this, it should be by antialiasing borders, not by changing layout / style computation.

Component: CSS Parsing and Computation → Graphics: WebRender
Blocks: wr-snap

I noticed today that this affects Responsive Design Mode as well, which I think uses CSS Transforms to apply a viewport scale-factor for mobile devices (for content that lacks a meta-viewport tag).

Loading this testcase in Firefox Nightly on my Ubuntu 24.10 machine in responsive-design-mode, some of the border-sides fail to render. (The sides that are missing are variable; it depends on the exact device/viewport-size that I've chosen in RDM.) For example, when I choose "Galaxy Note 20 Android 11", the bottom and right border-side are missing.

Consider decreasing the webcompat priority of this because it's a cosmetic glitch only that is unlikely to affect a large number of users regularly.

Severity: S3 → --

(In reply to Glenn Watson [:gw] from comment #20)

it's a cosmetic glitch only that is unlikely to affect a large number of users regularly.

That's an accurate statement, I think.

For the record, I think the current user impact here is as-follows:

  • We have one report of a site that's currently got some cosmetic breakage from this (bug 1926453).
  • Outside of that, I suspect that the places where users are most-likely to encounter this would be when Firefox itself is scaling down content that happens to have 1px-wide borders (e.g. a form control or an underline) -- for example, when viewing non-mobile-optimized content on Android (dupe bug 1784322) or when print-previewing (dupe bug 1775345).
User Story: (updated)

it's a cosmetic glitch only that is unlikely to affect a large number of users regularly.

While it may be accurate, it can still produce a bad user experience in some specific applications.

I'm leading an open source project, Sirius Web, used to build complex diagrams for major corporations. Our end users are constantly zooming in and out of diagrams with tons of divs with borders. With Firefox it can very quickly become unusable (especially on windows from what I've seen). Our diagram elements can lose their borders quite easily and, in complex diagrams, it becomes impossible to see where some of our element are.

I've made a small video showing the difference between Firefox and Chrome here on a simple diagram, if you want to see the impact on use cases such as ours. Some of our users just have to barely zoom out to see all borders disappear.

User Story: (updated)

The severity field is not set for this bug.
:gw, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(mozilla)
Severity: -- → S3
User Story: (updated)
Flags: needinfo?(mozilla)
User Story: (updated)

A border edge is composited onto its layout-space rect, so a transform makes a
1px border a fractional device thickness. Two symptoms follow:

  • Under a downscale (e.g. scale(0.5)) the edge shrinks below a device pixel and
    edge antialiasing fades it away entirely, until whole sides of the border
    disappear (bug 1258112).
  • At other scales (e.g. scale(0.5) on a devicePixelRatio 2 display, or at
    fractional zoom) the four sides land at different sub-pixel phases and render
    with visibly uneven thickness - right and bottom look thicker than left and
    top (bug 1950029).

The cached border texture is already floored to a device pixel; the collapse is
only in the on-screen composite geometry, which was never snapped. In
NormalBorderData::update, snap each edge thickness on the true world scale that
drives the composite: round a border the author declared at >= 1 CSS pixel to
the nearest whole device pixel, floored at 1 so a real border can't vanish. With
the outer box already device-snapped, this grid-aligns the inner edges too, so
every side renders as a consistent whole-pixel width. Genuinely sub-CSS-pixel
edges are left untouched. This mirrors how line decorations snap their thin axis
(bug 1783779), one layer deeper (a border is a single prim rather than a thin
rect).

Assignee: nobody → mozilla
Status: NEW → ASSIGNED
Status: ASSIGNED → RESOLVED
Closed: 6 days ago
Resolution: --- → FIXED
Target Milestone: --- → 154 Branch
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: