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).
Representing a text run panting command as a vector of glyphs, rather
than as a string simplifies collecting of unique glyphs which is a
prerequisite for `prepare_glyphs_texture()` call.
This makes use of the new Gfx::Path::text() to handle SVG text elements,
with this text is just a regular path, and can be manipulated like any
other graphics element.
This removes the SVGTextPaintable and makes both <text> and geometry
elements use a new (shared) SVGPathPaintable. This is identical to the
old SVGGeometryPaintable. This simplifies painting as once something is
resolved to a Gfx::Path, the painting logic is the same.
In the upcoming changes, Painter will be used to store the state of
OpenGL context. For example, if Painter is aware of the shader that
have already been loaded, it will be possible to reuse them across
repaints. Also, it would be possible to manage state of loaded textures
and add/remove them depending on which ones are present in the next
sequence of painting commands.
Previously we didn't always set the bitmap_intersect correctly when
applying an object-position. This lead to images not correctly being
centered when the axis that it should move along was not the specified
axis.
Previously, all SVG <text> elements were zero-sized boxes, that were
only actually positioned and sized during painting. This led to a number
of problems, the most visible of which being that text could not be
scaled based on the viewBox.
Which this patch, <text> elements get a correctly sized layout box,
that can be hit-tested and respects the SVG viewBox.
To share code with SVGGeometryElement's the PathData (from the prior
commit) has been split into a computed path and computed transforms.
The computed path is specific to geometry elements, but the computed
transforms are shared between all SVG graphics elements.
This removes the awkward hack to recompute the layout transform at paint
time, and makes it possible for path sizes to be computed during layout.
For example, it's possible to use relative units in SVG shapes (e.g.
<rect>), which can be resolved during layout, but would be hard to
resolve again during painting.
The postitioning enum values are used by the position CSS property.
Unfortunately, the prior naming clashes with the CSS Values-4 type
named position, which will be implemented in a later commit.