Commit graph

167 commits

Author SHA1 Message Date
Aliaksandr Kalenik
0d76a9da17 LibWeb: Resolve text-decoration-thickness during layout commit
Refactoring towards stop resolving CSS lengths during paintable
commands recording.
2024-01-26 07:36:40 +01:00
Aliaksandr Kalenik
d5e3158cfe LibWeb: Use PaintableFragment::baseline() in paint_text_decoration()
No need to calculate baseline based on glyph height when we can get
this information from a fragment.
2024-01-26 07:36:40 +01:00
Aliaksandr Kalenik
1c4ce2c5f1 LibWeb: Don't check containing block of fragments in hit testing
We do not rely on existence of containing block in
`PaintableWithLines::hit_test()` so it should be safe to remove this
check.
2024-01-26 07:36:40 +01:00
Aliaksandr Kalenik
270bbf43ab LibWeb: Remove is<BlockContainer> check for fragments in hit testing
This change introduces a method for direct access to the paintable of
a PaintableFragment. This method is intended to replace the usage of
the layout node pointer. Currently, we are only eliminating the use
of `layout_node()` in hit testing.

Additionally, we no longer check if a fragment's layout node is a
`BlockContainer` before recursing into its children during hit testing.
This check was likely relevant when all fragments were owned by
`PaintableWithLines`, but now it should be safe to remove this check.
2024-01-26 07:36:40 +01:00
Aliaksandr Kalenik
64a48065b0 LibWeb: Check if corners have radius after converting to device pixels
Check needs to happen after conversion because non-zero radius in
CSSPixels could turn into zero in device pixels.

Fixes https://github.com/SerenityOS/serenity/issues/22765
2024-01-15 15:21:17 +01:00
Aliaksandr Kalenik
7c2713c14f LibWeb: Move set_needs_display() from layout node to paintable
For this method, there is no need to go through the layout node when we
can directly reach the paintable.
2024-01-15 09:00:35 +01:00
Aliaksandr Kalenik
2960bf4ec8 LibWeb: Make inline paintables own their fragments
The paintable tree structure more closely matches the painting order
when fragments are owned by corresponding inline paintables. This
change does not affect the layout tree, as it is more convenient for
layout purposes to have all fragments owned by a block container in
one place.

Additionally, this improves performance significantly on pages with
many fragments, as we no longer have to walk the ancestor chain up
to the closest block container to determine if a fragment belongs
to an inline paintable.
2024-01-13 18:46:41 +01:00
Aliaksandr Kalenik
ee5d66c5d5 LibWeb: Resolve text shadows in LayoutState::commit()
Rather than resolving the text-shadow each time painting commands are
recorded, we can resolve it once during the layout commit and save the
resolved values in paintable fragments. This is also step towards
getting rid of layout node pointer in paintable fragment.
2024-01-13 12:03:32 +01:00
Aliaksandr Kalenik
de32b77ceb LibWeb: Use separate structure to represent fragments in paintable tree
This is a part of refactoring towards making the paintable tree
independent of the layout tree. Now, instead of transferring text
fragments from the layout tree to the paintable tree during the layout
commit phase, we allocate separate PaintableFragments that contain only
the information necessary for painting. Doing this also allows us to
get rid LineBoxes, as they are used only during layout.
2024-01-13 10:53:38 +01:00
Aliaksandr Kalenik
4bc38300ad LibWeb: Forbid using CSS::Length as reference value in resolved()
CSSPixels should not be wrapped into CSS::Length before being passed
to resolved() to end up resolving percentages without losing
precision.

