Instead, it could be applied directly as a clip path in Skia painter.
As a side bonus, we get rid of some DeprecatedPath and
AntiAliasingPainter usage.
SVG and and CSS border rendering now sits on top of SkPath instead of
the old Gfx::DeprecatedPath.
Due to an imperceptible (255, 255, 255) vs (255, 254, 255) color diff
in one ref test, I changed that test to not depend on border rendering
for a positive result, since that was incidental.
A new display list item type named PaintScrollBar is introduced. Having
a dedicated type for scroll bars allows the thumb position to be updated
without rebuilding a display list. This was not possible with
FillRectWithRoundedCorners that does not allow to tell whether it
belongs to scroll thumb.
While introducing clip and scroll frame trees, I made a mistake by
assuming that the paintable tree includes boxes from nested navigables.
Therefore, this comment in the code was incorrect, and clip/scroll
frames were simply not assigned for iframes:
// NOTE: We only need to refresh the scroll state for traversables
// because they are responsible for tracking the state of all
// nested navigables.
As a result, anything with "overflow: scroll" is currently not
scrollable inside an iframe
This change fixes that by ensuring clip and scroll frames are assigned
and refreshed for each navigable. To achieve this, I had to modify the
display list building process to record a separate display list for each
navigable. This is necessary because scroll frame ids are local to a
navigable, making it impossible to call
`DisplayList::apply_scroll_offsets()` on a display list that contains
ids from multiple navigables.
When converting a `Gfx::Bitmap` to a Skia bitmap, we cannot assume the
color data is unpremultiplied. For example, everything canvas-related
uses premultiplied color data:
https://html.spec.whatwg.org/multipage/canvas.html#premultiplied-alpha-and-the-2d-rendering-context
We were probably assuming unpremultiplied since that is what the PNG
decoder gives us. Since we now make `Gfx::Bitmap` identify what alpha
type is being used, we can instruct Skia a bit better :^)
Update our `EdgeFlagPathRasterizer` to use premultiplied alpha instead
of unpremultiplied so we can apply alpha correctly for path masks.
This fixes the dark borders sometimes visible when SVGs are blended
with a colored background.
This also exposed an issue with our `CanvasRenderingContext2D`, which is
supposed to hold a bitmap with premultiplied alpha internally but expose
a bitmap with unpremultiplied alpha in `CanvasImageData`. Expand our C2D
test to include the alpha channel as well.
Finally, this also exposed an off-by-one issue in
`EdgeFlagPathRasterizer` which caused the last scanlines for edges to
render incorrectly. We had some reference images which included these
corruptions (they were almost unnoticeable), so update them as well.
Instead of carrying the display list for a mask in each command that
might potentially be affected by "background-clip: text", this change
introduces a new AddMask command that is applied once for all
background layers within one box.
The new AddMask command includes a rectangle for the mask destination
that is translated by the corresponding scroll offset.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/857
Contrary to LibGfx, where corner clipping was implemented by sampling
and blitting pixels under corners into a temporary bitmap, Skia allows
us to simply apply a mask. As a result, we no longer need the
BlitCornerClipping command, which has become a no-op.
- SampleUnderCorners is renamed to AddRoundedRectClip
- The optimization that skipped unnecessary blit and sample commands has
been removed. However, this should not result in a performance
regression because Skia seems to perform mask rasterization lazily.
The main incentive is much better performance. We could have gone a bit
further in optimizing the Skia painter to blit glyphs produced by LibGfx
more efficiently from the glyph atlas, but eventually, we also want Skia
to improve correctness.
This change does not completely replace LibGfx in text handling. It's
still used at all stages, including layout, up until display list
replaying.
Before this change, "background-clip: text" was implemented by saving a
Vector<Gfx::Path> of all glyphs needed to paint a mask for the
background. The issue with this approach was that once glyphs were
extracted into vector paths, the glyph rasterization cache could no
longer be utilized.
With this change, all text required for mask painting is saved in a
nested display list and rasterized as a regular text.
CommandResult was needed by LibGfx display list player that could have
failed to allocate a temporary bitmap for painting a stacking context
with CSS transforms. This is no longer an issue with Skia painter, so
we can delete CommandResult::SkipStackingContext handling path.
With this change, instead of recording a display list item for each
instance of a repeated background, a new DrawRepeatedImmutableBitmap
type is used. This allows the painter to use optimized repeated image
painting and, when the GPU backend is used, avoid re-uploading the image
texture for each repetition.
Some screenshot tests are affected, but there are no visible
regressions.
https://null.com/games/chainstaff works a lof faster with this change.
If no header includes the prototype of a function, then it cannot be
used from outside the translation unit it was defined in. In that case,
it should be marked as `static`, in order to avoid possible ODR
problems, unnecessary exported symbols, and allow the compiler to better
optimize those.
If this warning triggers in a function defined in a header, `inline`
needs to be added, otherwise if the header is included in more than one
TU, it will fail to link with a duplicate definition error.
The reason this diff got so big is that Lagom-only code wasn't built
with this flag even in Serenity times.
Each item in clip_paths represents a glyph run, and applying them as a
clip in intersection mode one by one results in an empty clip. Instead,
now all clip paths are joined and applied as a clip together.
This change fixes rendering of "background-clip: text" when an element
has more than one glyph run.
Fixed ref-test: Tests/LibWeb/Ref/css-background-clip-text.html
Couple fixes found by reading the spec:
- Repeating should also happen in negative direction, so the whole
[0, 1] is covered.
- Leftmost and rightmost stops should be clamped to [0, 1] range if
needed, because Skia ignores everything outside of this range.
Skia does not have built-in support for gradient transition hints. So
instead of adding custom gradient painting, now we do the same thing as
other engines and preprocess color stops by replacing transition hints
with a bunch of points lying between adjacent color stops and calculated
using non-linear formula from the spec. As a result we get visually
close enough rendering we would get by applying spec-formula
individually to each point of a gradient.
Skia now uses GPU-accelerated painting on Linux if Vulkan is available.
Most of the performance gain is currently negated by reading the GPU
backend back into RAM to pass it to the Browser process. In the future,
this could be improved by sharing GPU-allocated memory across the
Browser and WebContent processes.
From SkPaint.h:
"Sets the thickness of the pen used by the paint to outline the shape.
A stroke-width of zero is treated as "hairline" width. Hairlines are
always exactly one pixel wide in device space (their thickness does not
change as the canvas is scaled)."
While we expect stroke-width=0 to simply not be painted.
The ChunkIterator now limits a chunk to using only one font (before, it
was possible to have a chunk with >1 font, when `unicode-range` CSS
property is used).
This change allows us to reduce some complexity in the text shaping and
painting code and makes us compatible with the APIs in Skia and
HarfBuzz.
If Metal context and IOSurface are available, Skia painter will use
Ganesh GPU backend on macOS, which is noticeably faster than the default
CPU backend.
Painting pipeline:
1. (WebContent) Allocate IOSurface for backing store
2. (WebContent) Allocate MTLTexture that wraps IOSurface
3. (WebContent) Paint into MTLTexture using Skia
4. (Browser) Wrap IOSurface into Gfx::Painter and use
QPainter/CoreGraphics to blit backing store into viewport.
Things we should improve in the future:
1. Upload textures for images in advance instead of doing that before
every repaint.
2. Teach AppKit client to read directly from IOSurface instead of
copying.