CSS Transforms Level 1
introduced 2D transforms, that can be specified using the
For example, they can be used to rotate or scale an element:
transform: scale(1, 0.5)
CSS Transforms Level 2 extends that feature to allow transforms in 3D space, for example:
transform: rotate3d(1, 1, 1, 45deg)
transform: scale3d(1, 0.5, 2)
Typically, using 3D transforms forces the element into its own rendering layer. This is sometimes desired by authors, since it can improve performance if for example the element is moving around.
Therefore, identity transformations in the Z axis,
scale3d(X, Y, 1) instead of
are sometimes used to opt-in into this behavior.
This trick works on Chromium, but note it’s not compliant with the spec.
Forcing an element to be rasterized in its own layer can have some disadvantages.
For example, Chromium used to rasterize it using a single
When the transform had different X and Y scale components,
Chromium just picked the bigger one,
clamped by 5 times the smaller one (to avoid memory problems).
And then it used this raster scale for both axes,
producing suboptimal results.
Also, Chromium only uses LCD text antialiasing when the internal raster scale matches the actual X and Y scales in the transform. Therefore, non-uniform scales prevented the nicer LCD antialiasing.
And unrelated to uniform scales, if the transformed element doesn’t have an opaque background, LCD antialiasing is not used either, since Chromium needs to know the color behind the text.
The last problem remains unsolved, but I fixed the other two in Chromium 92, which has been released today.
Thanks to Bloomberg for sponsoring Igalia to do it!
Basically, it was a matter of changing
to store a 2D scale rather than a single
gfx::Vector2dF, which is like a pair of floats with some nice methods
to clamp by a minim or maximum, scale both floats by the same factor, etc.
I kept most tiling logic as it was,
just taking the maximum component of the
gfx::Vector2dF as the “scale key”.
However, different 2D scales can have the same key, for example by dynamically changing
scale3d(1, 5, 1) into
scale3d(5, 1, 1), both with a scale key of 5.
Therefore, when finding if there already was a tiling with the desired scale key,
I made sure to the check the 2D scales, and recreate the tiling if they were different.
This is an example of how it looked like in Chromium:
This is how it looked when internally using 2D scales:
And finally, with LCD text antialiasing:
For reference, this is how your browser renders it (live example):
Comparing the 1st and 2nd images, using 2D scales clearly improved the text, which was hard to read due to missing some thin parts of the glyphs, and also note the border diagonals in the corners look less jagged.
At first glance it may be hard to notice the difference between the 2nd and 3rd images, so you can compare the text antialiasing in these magnified images:
At the top, the edges of the glyphs simply use grayscale antialiasing, while at the bottom, the LCD antialiasing uses some colored pixels.
While my patch improved the common basic cases, Chromium will still fall back to a 1D scale in these cases:
- Directly composited images
- Heads up layers
- Scrollbar layers
- Mirror layers
- When the layer has perspective
Some of them may be addressed in the future, this is tracked in bug 1196414.
For example, this live example uses a CSS animation so it still looks wrong in Chromium 92:
I actually started a patch to address animations, and it seemed to work well in simple cases, but it could be wrong when ancestors had additional transforms. Handling that properly would have required more complexity, and it wasn’t clear that it was worth it.
Therefore, I don’t plan to continue working on these edge cases, but if you are affected by them, you can star bug 1196414 and provide a good testcase. This may help increase the priority of the bug!