Fixes thrashing layout when 33.3333% width is used together with
"box-sizing: border-box".
2024-01-07 09:03:57 +01:00
Aliaksandr Kalenik
3cf5ad002a LibWeb: Allow inline nodes to establish a stacking context
With this change, a stacking context can be established by any
paintable, including inline paintables. The stacking context traversal
is updated to remove the assumption that the stacking context root is
paintable box.
2024-01-05 19:36:55 +01:00
Aliaksandr Kalenik
6c645f3a9f LibWeb: Paint fragments contained by inline node as part of this node
Fragments contained by the inline node should be painted in the
foreground phase for this node, instead of being painted as a part of
the containing PaintableWithLines. This change implements that by
marking all fragments contained by inline nodes so they can be skipped
while painting the content of PaintableWithLines. This is an ugly way,
and instead, we should make InlinePaintables own all fragments
contained by them.
2024-01-05 19:36:55 +01:00
Aliaksandr Kalenik
ac6b3c989d LibWeb: Apply scroll boxes offsets after painting commands recording
With this change, instead of applying scroll offsets during the
recording of the painting command list, we do the following:
1. Collect all boxes with scrollable overflow into a PaintContext,
   each with an id and the total amount of scrolling offset accumulated
   from ancestor scrollable boxes.
2. During the recording phase assign a corresponding scroll_frame_id to
   each command that paints content within a scrollable box.
3. Before executing the recorded commands, translate each command that
   has a scroll_frame_id by the accumulated scroll offset.

This approach has following advantages:
- Implementing nested scrollables becomes much simpler, as the
  recording phase only requires the correct assignment of the nearest
  scrollable's scroll_frame_id, while the accumulated offset from
  ancestors is applied subsequently.
- The recording of painting commands is not tied to a specific offset
  within scrollable boxes, which means in the future, it will be
  possible to update the scrolling offset and repaint without the need
  to re-record painting commands.
2023-12-30 11:10:24 +01:00
Andreas Kling
bd9989b08a LibWeb: Don't pass StringView to RecordingPainter, to avoid copy
Instead, we now pass String if we have one. In particular, this fixes an
issue where image elements with a data: URL src would copy the entire
URL string every time we painted (before the image had been decoded).
This was very noticeable on "fully downloaded" web pages where every
single image has been turned into a data: URL.
2023-12-27 11:41:15 +01:00
Andreas Kling
f900957d26 LibGfx+LibWeb: Move Gfx::ScaledFont caching from LibWeb into LibGfx
Before this change, we would only cache and reuse Gfx::ScaledFont
instances for downloaded CSS fonts.

By moving it into Gfx::VectorFont, we get caching for all vector fonts,
including local system TTFs etc.

This avoids a *lot* of style invalidations in LibWeb, since we now vend
the same Gfx::Font pointer for the same font when used repeatedly.
2023-12-26 18:15:55 +01:00
Aliaksandr Kalenik
b2abd1dd05 LibWeb: Resolve box shadow data for paintable boxes during layout
Step towards making the paintable tree independent of the layout tree.
2023-12-19 21:08:51 +01:00
Ali Mohammad Pur
5e1499d104 Everywhere: Rename {Deprecated => Byte}String
This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).

This commit is auto-generated:
  $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
    Meta Ports Ladybird Tests Kernel)
  $ perl -pie 's/\bDeprecatedString\b/ByteString/g;
    s/deprecated_string/byte_string/g' $xs
  $ clang-format --style=file -i \
    $(git diff --name-only | grep \.cpp\|\.h)
  $ gn format $(git ls-files '*.gn' '*.gni')
2023-12-17 18:25:10 +03:30
Aliaksandr Kalenik
f361b8c000 LibWeb: Make private RecordingPainter::state()
Removes usage of `RecordingPainter::state()` and makes it private.

No behavior change intended.
2023-12-16 15:10:07 +01:00
Aliaksandr Kalenik
6b79508c08 LibWeb: Use offset of nearest scrollable ancestor for positioned boxes
Because positioned descendants are painted out-of-order we need to
separately apply offset of nearest scrollable box for them.

