This fixes the issue that occurred when, after clicking an inline
paintable page would always scroll to the top. The problem was that
`scroll_an_element_into_view()` relies on `get_bounding_client_rect()`
to produce the correct scroll position and for inline paintables we
were always returning zero rect before this change.
Out-of-flow boxes (floating and absolutely-positioned elements) were
previously collected and put in the anonymous block wrapper as well, but
this actually made hit testing not able to find them, since they were
breaking expectations about tree structure that hit testing relies on.
After this change, we simply let out-of-flow boxes stay in their
original parent, preserving the author's intended box tree structure.
According to the CSS font matching algorithm specification, it is
supposed to be executed for each glyph instead of each text run, as is
currently done. This change partially implements this by having the
font matching algorithm produce a list of fonts against which each
glyph will be tested to find its suitable font.
Now, it becomes possible to have per-glyph fallback fonts: if the
needed glyph is not present in a font, we can check the subsequent
fonts in the list.
This change fixes a problem that we should not call `to_px()` to
resolve any length or percentage values during paintables traversal
because that is supposed to happen while performing layout.
Also it improves performance because before we were resolving border
radii during each painting phase but now it happens only once during
layout.
BorderRadiusCornerClipper usage to clip border radius is specific to
CPU painter so it should not be stored in painting commands.
Also with this change bitmaps for corner sampling are allocated during
painting commands replaying instead of commands recording.
Given that we have a glyph run where the position of each glyph is
calculated for text fragments during layout, we can reuse it to avoid
this work during painting.
Previously, we determined the positions of glyphs for each text run at
the time of painting, which constituted a significant portion of the
painting process according to profiles. However, since we already go
through each glyph to figure out the width of each fragment during
layout, we can simultaneously gather data about the position of each
glyph in the layout phase and utilize this information in the painting
phase.
I had to update expectations for a couple of reference tests. These
updates are due to the fact that we now measure glyph positions during
layout using a 1x font, and then linearly scale each glyph's position
to device pixels during painting. This approach should be acceptable,
considering we measure a fragment's width and height with an unscaled
font during layout.
If `SkipStackingContext` is returned we also need to restore saved
painter state because corresponding pop_stacking_context command that
is supposed to do that will be skipped.
Fixes https://github.com/SerenityOS/serenity/issues/22092
This change ensures that the GPU painting executor follows the pattern
of the CPU executor, where the state is stored for each stacking
context, but a painter is created only for those with opacity.
Fixes crashing on apple.com because now save() and restore() are called
on correct painters.
This change introduces a texture cache for immutable bitmaps in the
GPU painter. The cache is persisted across page repaints, so now, on
page scroll repaint, in many cases, we won't need to upload any new
textures. Also, if the same image is painted more than once on a page,
its texture will only be uploaded once.
Generally, the GPU painter works much faster with this change on all
pages that have images.
Before this change, we used Gfx::Bitmap to represent both decoded
images that are not going to be mutated and bitmaps corresponding
to canvases that could be mutated.
This change introduces a wrapper for bitmaps that are not going to be
mutated, so the painter could do caching: texture caching in the case
of GPU painter and potentially scaled bitmap caching in the case of CPU
painter.
For each stacking context with an opacity less than 1, we create a
separate framebuffer. We then blit the texture attached to this
framebuffer with the specified opacity.
To avoid the performance overhead of reading pixels from the texture
into Gfx::Bitmap, a new method that allows for direct blitting from
the texture is introduced, named blit_scaled_texture().
Currently, in CPU painter, border painting is implemented by building
a Gfx::Path that is filled by Gfx::AntiAliasingPainter. In the GPU
painter, we will likely want to do something different, and with a
special command, it becomes possible.
Also, by making this change, the CPU executor also benefits because now
we can skip building paths for borders that are out of the viewport.
By consistently accepting only device pixel values instead of a mix of
CSSPixels and DevicePixels values, we can simplify the implementation
of paint_border() and paint_all_borders().
The current set of stacking context commands do not encode the
information needed to correctly paint the stacking context, instead,
they're based on the limitations of the current CPU renderer.
Stacking contexts should be able to be transformed by an arbitrary
3D transformation matrix, not just scaled from a source to a destination
rect. The `_with_mask()` stacking context also should not be separate
from the regular stacking context.
```c++
push_stacking_context(
bool semitransparent_or_has_non_identity_transform,
float opacity, Gfx::FloatRect const& source_rect,
Gfx::FloatRect const& transformed_destination_rect,
Gfx::IntPoint const& painter_location);
pop_stacking_context(
bool semitransparent_or_has_non_identity_transform,
Gfx::Painter::ScalingMode scaling_mode);
push_stacking_context_with_mask(
Gfx::IntRect const& paint_rect);
pop_stacking_context_with_mask(
Gfx::IntRect const& paint_rect,
RefPtr<Gfx::Bitmap> const& mask_bitmap,
Gfx::Bitmap::MaskKind mask_kind, float opacity);
```
This patch replaces this APIs with just:
```c++
push_stacking_context(
float opacity,
bool is_fixed_position,
Gfx::IntRect const& source_paintable_rect,
Gfx::IntPoint post_transform_translation,
CSS::ImageRendering image_rendering,
StackingContextTransform transform,
Optional<StackingContextMask> mask);
pop_stacking_context()
```
And moves the implementation details into the executor, this should
allow future backends to implement stacking contexts without these
limitations.
Linear gradient painting is implemented in the following way:
1. The rectangle is divided into segments where each segment represents
a simple linear gradient between an adjacent pair of stops.
2. Each quad is filled separately using a fragment shader that
interpolates between two colors.
For now `angle` and `repeat_length` parameters are ignored.
Since we already call `Painter::flush()` in `PageHost::paint()` we do
not need to do that again in `PaintingCommandExecutorGPU` destructor.
This makes GPU painting run noticeably faster because `flush()` does
expensive `glReadPixels()` call.
Framebuffer object is allocated using OpenGL's API and is not platform
specific which means it could be used on both macOS and Linux unlike
EGL specific PBuffer.
Reduction of bug:
```html
<!DOCTYPE html><style>
html {
overflow-x: hidden;
overflow-y: scroll;
}
div {
background: orange;
height: 1000px;
width: 500px;
}
</style><body><div>
```
Fixes https://github.com/SerenityOS/serenity/issues/21690 and painting
on many other websites (null.com, servo.org).