Fixes https://github.com/SerenityOS/serenity/issues/20554
2023-12-11 20:37:05 +01:00
Aliaksandr Kalenik
2952f01e84 LibWeb: Add pair of methods to apply/reset scroll offset of paintable
Separate scroll application from before_children_paint and
after_children_paint.
2023-12-11 20:37:05 +01:00
Andreas Kling
6994ea5885 LibWeb: Skip out-of-flow boxes when wrapping inlines in anonymous block
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.
2023-12-11 13:19:12 +01:00
Aliaksandr Kalenik
df57d7ca68 LibGfx+LibWeb: Update for_each_glyph_position to use font cascade list
This change updates function that builds list of glyphs to use font
cascade list to find font for each code point.
2023-12-10 17:32:04 +01:00
Aliaksandr Kalenik
2cb0039a13 LibGfx+LibWeb: Produce font cascade list in CSS font matching algorithm
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.
2023-12-10 17:32:04 +01:00
Aliaksandr Kalenik
0d03257e69 LibGfx+LibAccelGfx+LibWeb: Use RefPtr for font in DrawGlyphOrEmoji 2023-12-10 17:32:04 +01:00
Aliaksandr Kalenik
d1d6da6ab6 LibWeb: Resolve border radius during layout and save it in paintables
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.
2023-12-07 10:52:47 +01:00
Aliaksandr Kalenik
e8960cf754 LibWeb: Move BorderRadiusCornerClipper allocation into CPU executor
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.
2023-12-06 13:05:59 +01:00
Aliaksandr Kalenik
f93cab7679 LibWeb/Painting: Use scaled font for selected text
Fixes regression introduced in:
681771d210
2023-12-03 23:16:17 +01:00
Aliaksandr Kalenik
681771d210 LibGfx+LibWeb: Calculate and save glyph positions during layout
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.
2023-12-02 22:06:11 +01:00
Shannon Booth
56d10bf198 LibWeb: Port Layout::TextNode from DeprecatedString 2023-11-28 17:15:27 -05:00
Aliaksandr Kalenik
1b3223dd9e LibWeb: Rename painter() to recording_painter() in PaintContext
Using recording_painter() as a name is less misleading, indicating
the painter in stacking context traversal doesn't perform actual
painting commands.
2023-11-27 21:53:38 +01:00
Aliaksandr Kalenik
29ff1f67be LibWeb: Introduce dedicated painting command for borders
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.
2023-11-20 14:59:47 +01:00
Aliaksandr Kalenik
70353b79af LibWeb: Change paint_all_borders() to accept only device pixel values
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().
2023-11-20 14:59:47 +01:00
Aliaksandr Kalenik
fd3411c868 LibWeb: Ignore document's box during overflow clip rect calculation
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).
2023-11-09 07:56:34 +01:00
Aliaksandr Kalenik
4318bcf447 LibWeb: Record painting commands in coordinates of stacking context
By storing painting command coordinates relative to the nearest
stacking context we can get rid of the Translate command.

Additionally, this allows us to easily check if the bounding
rectangles of the commands cover or intersect within a stacking
context. This should be useful if we decide to optimize by avoiding
the execution of commands that will be overpainted by the results of
subsequent commands.
2023-10-25 05:53:36 +02:00
MacDue
1c012f0a4a LibWeb: Remove "cached corner bitmap" and its use in the corner clipper
With the recording painter the actual painting operations are delayed,
so now if multiple corner clippers are constructed, and they use a
shared bitmap they can interfere with each other. The use of this shared
bitmap was somewhat questionable anyway, so this is not much of a loss.

This fixes the border-radius.html test page.
2023-10-21 23:16:17 +02:00
Aliaksandr Kalenik
063e66cae9 LibWeb: Introduce RecordingPainter to serialize painting commands
This modification introduces a new layer to the painting process. The
stacking context traversal no longer immediately calls the
Gfx::Painter methods. Instead, it writes serialized painting commands
into newly introduced RecordingPainter. Created list of commands is
executed later to produce resulting bitmap.

Producing painting command list will make it easier to add new
optimizations:
- It's simpler to check if the painting result is not visible in the
  viewport at the command level rather than during stacking context
  traversal.
- Run painting in a separate thread. The painting thread can process
  serialized painting commands, while the main thread can work on the
  next paintable tree and safely invalidate the previous one.
- As we consider GPU-accelerated painting support, it would be easier
  to back each painting command rather than constructing an alternative
  for the entire Gfx::Painter API.
2023-10-18 10:58:42 +02:00
Aliaksandr Kalenik
7803dcfcf9 LibWeb: Resolve backdrop filter length in apply_style()
Instead of resolving lengths used in the backdrop-filter during
painting, we can do that earlier in apply_style().

This change moves us a bit closer to the point when the stacking
context tree will be completely separated from the layout tree :)
2023-10-12 07:38:48 +02:00
Aliaksandr Kalenik
46254101f7 LibWeb: Change DOM::Position to be GC-allocated 2023-09-26 21:25:54 +02:00
Andreas Kling
1f69e9cddf LibWeb: Remove Layout::Node::m_visible and compute it on the fly
This fixes an issue where the value would be out of sync with reality
in anonymous wrapper block boxes, since we forgot to compute m_visible
after assigning the computed values to them.

Fixes #21106
2023-09-18 14:45:20 +02:00
Zaggy1024
709767cc4b LibWeb: Don't convert to floating point in CSS::EdgeRect 2023-09-01 09:40:14 +02:00
Zaggy1024
9d4a1ac2b3 LibWeb: Apply CSS clip property to an element as well as its children
d06d4eb made the `clip` property apply to children of an absolute-
positioned element, but caused it not to be applied to the element the
property was applied to directly.

To fix this, apply the clip in new `before_paint()` and `after_paint()`
functions. Doing so keeps painter state from leaking from `paint()`,
but still allows subclasses of `PaintableBox` clip their contents
correctly without repeating the application of the clip rectangle.
2023-09-01 09:40:14 +02:00
Andreas Kling
6238860c6d LibWeb: Simplify PaintableBox::enclosing_stacking_context() 2023-08-28 23:36:22 +02:00
MacDue
71baa8c31a LibWeb: Add CSSPixels::nearest_value_for(FloatingPoint)
This is intended to annotate conversions from unknown floating-point
values to CSSPixels, and make it more obvious the fp value will be
rounded to the nearest fixed-point value.
2023-08-26 23:53:45 +02:00
MacDue
360c0eb509 LibWeb: Remove implicit conversion from float and double to CSSPixels
In general it is not safe to convert any arbitrary floating-point value
to CSSPixels. CSSPixels has a resolution of 0.015625, which for small
values (e.g. scale factors between 0 and 1), can produce bad results
if converted to CSSPixels then scaled back up. In the worst case values
can underflow to zero and produce incorrect results.
2023-08-26 23:53:45 +02:00
Sam Atkins
84a5c67d6b LibWeb: Use system colors in more places 2023-08-25 20:30:20 +01:00
Aliaksandr Kalenik
d06d4eb388 LibWeb: Apply "clip" property in apply_clip_overflow_rect()
Fixes bug when "clip" property does not affect abspos children.
This change makes "clip" property to be applied together with
"overflow: hidden" in `apply_clip_overflow_rect()` that already
handles abspos children correctly.
2023-08-23 15:49:06 +02:00
Andreas Kling
8bb275f2ea LibWeb: Move painting logic from Layout::Viewport to ViewportPaintable 2023-08-20 05:02:59 +02:00
Andreas Kling
79db9c27c6 LibWeb: Rename PaintableBox::effective_offset() => offset()
Since this function no longer does any computation, just "offset" seems
like a fine name.
2023-08-15 16:37:11 +02:00
Andreas Kling
25a3d0d643 LibWeb: Resolve relative offsets *once* after layout
Instead of applying relative offsets (like position:relative insets)
during painting and hit testing, we now do a pass at the end of layout
and assign the final resolved offsets to paintables.

This makes painting and hit testing easier since they don't have to
think about relative offsets, and it also fixes a bug where offsets were
not applied to text fragments inside inline-flow elements that were
themselves position:relative.
2023-08-15 16:37:11 +02:00
Aliaksandr Kalenik
5f364e9bc1 LibWeb: Rename is_scrollable() to is_user_scrollable()
is_user_scrollable() is more appropriate name since this function
returns true only for boxes scrollable by user interaction.
2023-08-08 16:55:14 +02:00